slim_lint_standard 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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