lint_trappings 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|