lint_trappings 0.1.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.
- checksums.yaml +7 -0
- data/LICENSE.md +21 -0
- data/lib/lint_trappings.rb +17 -0
- data/lib/lint_trappings/application.rb +138 -0
- data/lib/lint_trappings/arguments_parser.rb +145 -0
- data/lib/lint_trappings/cli.rb +61 -0
- data/lib/lint_trappings/command/base.rb +36 -0
- data/lib/lint_trappings/command/display_documentation.rb +65 -0
- data/lib/lint_trappings/command/display_formatters.rb +14 -0
- data/lib/lint_trappings/command/display_help.rb +8 -0
- data/lib/lint_trappings/command/display_linters.rb +24 -0
- data/lib/lint_trappings/command/display_version.rb +14 -0
- data/lib/lint_trappings/command/scan.rb +19 -0
- data/lib/lint_trappings/configuration.rb +94 -0
- data/lib/lint_trappings/configuration_loader.rb +98 -0
- data/lib/lint_trappings/configuration_resolver.rb +49 -0
- data/lib/lint_trappings/document.rb +45 -0
- data/lib/lint_trappings/errors.rb +127 -0
- data/lib/lint_trappings/executable.rb +26 -0
- data/lib/lint_trappings/file_finder.rb +171 -0
- data/lib/lint_trappings/formatter/base.rb +67 -0
- data/lib/lint_trappings/formatter/checkstyle.rb +34 -0
- data/lib/lint_trappings/formatter/default.rb +99 -0
- data/lib/lint_trappings/formatter/json.rb +62 -0
- data/lib/lint_trappings/formatter_forwarder.rb +23 -0
- data/lib/lint_trappings/formatter_loader.rb +45 -0
- data/lib/lint_trappings/lint.rb +37 -0
- data/lib/lint_trappings/linter.rb +182 -0
- data/lib/lint_trappings/linter_configuration_validator.rb +42 -0
- data/lib/lint_trappings/linter_loader.rb +44 -0
- data/lib/lint_trappings/linter_plugin.rb +35 -0
- data/lib/lint_trappings/linter_selector.rb +120 -0
- data/lib/lint_trappings/location.rb +39 -0
- data/lib/lint_trappings/output.rb +118 -0
- data/lib/lint_trappings/preprocessor.rb +41 -0
- data/lib/lint_trappings/rake_task.rb +145 -0
- data/lib/lint_trappings/report.rb +58 -0
- data/lib/lint_trappings/runner.rb +161 -0
- data/lib/lint_trappings/spec.rb +12 -0
- data/lib/lint_trappings/spec/directory_helpers.rb +22 -0
- data/lib/lint_trappings/spec/indentation_helpers.rb +7 -0
- data/lib/lint_trappings/spec/matchers/report_lint_matcher.rb +169 -0
- data/lib/lint_trappings/spec/shared_contexts/linter_shared_context.rb +35 -0
- data/lib/lint_trappings/utils.rb +123 -0
- data/lib/lint_trappings/version.rb +4 -0
- metadata +117 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
module LintTrappings
|
2
|
+
# Store location of a {Lint} in a document.
|
3
|
+
class Location
|
4
|
+
include Comparable
|
5
|
+
|
6
|
+
attr_reader :line, :column
|
7
|
+
|
8
|
+
# @param line [Integer] One-based index
|
9
|
+
# @param column [Integer] One-based index
|
10
|
+
def initialize(line = 1, column = 1)
|
11
|
+
raise ArgumentError, "Line must be >= 0, but was #{line}" if line < 0
|
12
|
+
raise ArgumentError, "Column must be >= 0, but was #{column}" if column < 0
|
13
|
+
|
14
|
+
@line = line
|
15
|
+
@column = column
|
16
|
+
end
|
17
|
+
|
18
|
+
def ==(other)
|
19
|
+
[:line, :column].all? do |attr|
|
20
|
+
send(attr) == other.send(attr)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
alias eql? ==
|
25
|
+
|
26
|
+
def <=>(other)
|
27
|
+
[:line, :column].each do |attr|
|
28
|
+
result = send(attr) <=> other.send(attr)
|
29
|
+
return result unless result == 0
|
30
|
+
end
|
31
|
+
|
32
|
+
0
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
"(#{line},#{column})"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module LintTrappings
|
2
|
+
# Encapsulates all communication to an output source.
|
3
|
+
class Output
|
4
|
+
# Whether colored output via ANSI escape sequences is enabled.
|
5
|
+
# @return [true,false]
|
6
|
+
attr_accessor :color_enabled
|
7
|
+
|
8
|
+
# Creates a logger which outputs nothing.
|
9
|
+
# @return [SlimLint::Logger]
|
10
|
+
def self.silent
|
11
|
+
new(File.open(File::NULL, 'w'))
|
12
|
+
end
|
13
|
+
|
14
|
+
# Creates a new {SlimLint::Logger} instance.
|
15
|
+
#
|
16
|
+
# @param out [IO] the output destination.
|
17
|
+
def initialize(out)
|
18
|
+
@out = out
|
19
|
+
@color_enabled = tty?
|
20
|
+
end
|
21
|
+
|
22
|
+
# Print the specified output.
|
23
|
+
#
|
24
|
+
# @param output [String] the output to send
|
25
|
+
# @param newline [true,false] whether to append a newline
|
26
|
+
def puts(output, newline = true)
|
27
|
+
@out.print(output)
|
28
|
+
@out.print("\n") if newline
|
29
|
+
end
|
30
|
+
|
31
|
+
# Print the specified output without a newline.
|
32
|
+
#
|
33
|
+
# @param output [String] the output to send
|
34
|
+
def print(output)
|
35
|
+
puts(output, false)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Print the specified output in bold face.
|
39
|
+
# If output destination is not a TTY, behaves the same as {#log}.
|
40
|
+
#
|
41
|
+
# @param args [Array<String>]
|
42
|
+
def bold(*args)
|
43
|
+
color('1', *args)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Print the specified output in a color indicative of error.
|
47
|
+
# If output destination is not a TTY, behaves the same as {#log}.
|
48
|
+
#
|
49
|
+
# @param args [Array<String>]
|
50
|
+
def error(*args)
|
51
|
+
color(31, *args)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Print the specified output in a bold face and color indicative of error.
|
55
|
+
# If output destination is not a TTY, behaves the same as {#log}.
|
56
|
+
#
|
57
|
+
# @param args [Array<String>]
|
58
|
+
def bold_error(*args)
|
59
|
+
color('1;31', *args)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Print the specified output in a color indicative of success.
|
63
|
+
# If output destination is not a TTY, behaves the same as {#log}.
|
64
|
+
#
|
65
|
+
# @param args [Array<String>]
|
66
|
+
def success(*args)
|
67
|
+
color(32, *args)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Print the specified output in a color indicative of a warning.
|
71
|
+
# If output destination is not a TTY, behaves the same as {#log}.
|
72
|
+
#
|
73
|
+
# @param args [Array<String>]
|
74
|
+
def warning(*args)
|
75
|
+
color(33, *args)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Print the specified output in a color indicating something worthy of
|
79
|
+
# notice.
|
80
|
+
# If output destination is not a TTY, behaves the same as {#log}.
|
81
|
+
#
|
82
|
+
# @param args [Array<String>]
|
83
|
+
def notice(*args)
|
84
|
+
color(35, *args)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Print the specified output in a color indicating information.
|
88
|
+
# If output destination is not a TTY, behaves the same as {#log}.
|
89
|
+
#
|
90
|
+
# @param args [Array<String>]
|
91
|
+
def info(*args)
|
92
|
+
color(36, *args)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Print a blank line.
|
96
|
+
def newline
|
97
|
+
puts('')
|
98
|
+
end
|
99
|
+
|
100
|
+
# Whether this logger is outputting to a TTY.
|
101
|
+
#
|
102
|
+
# @return [true,false]
|
103
|
+
def tty?
|
104
|
+
@out.respond_to?(:tty?) && @out.tty?
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
# Print output in the specified color.
|
110
|
+
#
|
111
|
+
# @param code [Integer,String] ANSI color code
|
112
|
+
# @param output [String] output to print
|
113
|
+
# @param newline [Boolean] whether to append a newline
|
114
|
+
def color(code, output, newline = true)
|
115
|
+
puts(color_enabled ? "\033[#{code}m#{output}\033[0m" : output, newline)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
module LintTrappings
|
5
|
+
# Processes a collection of streams with the specified command.
|
6
|
+
class Preprocessor
|
7
|
+
def initialize(config)
|
8
|
+
@config = config
|
9
|
+
@command = @config['preprocess_command']
|
10
|
+
@preprocess_files = @config['preprocess_files']
|
11
|
+
end
|
12
|
+
|
13
|
+
def preprocess_files(files_to_lint)
|
14
|
+
return unless @command
|
15
|
+
|
16
|
+
files_to_lint.each do |file_to_lint|
|
17
|
+
preprocess(file_to_lint) if preprocess_file?(file_to_lint.path)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def preprocess(file_to_lint)
|
24
|
+
contents, status = Open3.capture2(@command, stdin_data: file_to_lint.io.read)
|
25
|
+
|
26
|
+
unless status.success?
|
27
|
+
raise PreprocessorError,
|
28
|
+
"Preprocess command `#{@command}` failed when passed the " \
|
29
|
+
"contents of '#{file_to_lint.path}', returning an exit " \
|
30
|
+
"status of #{status.exitstatus}."
|
31
|
+
end
|
32
|
+
|
33
|
+
file_to_lint.io = StringIO.new(contents)
|
34
|
+
end
|
35
|
+
|
36
|
+
def preprocess_file?(file)
|
37
|
+
return true unless @preprocess_files
|
38
|
+
Utils.any_glob_matches?(@preprocess_files, file)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/tasklib'
|
3
|
+
|
4
|
+
module LintTrappings
|
5
|
+
# Rake task interface factory for a LintTrappings application.
|
6
|
+
#
|
7
|
+
# In your application, define your Rake task factory class as:
|
8
|
+
#
|
9
|
+
# require 'lint_trappings/rake_task'
|
10
|
+
# require 'my_app'
|
11
|
+
#
|
12
|
+
# module MyApp
|
13
|
+
# class RakeTask < LintTrappings::RakeTask
|
14
|
+
# def initialize(name = :my_app)
|
15
|
+
# @application_class = MyApp::Application
|
16
|
+
# super
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# Then developers can follow the instructions below (swapping out MyApp/my_app
|
22
|
+
# with the appropriate name of your application) to invoke your application
|
23
|
+
# via Rake.
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# # Add the following to your Rakefile...
|
27
|
+
# require 'my_app/rake_task'
|
28
|
+
#
|
29
|
+
# MyApp::RakeTask.new do |t|
|
30
|
+
# t.config = 'path/to/custom/config.yml'
|
31
|
+
# t.files = %w[app/views/**/*.txt custom/*.txt]
|
32
|
+
# t.quiet = true # Don't display output from app
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# # ...and then execute from the command line:
|
36
|
+
# rake my_app
|
37
|
+
#
|
38
|
+
# You can also specify the list of files as explicit task arguments:
|
39
|
+
#
|
40
|
+
# @example
|
41
|
+
# # Add the following to your Rakefile...
|
42
|
+
# require 'my_app/rake_task'
|
43
|
+
#
|
44
|
+
# MyApp::RakeTask.new
|
45
|
+
#
|
46
|
+
# # ...and then execute from the command line:
|
47
|
+
# rake my_app[some/directory, some/specific/file.txt]
|
48
|
+
#
|
49
|
+
class RakeTask < Rake::TaskLib
|
50
|
+
# Name of the task.
|
51
|
+
# @return [String]
|
52
|
+
attr_accessor :name
|
53
|
+
|
54
|
+
# Path of the configuration file to use.
|
55
|
+
# @return [String]
|
56
|
+
attr_accessor :config
|
57
|
+
|
58
|
+
# List of files to lint.
|
59
|
+
#
|
60
|
+
# Note that this will be ignored if you explicitly pass a list of files as
|
61
|
+
# task arguments via the command line.
|
62
|
+
# @return [Array<String>]
|
63
|
+
attr_accessor :files
|
64
|
+
|
65
|
+
# Whether output from application should not be displayed to the standard
|
66
|
+
# out stream.
|
67
|
+
# @return [true,false]
|
68
|
+
attr_accessor :quiet
|
69
|
+
|
70
|
+
# Create the task so it exists in the current namespace.
|
71
|
+
#
|
72
|
+
# @param name [Symbol] task name
|
73
|
+
def initialize(name)
|
74
|
+
@name = name
|
75
|
+
@files = []
|
76
|
+
@quiet = false
|
77
|
+
|
78
|
+
# Allow custom configuration to be defined in a block passed to constructor
|
79
|
+
yield self if block_given?
|
80
|
+
|
81
|
+
define
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
# Defines the Rake task.
|
87
|
+
def define
|
88
|
+
desc default_description unless ::Rake.application.last_description
|
89
|
+
|
90
|
+
task(name, [:files]) do |_task, task_args|
|
91
|
+
run_cli(task_args)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Executes the CLI given the specified task arguments.
|
96
|
+
#
|
97
|
+
# @param task_args [Rake::TaskArguments]
|
98
|
+
def run_cli(task_args)
|
99
|
+
raise ArgumentError, '@application_class must be defined!' unless @application_class
|
100
|
+
|
101
|
+
output = quiet ? LintTrappings::Output.silent : LintTrappings::Output.new(STDOUT)
|
102
|
+
app = @application_class.new(output)
|
103
|
+
|
104
|
+
options = {}.tap do |opts|
|
105
|
+
opts[:command] = :scan
|
106
|
+
opts[:config_file] = @config if @config
|
107
|
+
opts[:included_paths] = files_to_lint(task_args)
|
108
|
+
end
|
109
|
+
|
110
|
+
begin
|
111
|
+
app.run(options) # Will raise exception on failure
|
112
|
+
rescue LintTrappings::ScanWarned
|
113
|
+
puts "#{app.name} reported warnings"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns the list of files that should be linted given the specified task
|
118
|
+
# arguments.
|
119
|
+
#
|
120
|
+
# @param task_args [Rake::TaskArguments]
|
121
|
+
def files_to_lint(task_args)
|
122
|
+
# Note: we're abusing Rake's argument handling a bit here. We call the
|
123
|
+
# first argument `files` but it's actually only the first file--we pull
|
124
|
+
# the rest out of the `extras` from the task arguments. This is so we
|
125
|
+
# can specify an arbitrary list of files separated by commas on the
|
126
|
+
# command line or in a custom task definition.
|
127
|
+
explicit_files = Array(task_args[:files]) + Array(task_args.extras)
|
128
|
+
|
129
|
+
explicit_files.any? ? explicit_files : @files
|
130
|
+
end
|
131
|
+
|
132
|
+
# Friendly description that shows up in Rake task listing.
|
133
|
+
#
|
134
|
+
# This allows us to change the information displayed by `rake --tasks` based
|
135
|
+
# on the options passed to the constructor which defined the task.
|
136
|
+
#
|
137
|
+
# @return [String]
|
138
|
+
def default_description
|
139
|
+
description = "Run `#{@application_class.name}`"
|
140
|
+
description << ' quietly' if @quiet
|
141
|
+
description << " using config file #{@config}" if @config
|
142
|
+
description
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module LintTrappings
|
2
|
+
# Contains information about all lints detected during a scan.
|
3
|
+
class Report
|
4
|
+
# List of lints that were found.
|
5
|
+
# @return [Array<LintTrappings::Lint]
|
6
|
+
attr_accessor :lints
|
7
|
+
|
8
|
+
# @return [Array<String>] List of files that were inspected.
|
9
|
+
attr_reader :documents_inspected
|
10
|
+
|
11
|
+
# @param config [LintTrappings::Configuration]
|
12
|
+
# @param lints [Array<LintTrappings::Lint>] lints that were found
|
13
|
+
# @param documents [Array<Document>] files that were linted
|
14
|
+
def initialize(config, lints, documents)
|
15
|
+
@config = config
|
16
|
+
@lints = lints.sort_by { |lint| [lint.path, lint.source_range.begin.line] }
|
17
|
+
@documents_inspected = documents
|
18
|
+
end
|
19
|
+
|
20
|
+
def severities
|
21
|
+
@severities ||= @config.fetch('severities', error: 'fail', warning: 'warn')
|
22
|
+
end
|
23
|
+
|
24
|
+
def fail_severities
|
25
|
+
@fail_severities ||= severities.select { |_severity, action| action == 'fail' }.keys
|
26
|
+
end
|
27
|
+
|
28
|
+
def warn_severities
|
29
|
+
@warn_severities ||= severities.select { |_severity, action| action == 'warn' }.keys
|
30
|
+
end
|
31
|
+
|
32
|
+
def failures?
|
33
|
+
failures.any?
|
34
|
+
end
|
35
|
+
|
36
|
+
def failures
|
37
|
+
@failures ||=
|
38
|
+
begin
|
39
|
+
@lints.select { |lint| fail_severities.include?(lint.severity) }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def warnings?
|
44
|
+
warnings.any?
|
45
|
+
end
|
46
|
+
|
47
|
+
def warnings
|
48
|
+
@warnings ||=
|
49
|
+
begin
|
50
|
+
@lints.select { |lint| warn_severities.include?(lint.severity) }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def success?
|
55
|
+
lints.empty?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'lint_trappings/formatter_forwarder'
|
2
|
+
require 'lint_trappings/formatter_loader'
|
3
|
+
require 'lint_trappings/preprocessor'
|
4
|
+
|
5
|
+
module LintTrappings
|
6
|
+
# Linter runner.
|
7
|
+
#
|
8
|
+
# Runs linters against a set of files, ensuring the appropriate linters are
|
9
|
+
# run against the relevant files based on configuration.
|
10
|
+
class Runner
|
11
|
+
def initialize(application, config, output)
|
12
|
+
@application = application
|
13
|
+
@config = config
|
14
|
+
@output = output
|
15
|
+
end
|
16
|
+
|
17
|
+
# A individual unit of work which can be processed by a worker.
|
18
|
+
Job = Struct.new(:linter, :path)
|
19
|
+
|
20
|
+
# Runs the appropriate linters against the set of specified files, return a
|
21
|
+
# report of all lints found.
|
22
|
+
#
|
23
|
+
# @param options [Hash]
|
24
|
+
#
|
25
|
+
# @return [LintTrappings::Report] report of all lints found and other statistics
|
26
|
+
def run(options = {})
|
27
|
+
@options = options
|
28
|
+
|
29
|
+
# Coalesce formatters into a single formatter which will forward calls
|
30
|
+
formatters = FormatterLoader.new(@application, @config, @output).load(options)
|
31
|
+
@formatter = FormatterForwarder.new(formatters)
|
32
|
+
|
33
|
+
# We store the documents in a map so that if we're parallelizing the run
|
34
|
+
# we don't need to pass serialized Document objects via IPC, just the path
|
35
|
+
# string. Since forking will use copy-on-write semantics, we'll be able
|
36
|
+
# to reuse the memory storing those documents for all workers, since we're
|
37
|
+
# just reading.
|
38
|
+
@paths_to_documents_map, parse_lints = load_documents_to_lint(options)
|
39
|
+
|
40
|
+
# Extract all jobs we want to run as file/linter pairs
|
41
|
+
linter_selector = LinterSelector.new(@application, @config, options)
|
42
|
+
jobs = @paths_to_documents_map.keys.map do |path|
|
43
|
+
linter_selector.linters_for_file(path).map { |linter| Job.new(linter, path) }
|
44
|
+
end.flatten
|
45
|
+
|
46
|
+
lints = find_all_lints(jobs) + parse_lints
|
47
|
+
report = Report.new(@config, lints, @paths_to_documents_map.values)
|
48
|
+
|
49
|
+
@formatter.finished(report)
|
50
|
+
|
51
|
+
report
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# A file to be linted.
|
57
|
+
FileToLint = Struct.new(:io, :path)
|
58
|
+
|
59
|
+
def determine_files_to_lint(options)
|
60
|
+
if options[:stdin_file_path]
|
61
|
+
[FileToLint.new(options[:stdin], options[:stdin_file_path])]
|
62
|
+
else
|
63
|
+
find_files(options).map do |path|
|
64
|
+
FileToLint.new(File.open(path), path)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
rescue Errno::ENOENT => err
|
68
|
+
raise InvalidFilePathError, err.message
|
69
|
+
end
|
70
|
+
|
71
|
+
def find_files(options)
|
72
|
+
opts = {}
|
73
|
+
opts[:allowed_extensions] = @config.fetch(:file_extensions, @application.file_extensions)
|
74
|
+
|
75
|
+
opts[:included_paths] = options.fetch(:included_paths, @config.fetch(:included_paths, []))
|
76
|
+
opts[:excluded_paths] = @config.fetch(:excluded_paths, []) +
|
77
|
+
options.fetch(:excluded_paths, [])
|
78
|
+
|
79
|
+
opts[:included_patterns] = @config.fetch(:include) do
|
80
|
+
if opts[:included_paths].any?
|
81
|
+
# Don't specify default inclusion pattern since include paths were
|
82
|
+
# explicitly specified
|
83
|
+
[]
|
84
|
+
else
|
85
|
+
# Otherwise, we want the default behavior to lint all files with the
|
86
|
+
# default file extensions
|
87
|
+
opts[:allowed_extensions].map { |ext| "**/*.#{ext}" }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
opts[:excluded_patterns] = @config.fetch(:exclude, [])
|
91
|
+
|
92
|
+
FileFinder.find(opts)
|
93
|
+
end
|
94
|
+
|
95
|
+
def load_documents_to_lint(options)
|
96
|
+
documents = {}
|
97
|
+
parse_lints = []
|
98
|
+
|
99
|
+
files_to_lint = determine_files_to_lint(options)
|
100
|
+
Preprocessor.new(@config).preprocess_files(files_to_lint)
|
101
|
+
@formatter.started(files_to_lint)
|
102
|
+
|
103
|
+
files_to_lint.each do |file_to_lint|
|
104
|
+
begin
|
105
|
+
documents[file_to_lint.path] =
|
106
|
+
@application.document_class.new(file_to_lint.io.read,
|
107
|
+
@config,
|
108
|
+
path: file_to_lint.path)
|
109
|
+
rescue ParseError => err
|
110
|
+
parse_lints << Lint.new(
|
111
|
+
path: file_to_lint.path,
|
112
|
+
source_range: err.source_range,
|
113
|
+
message: "Error occurred while parsing #{file_to_lint.path}: #{err.message}",
|
114
|
+
severity: :error,
|
115
|
+
)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
[documents, parse_lints]
|
120
|
+
end
|
121
|
+
|
122
|
+
def scan_document(job)
|
123
|
+
@formatter.job_started(job)
|
124
|
+
|
125
|
+
document = @paths_to_documents_map[job.path]
|
126
|
+
reported_lints = job.linter.run(document)
|
127
|
+
|
128
|
+
@formatter.job_finished(job, reported_lints)
|
129
|
+
|
130
|
+
reported_lints
|
131
|
+
rescue => err
|
132
|
+
loc = Location.new(1)
|
133
|
+
message = "Error occurred while linting #{job.path}: #{err.message}"
|
134
|
+
|
135
|
+
lints = [Lint.new(
|
136
|
+
linter: job.linter,
|
137
|
+
path: job.path,
|
138
|
+
source_range: loc..loc,
|
139
|
+
message: message,
|
140
|
+
severity: :error,
|
141
|
+
exception: err,
|
142
|
+
)]
|
143
|
+
|
144
|
+
@formatter.job_finished(job, lints)
|
145
|
+
lints
|
146
|
+
end
|
147
|
+
|
148
|
+
def find_all_lints(jobs)
|
149
|
+
lints =
|
150
|
+
if workers = @options[:concurrency]
|
151
|
+
require 'parallel'
|
152
|
+
workers = workers == 'auto' ? Parallel.processor_count : Integer(workers)
|
153
|
+
::Parallel.map(jobs, { in_processes: workers }, &method(:scan_document))
|
154
|
+
else
|
155
|
+
jobs.map(&method(:scan_document))
|
156
|
+
end
|
157
|
+
|
158
|
+
lints.flatten
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|