slim_lint_standard 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.md +21 -0
  3. data/bin/slim-lint-standard +7 -0
  4. data/config/default.yml +109 -0
  5. data/lib/slim_lint/atom.rb +129 -0
  6. data/lib/slim_lint/capture_map.rb +19 -0
  7. data/lib/slim_lint/cli.rb +167 -0
  8. data/lib/slim_lint/configuration.rb +111 -0
  9. data/lib/slim_lint/configuration_loader.rb +86 -0
  10. data/lib/slim_lint/constants.rb +10 -0
  11. data/lib/slim_lint/document.rb +78 -0
  12. data/lib/slim_lint/engine.rb +41 -0
  13. data/lib/slim_lint/exceptions.rb +20 -0
  14. data/lib/slim_lint/file_finder.rb +88 -0
  15. data/lib/slim_lint/filter.rb +126 -0
  16. data/lib/slim_lint/filters/attribute_processor.rb +46 -0
  17. data/lib/slim_lint/filters/auto_indenter.rb +39 -0
  18. data/lib/slim_lint/filters/control_processor.rb +46 -0
  19. data/lib/slim_lint/filters/do_inserter.rb +39 -0
  20. data/lib/slim_lint/filters/end_inserter.rb +74 -0
  21. data/lib/slim_lint/filters/interpolation.rb +73 -0
  22. data/lib/slim_lint/filters/multi_flattener.rb +32 -0
  23. data/lib/slim_lint/filters/splat_processor.rb +20 -0
  24. data/lib/slim_lint/filters/static_merger.rb +47 -0
  25. data/lib/slim_lint/lint.rb +70 -0
  26. data/lib/slim_lint/linter/avoid_multiline_expressions.rb +41 -0
  27. data/lib/slim_lint/linter/comment_control_statement.rb +26 -0
  28. data/lib/slim_lint/linter/consecutive_control_statements.rb +26 -0
  29. data/lib/slim_lint/linter/control_statement_spacing.rb +32 -0
  30. data/lib/slim_lint/linter/dynamic_output_spacing.rb +77 -0
  31. data/lib/slim_lint/linter/embedded_engines.rb +18 -0
  32. data/lib/slim_lint/linter/empty_control_statement.rb +15 -0
  33. data/lib/slim_lint/linter/empty_lines.rb +24 -0
  34. data/lib/slim_lint/linter/file_length.rb +18 -0
  35. data/lib/slim_lint/linter/line_length.rb +18 -0
  36. data/lib/slim_lint/linter/redundant_div.rb +21 -0
  37. data/lib/slim_lint/linter/rubocop.rb +131 -0
  38. data/lib/slim_lint/linter/standard.rb +69 -0
  39. data/lib/slim_lint/linter/tab.rb +20 -0
  40. data/lib/slim_lint/linter/tag_case.rb +15 -0
  41. data/lib/slim_lint/linter/trailing_blank_lines.rb +19 -0
  42. data/lib/slim_lint/linter/trailing_whitespace.rb +17 -0
  43. data/lib/slim_lint/linter.rb +93 -0
  44. data/lib/slim_lint/linter_registry.rb +37 -0
  45. data/lib/slim_lint/linter_selector.rb +87 -0
  46. data/lib/slim_lint/logger.rb +103 -0
  47. data/lib/slim_lint/matcher/anything.rb +11 -0
  48. data/lib/slim_lint/matcher/base.rb +21 -0
  49. data/lib/slim_lint/matcher/capture.rb +32 -0
  50. data/lib/slim_lint/matcher/nothing.rb +13 -0
  51. data/lib/slim_lint/options.rb +110 -0
  52. data/lib/slim_lint/parser.rb +584 -0
  53. data/lib/slim_lint/rake_task.rb +125 -0
  54. data/lib/slim_lint/report.rb +25 -0
  55. data/lib/slim_lint/reporter/checkstyle_reporter.rb +42 -0
  56. data/lib/slim_lint/reporter/default_reporter.rb +40 -0
  57. data/lib/slim_lint/reporter/emacs_reporter.rb +40 -0
  58. data/lib/slim_lint/reporter/json_reporter.rb +50 -0
  59. data/lib/slim_lint/reporter.rb +44 -0
  60. data/lib/slim_lint/ruby_extract_engine.rb +30 -0
  61. data/lib/slim_lint/ruby_extractor.rb +175 -0
  62. data/lib/slim_lint/ruby_parser.rb +32 -0
  63. data/lib/slim_lint/runner.rb +82 -0
  64. data/lib/slim_lint/sexp.rb +134 -0
  65. data/lib/slim_lint/sexp_visitor.rb +150 -0
  66. data/lib/slim_lint/source_location.rb +45 -0
  67. data/lib/slim_lint/utils.rb +84 -0
  68. data/lib/slim_lint/version.rb +6 -0
  69. data/lib/slim_lint.rb +55 -0
  70. metadata +218 -0
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "slim_lint/ruby_extractor"
4
+ require "slim_lint/ruby_extract_engine"
5
+
6
+ module SlimLint
7
+ class Linter
8
+ # Runs RuboCop on Ruby code extracted from Slim templates.
9
+ class Standard < RuboCop
10
+ include LinterRegistry
11
+
12
+ def initialize(*args)
13
+ require "standard"
14
+ super
15
+ end
16
+
17
+ private
18
+
19
+ # Executes RuboCop against the given Ruby code and records the offenses as
20
+ # lints.
21
+ #
22
+ # @param ruby [String] Ruby code
23
+ # @param source_map [Hash] map of Ruby code line numbers to original line
24
+ # numbers in the template
25
+ def find_lints(ruby, source_map)
26
+ filename = document.file ? "#{document.file}.rb" : "ruby_script.rb"
27
+
28
+ with_ruby_from_stdin(ruby) do
29
+ extract_lints_from_offenses(lint_file(filename), source_map)
30
+ end
31
+ end
32
+
33
+ # Defined so we can stub the results in tests
34
+ #
35
+ # @param file [String]
36
+ # @return [Array<RuboCop::Cop::Offense>]
37
+ def lint_file(filename)
38
+ ::Standard::Cli.new(rubocop_flags << filename).run
39
+ OffenseCollector.offenses
40
+ end
41
+
42
+ # Aggregates RuboCop offenses and converts them to {SlimLint::Lint}s
43
+ # suitable for reporting.
44
+ #
45
+ # @param offenses [Array<RuboCop::Cop::Offense>]
46
+ # @param source_map [Hash]
47
+ def extract_lints_from_offenses(offenses, source_map)
48
+ offenses.each do |offense|
49
+ @lints << Lint.new(
50
+ [self, offense.cop_name],
51
+ document.file,
52
+ location_for_line(source_map, offense),
53
+ offense.message.gsub(/ at \d+, \d+/, "")
54
+ )
55
+ end
56
+ end
57
+
58
+ # Returns flags that will be passed to RuboCop CLI.
59
+ #
60
+ # @return [Array<String>]
61
+ def rubocop_flags
62
+ flags = %w[--format SlimLint::Linter::RuboCop::OffenseCollector]
63
+ flags += ["--no-display-cop-names"]
64
+ flags += ["--stdin"]
65
+ flags
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlimLint
4
+ # Searches for tab indentation
5
+ class Linter::Tab < Linter
6
+ include LinterRegistry
7
+
8
+ TAB_RE = /^( *)[\t ]*\t/
9
+ MSG = "Tab detected"
10
+
11
+ on_start do |_sexp|
12
+ document.source_lines.each.with_index(1) do |line, lineno|
13
+ next unless TAB_RE.match?(line)
14
+
15
+ sexp = Sexp.new(:dummy, start: [lineno, 0], finish: [lineno, ($` ? $`.size : 0)])
16
+ report_lint(sexp, MSG)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlimLint
4
+ # Searches for tags with uppercase characters.
5
+ class Linter::TagCase < Linter
6
+ include LinterRegistry
7
+
8
+ on [:html, :tag] do |sexp|
9
+ _, _, name = sexp
10
+ next unless name[/[A-Z]/]
11
+
12
+ report_lint(sexp, "Tag `#{name}` should be written as `#{name.downcase}`")
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlimLint
4
+ # This linter looks for trailing blank lines and a final newline.
5
+ class Linter::TrailingBlankLines < Linter
6
+ include LinterRegistry
7
+
8
+ on_start do |_sexp|
9
+ next if document.source.empty?
10
+
11
+ sexp = Sexp.new(:dummy, start: [document.source.lines.size, 0], finish: [document.source.lines.size, 0])
12
+ if !document.source.end_with?("\n")
13
+ report_lint(sexp, "No blank line in the end of file")
14
+ elsif document.source.lines.last.blank?
15
+ report_lint(sexp, "Multiple empty lines in the end of file")
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlimLint
4
+ # Checks for trailing whitespace.
5
+ class Linter::TrailingWhitespace < Linter
6
+ include LinterRegistry
7
+
8
+ on_start do |_sexp|
9
+ document.source_lines.each.with_index(1) do |line, lineno|
10
+ next unless /\s+$/.match?(line)
11
+
12
+ sexp = Sexp.new(:dummy, start: [lineno, line.rstrip.size], finish: [lineno, line.size])
13
+ report_lint(sexp, "Line contains trailing whitespace")
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlimLint
4
+ # Base implementation for all lint checks.
5
+ #
6
+ # @abstract
7
+ class Linter
8
+ # Include definitions for Sexp pattern-matching helpers.
9
+ include SexpVisitor
10
+ extend SexpVisitor::DSL
11
+
12
+ # List of lints reported by this linter.
13
+ #
14
+ # @todo Remove once spec/support/shared_linter_context returns an array of
15
+ # lints for the subject instead of the linter itself.
16
+ attr_reader :lints
17
+
18
+ # Initializes a linter with the specified configuration.
19
+ #
20
+ # @param config [Hash] configuration for this linter
21
+ def initialize(config)
22
+ @config = config
23
+ @lints = []
24
+ end
25
+
26
+ # Runs the linter against the given Slim document.
27
+ #
28
+ # @param document [SlimLint::Document]
29
+ def run(document)
30
+ @document = document
31
+ @lints = []
32
+ @disabled_lines = nil
33
+ trigger_pattern_callbacks(document.sexp)
34
+ @lints
35
+ end
36
+
37
+ # Returns the simple name for this linter.
38
+ #
39
+ # @return [String]
40
+ def name
41
+ self.class.name.split("::").last
42
+ end
43
+
44
+ private
45
+
46
+ attr_reader :config, :document
47
+
48
+ # Record a lint for reporting back to the user.
49
+ #
50
+ # @param node [Sexp, Atom] node to extract the line number from
51
+ # @param message [String] error/warning to display to the user
52
+ def report_lint(node, message)
53
+ return if disabled_for_line?(node.line)
54
+
55
+ @lints << SlimLint::Lint.new(self, @document.file, node.location, message)
56
+ end
57
+
58
+ # Parse Ruby code into an abstract syntax tree.
59
+ #
60
+ # @param source [String] Ruby code to parse
61
+ # @return [AST::Node]
62
+ def parse_ruby(source)
63
+ @ruby_parser ||= SlimLint::RubyParser.new
64
+ @ruby_parser.parse(source)
65
+ end
66
+
67
+ def disabled_for_line?(line)
68
+ disabled_lines.include?(line)
69
+ end
70
+
71
+ def disabled_lines
72
+ @disabled_lines ||= begin
73
+ currently_disabled = false
74
+ @document.source_lines.each_with_index.each_with_object([]) do |pair, lines|
75
+ line = pair[0]
76
+ line_number = pair[1] + 1
77
+
78
+ if line.match?(%r{/ slim-lint:disable #{linter_name}})
79
+ currently_disabled = true
80
+ elsif line.match?(%r{/ slim-lint:enable #{linter_name}})
81
+ currently_disabled = false
82
+ elsif currently_disabled
83
+ lines << line_number
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ def linter_name
90
+ @linter_name ||= self.class.name.split("::").last
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlimLint
4
+ class NoSuchLinter < StandardError; end
5
+
6
+ # Stores all defined linters.
7
+ module LinterRegistry
8
+ @linters = []
9
+
10
+ class << self
11
+ # List of all registered linters.
12
+ attr_reader :linters
13
+
14
+ # Executed when a linter includes the {LinterRegistry} module.
15
+ #
16
+ # This results in the linter being registered with the registry.
17
+ #
18
+ # @param subclass [Class]
19
+ def included(subclass)
20
+ @linters << subclass
21
+ end
22
+
23
+ # Return a list of {SlimLint::Linter} {Class}es corresponding to the
24
+ # specified list of names.
25
+ #
26
+ # @param linter_names [Array<String>]
27
+ # @return [Array<Class>]
28
+ def extract_linters_from(linter_names)
29
+ linter_names.map do |linter_name|
30
+ SlimLint::Linter.const_get(linter_name)
31
+ rescue NameError
32
+ raise NoSuchLinter, "Linter #{linter_name} does not exist"
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlimLint
4
+ # Chooses the appropriate linters to run given the specified configuration.
5
+ class LinterSelector
6
+ # Creates a selector using the given configuration and additional options.
7
+ #
8
+ # @param config [SlimLint::Configuration]
9
+ # @param options [Hash]
10
+ def initialize(config, options)
11
+ @config = config
12
+ @options = options
13
+ end
14
+
15
+ # Returns the set of linters to run against the given file.
16
+ #
17
+ # @param file [String]
18
+ # @raise [SlimLint::Exceptions::NoLintersError] when no linters are enabled
19
+ # @return [Array<SlimLint::Linter>]
20
+ def linters_for_file(file)
21
+ @linters ||= extract_enabled_linters(@config, @options)
22
+ @linters.select { |linter| run_linter_on_file?(@config, linter, file) }
23
+ end
24
+
25
+ private
26
+
27
+ # Returns a list of linter names that are enabled given the specified
28
+ # configuration and additional options.
29
+ #
30
+ # @param config [SlimLint::Configuration]
31
+ # @param options [Hash]
32
+ # @return [Array<String>]
33
+ def extract_enabled_linter_names(config, options)
34
+ included_linters = options.fetch(:included_linters, [])
35
+ included_linters = LinterRegistry.linters.map(&:name) if included_linters.empty?
36
+
37
+ excluded_linters = options.fetch(:excluded_linters, [])
38
+
39
+ # After filtering out explicitly included/excluded linters, only include
40
+ # linters which are enabled in the configuration
41
+ linters = (included_linters - excluded_linters).select do |name|
42
+ config.for_linter(name)["enabled"]
43
+ end
44
+
45
+ # Highlight condition where all linters were filtered out, as this was
46
+ # likely a mistake on the user's part
47
+ if linters.empty?
48
+ raise SlimLint::Exceptions::NoLintersError, "No linters specified"
49
+ end
50
+
51
+ linters
52
+ end
53
+
54
+ # Returns a list of linters that are enabled given the specified
55
+ # configuration and additional options.
56
+ #
57
+ # @param config [SlimLint::Configuration]
58
+ # @param options [Hash]
59
+ # @return [Array<SlimLint::Linter>]
60
+ def extract_enabled_linters(config, options)
61
+ linter_names = extract_enabled_linter_names(config, options)
62
+ linter_classes = LinterRegistry.extract_linters_from(linter_names)
63
+ linter_classes.map { |klass| klass.new(config.for_linter(klass)) }
64
+ end
65
+
66
+ # Whether to run the given linter against the specified file.
67
+ #
68
+ # @param config [SlimLint::Configuration]
69
+ # @param linter [SlimLint::Linter]
70
+ # @param file [String]
71
+ # @return [Boolean]
72
+ def run_linter_on_file?(config, linter, file)
73
+ linter_config = config.for_linter(linter)
74
+ incl, excl = linter_config["include"], linter_config["exclude"]
75
+
76
+ if incl.any? && !SlimLint::Utils.any_glob_matches?(incl, file)
77
+ return false
78
+ end
79
+
80
+ if SlimLint::Utils.any_glob_matches?(excl, file)
81
+ return false
82
+ end
83
+
84
+ true
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlimLint
4
+ # Encapsulates all communication to an output source.
5
+ class Logger
6
+ # Whether colored output via ANSI escape sequences is enabled.
7
+ # @return [true,false]
8
+ attr_accessor :color_enabled
9
+
10
+ # Creates a logger which outputs nothing.
11
+ # @return [SlimLint::Logger]
12
+ def self.silent
13
+ new(File.open("/dev/null", "w"))
14
+ end
15
+
16
+ # Creates a new {SlimLint::Logger} instance.
17
+ #
18
+ # @param out [IO] the output destination.
19
+ def initialize(out)
20
+ @out = out
21
+ end
22
+
23
+ # Print the specified output.
24
+ #
25
+ # @param output [String] the output to send
26
+ # @param newline [true,false] whether to append a newline
27
+ def log(output, newline = true)
28
+ @out.print(output)
29
+ @out.print("\n") if newline
30
+ end
31
+
32
+ # Print the specified output in bold face.
33
+ # If output destination is not a TTY, behaves the same as {#log}.
34
+ #
35
+ # @param args [Array<String>]
36
+ def bold(*args)
37
+ color("1", *args)
38
+ end
39
+
40
+ # Print the specified output in a color indicative of error.
41
+ # If output destination is not a TTY, behaves the same as {#log}.
42
+ #
43
+ # @param args [Array<String>]
44
+ def error(*args)
45
+ color(31, *args)
46
+ end
47
+
48
+ # Print the specified output in a bold face and color indicative of error.
49
+ # If output destination is not a TTY, behaves the same as {#log}.
50
+ #
51
+ # @param args [Array<String>]
52
+ def bold_error(*args)
53
+ color("1;31", *args)
54
+ end
55
+
56
+ # Print the specified output in a color indicative of success.
57
+ # If output destination is not a TTY, behaves the same as {#log}.
58
+ #
59
+ # @param args [Array<String>]
60
+ def success(*args)
61
+ color(32, *args)
62
+ end
63
+
64
+ # Print the specified output in a color indicative of a warning.
65
+ # If output destination is not a TTY, behaves the same as {#log}.
66
+ #
67
+ # @param args [Array<String>]
68
+ def warning(*args)
69
+ color(33, *args)
70
+ end
71
+
72
+ # Print the specified output in a color indicating information.
73
+ # If output destination is not a TTY, behaves the same as {#log}.
74
+ #
75
+ # @param args [Array<String>]
76
+ def info(*args)
77
+ color(36, *args)
78
+ end
79
+
80
+ # Print a blank line.
81
+ def newline
82
+ log("")
83
+ end
84
+
85
+ # Whether this logger is outputting to a TTY.
86
+ #
87
+ # @return [true,false]
88
+ def tty?
89
+ @out.respond_to?(:tty?) && @out.tty?
90
+ end
91
+
92
+ private
93
+
94
+ # Print output in the specified color.
95
+ #
96
+ # @param code [Integer,String] ANSI color code
97
+ # @param output [String] output to print
98
+ # @param newline [Boolean] whether to append a newline
99
+ def color(code, output, newline = true)
100
+ log(color_enabled ? "\033[#{code}m#{output}\033[0m" : output, newline)
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlimLint::Matcher
4
+ # Will match anything, acting as a wildcard.
5
+ class Anything < Base
6
+ # @see {SlimLint::Matcher::Base#match?}
7
+ def match?(*)
8
+ true
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlimLint::Matcher
4
+ # Represents a Sexp pattern implementing complex matching logic.
5
+ #
6
+ # Subclasses can implement custom logic to create complex matches that can be
7
+ # reused across linters, DRYing up matching code.
8
+ #
9
+ # @abstract
10
+ class Base
11
+ # Whether this matcher matches the specified object.
12
+ #
13
+ # This must be implemented by subclasses.
14
+ #
15
+ # @param other [Object]
16
+ # @return [Boolean]
17
+ def match?(*)
18
+ raise NotImplementedError, "Matcher must implement `match?`"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlimLint::Matcher
4
+ # Wraps a matcher, taking on the behavior of the wrapped matcher but storing
5
+ # the value that matched so it can be referred to later.
6
+ class Capture < Base
7
+ # @return [SlimLint::Matcher::Base] matcher that this capture wraps
8
+ attr_accessor :matcher
9
+
10
+ # @return [Object] value that was captured
11
+ attr_accessor :value
12
+
13
+ # Creates a capture that wraps that given matcher.
14
+ #
15
+ # @param matcher [SlimLint::Matcher::Base]
16
+ # @return [SlimLint::Matcher::Capture]
17
+ def self.from_matcher(matcher)
18
+ new.tap do |cap_matcher|
19
+ cap_matcher.matcher = matcher
20
+ end
21
+ end
22
+
23
+ # @see {SlimLint::Matcher::Base#match?}
24
+ def match?(object)
25
+ if (result = @matcher.match?(object))
26
+ @value = object
27
+ end
28
+
29
+ result
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlimLint::Matcher
4
+ # Does not match anything.
5
+ #
6
+ # This is used in specs.
7
+ class Nothing < Base
8
+ # @see {SlimLint::Matcher::Base#match?}
9
+ def match?(*)
10
+ false
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+
5
+ module SlimLint
6
+ # Handles option parsing for the command line application.
7
+ class Options
8
+ # Parses command line options into an options hash.
9
+ #
10
+ # @param args [Array<String>] arguments passed via the command line
11
+ # @return [Hash] parsed options
12
+ def parse(args)
13
+ @options = {}
14
+
15
+ OptionParser.new do |parser|
16
+ parser.banner = "Usage: #{APP_NAME} [options] [file1, file2, ...]"
17
+
18
+ add_linter_options parser
19
+ add_file_options parser
20
+ add_info_options parser
21
+ end.parse!(args)
22
+
23
+ # Any remaining arguments are assumed to be files
24
+ @options[:files] = args
25
+
26
+ @options
27
+ rescue OptionParser::InvalidOption => e
28
+ raise Exceptions::InvalidCLIOption,
29
+ e.message,
30
+ e.backtrace
31
+ end
32
+
33
+ private
34
+
35
+ # Register linter-related flags.
36
+ def add_linter_options(parser)
37
+ msg = "Specify which linters you want to run"
38
+ parser.on("-i", "--include-linter linter,...", Array, msg) do |linters|
39
+ @options[:included_linters] = linters
40
+ end
41
+
42
+ msg = "Specify which linters you don't want to run"
43
+ parser.on("-x", "--exclude-linter linter,...", Array, msg) do |linters|
44
+ @options[:excluded_linters] = linters
45
+ end
46
+
47
+ msg = "Specify which reporter you want to use to generate the output"
48
+ parser.on("-r", "--reporter reporter", String, msg) do |reporter|
49
+ @options[:reporter] = load_reporter_class(reporter.capitalize)
50
+ end
51
+ end
52
+
53
+ # Returns the class of the specified Reporter.
54
+ #
55
+ # @param reporter_name [String]
56
+ # @raise [SlimLint::Exceptions::InvalidCLIOption] if reporter doesn't exist
57
+ # @return [Class]
58
+ def load_reporter_class(reporter_name)
59
+ SlimLint::Reporter.const_get("#{reporter_name}Reporter")
60
+ rescue NameError
61
+ raise SlimLint::Exceptions::InvalidCLIOption,
62
+ "#{reporter_name}Reporter does not exist"
63
+ end
64
+
65
+ # Register file-related flags.
66
+ def add_file_options(parser)
67
+ msg = "Specify which configuration file you want to use"
68
+ parser.on("-c", "--config config-file", String, msg) do |conf_file|
69
+ @options[:config_file] = conf_file
70
+ end
71
+
72
+ msg = "List of file names to exclude"
73
+ parser.on("-e", "--exclude file,...", Array, msg) do |files|
74
+ @options[:excluded_files] = files
75
+ end
76
+
77
+ msg = "Pipe source from STDIN, using file in offense reports."
78
+ parser.on("--stdin-file-path file", String, msg) do |file|
79
+ @options[:stdin_file_path] = file
80
+ end
81
+ end
82
+
83
+ # Register informational flags.
84
+ def add_info_options(parser)
85
+ parser.on("--show-linters", "Display available linters") do
86
+ @options[:show_linters] = true
87
+ end
88
+
89
+ parser.on("--show-reporters", "Display available reporters") do
90
+ @options[:show_reporters] = true
91
+ end
92
+
93
+ parser.on("--[no-]color", "Force output to be colorized") do |color|
94
+ @options[:color] = color
95
+ end
96
+
97
+ parser.on_tail("-h", "--help", "Display help documentation") do
98
+ @options[:help] = parser.help
99
+ end
100
+
101
+ parser.on_tail("-v", "--version", "Display version") do
102
+ @options[:version] = true
103
+ end
104
+
105
+ parser.on_tail("-V", "--verbose-version", "Display verbose version information") do
106
+ @options[:verbose_version] = true
107
+ end
108
+ end
109
+ end
110
+ end