Skip to content

Commit

Permalink
Add support for showing end location marker (#200)
Browse files Browse the repository at this point in the history
* Add support for showing end location marker

* Cleanup Reportable method definitions

There’s no need for double splats, since they mess up method resolution, and obscure the actual - single (!) - argument - `status`, so… be gone

Also, all of the helpers return the constructed `Issue` like a behaving good methods.

* Refactor Util#affected_code

* Increase max length of trimmed lines to 120 characters

* Refactor Issue to use enum instead of a symbol for #status

* Optimize Reportable#valid?

* Add spec coverage for newly added Util methods

* Refactor DotFormatter a bit

Make text format moar in line with Crystal spec runner.

* Update README.md
Sija authored Jan 26, 2021

Unverified

This user has not yet uploaded their public signing key.
1 parent 8b52dc4 commit ea98554
Showing 9 changed files with 226 additions and 101 deletions.
10 changes: 4 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@
<p align="center">Code style linter for Crystal<p>
<p align="center">
<sup>
<i> (a single-celled animal that catches food and moves about by extending fingerlike projections of protoplasm) </i>
<i>(a single-celled animal that catches food and moves about by extending fingerlike projections of protoplasm)</i>
</sup>
</p>
<p align="center">
@@ -35,7 +35,7 @@
## About

Ameba is a static code analysis tool for the Crystal language.
It enforces a consistent [Crystal code style](https://crystal-lang.org/docs/conventions/coding_style.html),
It enforces a consistent [Crystal code style](https://crystal-lang.org/reference/conventions/coding_style.html),
also catches code smells and wrong code constructions.

See also [Roadmap](https://github.com/crystal-ameba/ameba/wiki).
@@ -46,7 +46,7 @@ Run `ameba` binary within your project directory to catch code issues:

```sh
$ ameba
Inspecting 107 files.
Inspecting 107 files

...............F.....................F....................................................................

@@ -61,9 +61,7 @@ src/ameba/formatter/base_formatter.cr:12:7
^

Finished in 542.64 milliseconds

129 inspected, 2 failures.

129 inspected, 2 failures
```

### Run in parallel
10 changes: 5 additions & 5 deletions spec/ameba/formatter/dot_formatter_spec.cr
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ module Ameba::Formatter
describe "#started" do
it "writes started message" do
subject.started [Source.new ""]
output.to_s.should eq "Inspecting 1 file.\n\n"
output.to_s.should eq "Inspecting 1 file\n\n"
end
end

@@ -29,7 +29,7 @@ module Ameba::Formatter
describe "#finished" do
it "writes a final message" do
subject.finished [Source.new ""]
output.to_s.should contain "1 inspected, 0 failures."
output.to_s.should contain "1 inspected, 0 failures"
end

it "writes the elapsed time" do
@@ -45,7 +45,7 @@ module Ameba::Formatter
end
subject.finished [s]
log = output.to_s
log.should contain "1 inspected, 2 failures."
log.should contain "1 inspected, 2 failures"
log.should contain "DummyRuleError"
log.should contain "NamedRuleError"
end
@@ -60,7 +60,7 @@ module Ameba::Formatter
end
subject.finished [s]
log = output.to_s
log.should contain "> a = 22"
log.should contain "> \e[97ma = 22"
log.should contain " \e[33m^\e[0m"
end

@@ -99,7 +99,7 @@ module Ameba::Formatter
s.add_issue(DummyRule.new, location: {1, 1},
message: "DummyRuleError", status: :disabled)
subject.finished [s]
output.to_s.should contain "1 inspected, 0 failures."
output.to_s.should contain "1 inspected, 0 failures"
end
end
end
61 changes: 60 additions & 1 deletion spec/ameba/formatter/util_spec.cr
Original file line number Diff line number Diff line change
@@ -8,6 +8,65 @@ module Ameba::Formatter
subject = Subject.new

describe Util do
describe "#deansify" do
it "returns given string without ANSI codes" do
str = String.build do |io|
io << "foo".colorize.green.underline
io << '-'
io << "bar".colorize.red.underline
end
subject.deansify("foo-bar").should eq "foo-bar"
subject.deansify(str).should eq "foo-bar"
end
end

describe "#trim" do
it "trims string longer than :max_length" do
subject.trim(("+" * 300), 1).should eq "+"
subject.trim(("+" * 300), 3).should eq "+++"
subject.trim(("+" * 300), 5).should eq "+ ..."
subject.trim(("+" * 300), 7).should eq "+++ ..."
end

it "leaves intact string shorter than :max_length" do
subject.trim(("+" * 3), 100).should eq "+++"
end

it "allows to use custom ellipsis" do
subject.trim(("+" * 300), 3, "").should eq "++…"
end
end

describe "#context" do
it "returns correct pre/post context lines" do
source = Source.new <<-EOF
# pre:1
# pre:2
# pre:3
# pre:4
# pre:5
a = 1
# post:1
# post:2
# post:3
# post:4
# post:5
EOF

subject.context(source.lines, lineno: 6, context_lines: 3)
.should eq({<<-PRE.lines, <<-POST.lines
# pre:3
# pre:4
# pre:5
PRE
# post:1
# post:2
# post:3
POST
})
end
end

describe "#affected_code" do
it "returns nil if there is no such a line number" do
source = Source.new %(
@@ -23,7 +82,7 @@ module Ameba::Formatter
)
location = Crystal::Location.new("filename", 1, 1)
subject.deansify(subject.affected_code(source, location))
.should eq "> a = 1\n ^"
.should eq "> a = 1\n ^\n"
end

it "returns correct line if it is found" do
17 changes: 15 additions & 2 deletions spec/ameba/issue_spec.cr
Original file line number Diff line number Diff line change
@@ -42,9 +42,22 @@ module Ameba
location: nil,
end_location: nil,
message: "",
status: :enabled
status: :disabled

issue.status.should eq :enabled
issue.status.should eq Issue::Status::Disabled
issue.disabled?.should be_true
issue.enabled?.should be_false
end

it "sets status to :enabled by default" do
issue = Issue.new rule: DummyRule.new,
location: nil,
end_location: nil,
message: ""

issue.status.should eq Issue::Status::Enabled
issue.enabled?.should be_true
issue.disabled?.should be_false
end
end
end
23 changes: 12 additions & 11 deletions src/ameba/formatter/dot_formatter.cr
Original file line number Diff line number Diff line change
@@ -6,14 +6,15 @@ module Ameba::Formatter
class DotFormatter < BaseFormatter
include Util

@started_at : Time?
@started_at : Time::Span?
@mutex = Thread::Mutex.new

# Reports a message when inspection is started.
def started(sources)
@started_at = Time.utc # Time.monotonic
@started_at = Time.monotonic

output << started_message(sources.size)
output.puts started_message(sources.size)
output.puts
end

# Reports a result of the inspection of a corresponding source.
@@ -41,29 +42,29 @@ module Ameba::Formatter
"#{issue.rule.name}: " \
"#{issue.message}".colorize(:red)

if show_affected_code && (code = affected_code(source, location))
if show_affected_code && (code = affected_code(source, location, issue.end_location))
output << code.colorize(:default)
end

output << '\n'
output.puts
end
end

output << finished_in_message(@started_at, Time.utc) # Time.monotonic
output << final_message(sources, failed_sources)
output.puts finished_in_message(@started_at, Time.monotonic)
output.puts final_message(sources, failed_sources)
end

private def started_message(size)
if size == 1
"Inspecting 1 file.\n\n".colorize(:default)
"Inspecting 1 file".colorize(:default)
else
"Inspecting #{size} files.\n\n".colorize(:default)
"Inspecting #{size} files".colorize(:default)
end
end

private def finished_in_message(started, finished)
if started && finished
"Finished in #{to_human(finished - started)} \n\n".colorize(:default)
"Finished in #{to_human(finished - started)}".colorize(:default)
end
end

@@ -93,7 +94,7 @@ module Ameba::Formatter
color = failures == 0 ? :green : :red
s = failures != 1 ? "s" : ""

"#{total} inspected, #{failures} failure#{s}.\n".colorize(color)
"#{total} inspected, #{failures} failure#{s}".colorize(color)
end
end
end
29 changes: 15 additions & 14 deletions src/ameba/formatter/explain_formatter.cr
Original file line number Diff line number Diff line change
@@ -20,35 +20,36 @@ module Ameba::Formatter
# ExplainFormatter.new output,
# {file: path, line: line_number, column: column_number}
# ```
def initialize(@output, loc)
@location = Crystal::Location.new(loc[:file], loc[:line], loc[:column])
def initialize(@output, location)
@location = Crystal::Location.new(location[:file], location[:line], location[:column])
end

# Reports the explainations at the *@location*.
def finished(sources)
source = sources.find { |s| s.path == @location.filename }

source = sources.find(&.path.==(@location.filename))
return unless source

source.issues.each do |issue|
if (location = issue.location) &&
location.line_number == @location.line_number &&
location.column_number == @location.column_number
explain(source, issue)
end
end
issue = source.issues.find(&.location.==(@location))
return unless issue

explain(source, issue)
end

private def explain(source, issue)
rule = issue.rule

location, end_location =
issue.location, issue.end_location

return unless location

output_title "ISSUE INFO"
output_paragraph [
issue.message.colorize(:red).to_s,
@location.to_s.colorize(:cyan).to_s,
location.to_s.colorize(:cyan).to_s,
]

if affected_code = affected_code(source, @location, context_lines: 3)
if affected_code = affected_code(source, location, end_location, context_lines: 3)
output_title "AFFECTED CODE"
output_paragraph affected_code
end
@@ -68,7 +69,7 @@ module Ameba::Formatter
end

private def output_paragraph(paragraph : String)
output_paragraph(paragraph.split('\n'))
output_paragraph(paragraph.lines)
end

private def output_paragraph(paragraph : Array(String))
Loading

0 comments on commit ea98554

Please sign in to comment.