Skip to content

Commit

Permalink
Merge pull request #554 from nobodywasishere/nobody/heredoc-indent
Browse files Browse the repository at this point in the history
Add `Style/HeredocIndent`
  • Loading branch information
Sija authored Feb 4, 2025
2 parents 17cdb61 + e080de7 commit bf25d80
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 25 deletions.
16 changes: 8 additions & 8 deletions spec/ameba/rule/documentation/documentation_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ module Ameba::Rule::Documentation
private macro bag
end
CRYSTAL
CRYSTAL
end

it "passes for documented public types" do
Expand Down Expand Up @@ -59,31 +59,31 @@ module Ameba::Rule::Documentation
# bag
macro bag
end
CRYSTAL
CRYSTAL
end

it "fails if there is an undocumented public type" do
expect_issue subject, <<-CRYSTAL
class Foo
# ^^^^^^^^^ error: Missing documentation
# ^^^^^^^ error: Missing documentation
end
module Bar
# ^^^^^^^^^^ error: Missing documentation
# ^^^^^^^^ error: Missing documentation
end
enum Baz
# ^^^^^^^^ error: Missing documentation
# ^^^^^^ error: Missing documentation
end
def bat
# ^^^^^^^ error: Missing documentation
# ^^^^^ error: Missing documentation
end
macro bag
# ^^^^^^^^^ error: Missing documentation
# ^^^^^^^ error: Missing documentation
end
CRYSTAL
CRYSTAL
end

context "properties" do
Expand Down
10 changes: 5 additions & 5 deletions spec/ameba/rule/lint/empty_ensure_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@ module Ameba::Rule::Lint
ensure
nil
end
CRYSTAL
CRYSTAL
end

it "fails if there is an empty ensure in method" do
expect_issue subject, <<-CRYSTAL
def method
do_some_stuff
ensure
# ^^^^^^ error: Empty `ensure` block detected
# ^^^^ error: Empty `ensure` block detected
end
CRYSTAL
CRYSTAL
end

it "fails if there is an empty ensure in a block" do
Expand All @@ -43,10 +43,10 @@ module Ameba::Rule::Lint
rescue
do_some_other_stuff
ensure
# ^^^^^^ error: Empty `ensure` block detected
# ^^^^ error: Empty `ensure` block detected
# nothing here
end
CRYSTAL
CRYSTAL
end
end
end
24 changes: 12 additions & 12 deletions spec/ameba/rule/lint/shadowed_exception_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ module Ameba::Rule::Lint
rescue e : Exception
handle_exception
end
CRYSTAL
CRYSTAL
end

it "fails if there is a shadowed exception" do
Expand All @@ -38,7 +38,7 @@ module Ameba::Rule::Lint
# ^^^^^^^^^^^^^ error: Shadowed exception found: `ArgumentError`
handle_argument_error_exception
end
CRYSTAL
CRYSTAL
end

it "fails if there is a custom shadowed exceptions" do
Expand All @@ -51,7 +51,7 @@ module Ameba::Rule::Lint
# ^^^^^^^^^^^^^^^^ error: Shadowed exception found: `MySuperException`
3
end
CRYSTAL
CRYSTAL
end

it "fails if there is a shadowed exception in a type list" do
Expand All @@ -60,7 +60,7 @@ module Ameba::Rule::Lint
rescue Exception | IndexError
# ^^^^^^^^^^ error: Shadowed exception found: `IndexError`
end
CRYSTAL
CRYSTAL
end

it "fails if there is a first shadowed exception in a type list" do
Expand All @@ -72,7 +72,7 @@ module Ameba::Rule::Lint
# ^^^^^^^^^ error: Shadowed exception found: `Exception`
rescue
end
CRYSTAL
CRYSTAL
end

it "fails if there is a shadowed duplicated exception" do
Expand All @@ -83,7 +83,7 @@ module Ameba::Rule::Lint
rescue IndexError
# ^^^^^^^^^^ error: Shadowed exception found: `IndexError`
end
CRYSTAL
CRYSTAL
end

it "fails if there is a shadowed duplicated exception in a type list" do
Expand All @@ -93,7 +93,7 @@ module Ameba::Rule::Lint
rescue ArgumentError | IndexError
# ^^^^^^^^^^ error: Shadowed exception found: `IndexError`
end
CRYSTAL
CRYSTAL
end

it "fails if there is only shadowed duplicated exceptions" do
Expand All @@ -104,7 +104,7 @@ module Ameba::Rule::Lint
# ^^^^^^^^^^ error: Shadowed exception found: `IndexError`
rescue Exception
end
CRYSTAL
CRYSTAL
end

it "fails if there is only shadowed duplicated exceptions in a type list" do
Expand All @@ -113,7 +113,7 @@ module Ameba::Rule::Lint
rescue IndexError | IndexError
# ^^^^^^^^^^ error: Shadowed exception found: `IndexError`
end
CRYSTAL
CRYSTAL
end

it "fails if all rescues are shadowed and there is a catch-all rescue" do
Expand All @@ -129,7 +129,7 @@ module Ameba::Rule::Lint
# ^^^^^^^^ error: Shadowed exception found: `KeyError`
rescue
end
CRYSTAL
CRYSTAL
end

it "fails if there are shadowed exception with args" do
Expand All @@ -140,7 +140,7 @@ module Ameba::Rule::Lint
# ^^^^^^^^^^ error: Shadowed exception found: `IndexError`
rescue
end
CRYSTAL
CRYSTAL
end

it "fails if there are multiple shadowed exceptions" do
Expand All @@ -152,7 +152,7 @@ module Ameba::Rule::Lint
rescue IndexError
# ^^^^^^^^^^ error: Shadowed exception found: `IndexError`
end
CRYSTAL
CRYSTAL
end

it "fails if there are multiple shadowed exceptions in a type list" do
Expand Down
81 changes: 81 additions & 0 deletions spec/ameba/rule/style/heredoc_indent_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
require "../../../spec_helper"

module Ameba::Rule::Style
describe HeredocIndent do
subject = HeredocIndent.new

it "passes if heredoc body indented one level" do
expect_no_issues subject, <<-CRYSTAL
<<-HEREDOC
hello world
HEREDOC
<<-HEREDOC
hello world
HEREDOC
CRYSTAL
end

it "fails if the heredoc body is indented incorrectly" do
expect_issue subject, <<-CRYSTAL
<<-ONE
# ^^^^ error: Heredoc body should be indented by 2 spaces
hello world
ONE
<<-TWO
# ^^^^^^ error: Heredoc body should be indented by 2 spaces
hello world
TWO
<<-THREE
# ^^^^^^^^ error: Heredoc body should be indented by 2 spaces
hello world
THREE
<<-FOUR
# ^^^^^^^ error: Heredoc body should be indented by 2 spaces
hello world
FOUR
CRYSTAL
end

context "properties" do
context "#indent_by" do
rule = HeredocIndent.new
rule.indent_by = 0

it "passes if heredoc body has the same indent level" do
expect_no_issues rule, <<-CRYSTAL
<<-HEREDOC
hello world
HEREDOC
<<-HEREDOC
hello world
HEREDOC
CRYSTAL
end

it "fails if the heredoc body is indented incorrectly" do
expect_issue rule, <<-CRYSTAL
<<-ONE
# ^^^^ error: Heredoc body should be indented by 0 spaces
hello world
ONE
<<-TWO
# ^^^^^^ error: Heredoc body should be indented by 0 spaces
hello world
TWO
<<-FOUR
# ^^^^^^^ error: Heredoc body should be indented by 0 spaces
hello world
FOUR
CRYSTAL
end
end
end
end
end
66 changes: 66 additions & 0 deletions src/ameba/rule/style/heredoc_indent.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
module Ameba::Rule::Style
# A rule that enforces _Heredoc_ bodies be indented one level above the indentation of the
# line they're used on.
#
# For example, this is considered invalid:
#
# ```
# <<-HERERDOC
# hello world
# HEREDOC
#
# <<-HERERDOC
# hello world
# HEREDOC
# ```
#
# And should be written as:
#
# ```
# <<-HERERDOC
# hello world
# HEREDOC
#
# <<-HERERDOC
# hello world
# HEREDOC
# ```
#
# The `IndentBy` configuration option changes the enforced indentation level of the _heredoc_.
#
# ```
# Style/HeredocIndent:
# Enabled: true
# IndentBy: 2
# ```
class HeredocIndent < Base
properties do
since_version "1.7.0"
description "Recommends heredoc bodies are indented consistently"
indent_by 2
end

MSG = "Heredoc body should be indented by %s spaces"

def test(source, node : Crystal::StringInterpolation)
return unless start_location = node.location

start_location_pos = source.pos(start_location)
return unless source.code[start_location_pos..(start_location_pos + 2)]? == "<<-"

correct_indent = line_indent(source, start_location) + indent_by

unless node.heredoc_indent == correct_indent
issue_for node, MSG % indent_by
end
end

private def line_indent(source, start_location) : Int32
line_location = Crystal::Location.new(nil, start_location.line_number, 1)
line_location_pos = source.pos(line_location)
line = source.code[line_location_pos..(line_location_pos + start_location.column_number)]

line.size - line.lstrip.size
end
end
end

0 comments on commit bf25d80

Please sign in to comment.