Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement better handling of long files and fixes #12

Merged
merged 5 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 28 additions & 4 deletions src/rapids_pre_commit_hooks/lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import contextlib
import functools
import itertools
import re
import warnings


Expand Down Expand Up @@ -70,6 +71,8 @@ def __repr__(self):


class Linter:
NEWLINE_RE = re.compile("[\r\n]")

def __init__(self, filename, content):
self.filename = filename
self.content = content
Expand Down Expand Up @@ -117,31 +120,52 @@ def print_warnings(self, fix_applied=False):

for replacement in warning.replacements:
line_index = self.line_for_pos(replacement.pos[0])
newtext = replacement.newtext
if match := self.NEWLINE_RE.search(newtext):
newtext = newtext[: match.start()] + "..."
long = True
else:
long = False

print(f"In file {self.filename}:{line_index + 1}:")
self.print_highlighted_code(replacement.pos, replacement.newtext)
self.print_highlighted_code(replacement.pos, newtext)
if fix_applied:
print("note: suggested fix applied")
if long:
print("note: suggested fix applied but is too long to display")
else:
print("note: suggested fix applied")
else:
print("note: suggested fix")
if long:
print(
"note: suggested fix is too long to display, use --fix to "
"apply it"
)
else:
print("note: suggested fix")
print()

def print_highlighted_code(self, pos, replacement=""):
line_index = self.line_for_pos(pos[0])
line_pos = self.lines[line_index]
left = pos[0] - line_pos[0]

if replacement:
replacement = f" {replacement}"
KyleFromNVIDIA marked this conversation as resolved.
Show resolved Hide resolved

if self.line_for_pos(pos[1]) == line_index:
right = pos[1] - line_pos[0]
long = False
else:
right = line_pos[1] - line_pos[0]
long = True
length = right - left

print(self.content[line_pos[0] : line_pos[1]])
print(" " * left, end="")
if length == 0:
print(f"^{replacement}")
else:
print(f"{'~' * length}{replacement}")
print(f"{'~' * (length - 1)}{'|' if long else '~'}{replacement}")

def line_for_pos(self, index):
@functools.total_ordering
Expand Down
112 changes: 104 additions & 8 deletions test/rapids_pre_commit_hooks/test_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,14 @@ def binary_file(self):
f.seek(0)
yield f

@pytest.fixture
def long_file(self):
with tempfile.NamedTemporaryFile("w+") as f:
f.write("This is a long file\nIt has multiple lines\n")
f.flush()
f.seek(0)
yield f

def the_check(self, linter, args):
assert args.check_test
linter.add_warning((0, 5), "say good bye instead").add_replacement(
Expand All @@ -150,6 +158,14 @@ def the_check(self, linter, args):
if linter.content[5] != "!":
linter.add_warning((5, 5), "use punctuation").add_replacement((5, 5), ",")

def long_file_check(self, linter, args):
linter.add_warning((0, len(linter.content)), "this is a long file")

def long_fix_check(self, linter, args):
linter.add_warning((0, 19), "this is a long line").add_replacement(
(0, 19), "This is a long file\nIt's even longer now"
)

def test_no_warnings_no_fix(self, hello_world_file, capsys):
with patch("sys.argv", ["check-test", "--check-test", hello_world_file.name]):
m = LintMain()
Expand Down Expand Up @@ -191,7 +207,7 @@ def test_warnings_no_fix(self, hello_world_file, capsys):

In file {hello_world_file.name}:1:
Hello world!
~~~~~Good bye
~~~~~ Good bye
note: suggested fix

In file {hello_world_file.name}:1:
Expand All @@ -201,7 +217,7 @@ def test_warnings_no_fix(self, hello_world_file, capsys):

In file {hello_world_file.name}:1:
Hello world!
^,
^ ,
note: suggested fix

"""
Expand All @@ -226,7 +242,7 @@ def test_warnings_fix(self, hello_world_file, capsys):

In file {hello_world_file.name}:1:
Hello world!
~~~~~Good bye
~~~~~ Good bye
note: suggested fix applied

In file {hello_world_file.name}:1:
Expand All @@ -236,7 +252,7 @@ def test_warnings_fix(self, hello_world_file, capsys):

In file {hello_world_file.name}:1:
Hello world!
^,
^ ,
note: suggested fix applied

"""
Expand Down Expand Up @@ -269,7 +285,7 @@ def test_multiple_files(self, hello_world_file, hello_file, capsys):

In file {hello_world_file.name}:1:
Hello world!
~~~~~Good bye
~~~~~ Good bye
note: suggested fix applied

In file {hello_world_file.name}:1:
Expand All @@ -279,7 +295,7 @@ def test_multiple_files(self, hello_world_file, hello_file, capsys):

In file {hello_world_file.name}:1:
Hello world!
^,
^ ,
note: suggested fix applied

In file {hello_file.name}:1:
Expand All @@ -289,13 +305,13 @@ def test_multiple_files(self, hello_world_file, hello_file, capsys):

In file {hello_file.name}:1:
Hello!
~~~~~Good bye
~~~~~ Good bye
note: suggested fix applied

"""
)

def test_binary_file(self, binary_file, capsys):
def test_binary_file(self, binary_file):
mock_linter = Mock(wraps=Linter)
with patch(
"sys.argv",
Expand All @@ -314,3 +330,83 @@ def test_binary_file(self, binary_file, capsys):
with m.execute() as ctx:
ctx.add_check(self.the_check)
mock_linter.assert_not_called()

def test_long_file(self, long_file, capsys):
with patch(
"sys.argv",
[
"check-test",
long_file.name,
],
), pytest.raises(SystemExit, match=r"^1$"):
m = LintMain()
with m.execute() as ctx:
ctx.add_check(self.long_file_check)
ctx.add_check(self.long_fix_check)
assert (
long_file.read()
== """This is a long file
It has multiple lines
"""
)
captured = capsys.readouterr()
assert (
captured.out
== f"""In file {long_file.name}:1:
This is a long file
~~~~~~~~~~~~~~~~~~~
warning: this is a long line

In file {long_file.name}:1:
This is a long file
~~~~~~~~~~~~~~~~~~~ This is a long file...
note: suggested fix is too long to display, use --fix to apply it

In file {long_file.name}:1:
This is a long file
~~~~~~~~~~~~~~~~~~|
warning: this is a long file

"""
)

def test_long_file_fix(self, long_file, capsys):
with patch(
"sys.argv",
[
"check-test",
"--fix",
long_file.name,
],
), pytest.raises(SystemExit, match=r"^1$"):
m = LintMain()
with m.execute() as ctx:
ctx.add_check(self.long_file_check)
ctx.add_check(self.long_fix_check)
assert (
long_file.read()
== """This is a long file
It's even longer now
It has multiple lines
"""
)
captured = capsys.readouterr()
assert (
captured.out
== f"""In file {long_file.name}:1:
This is a long file
~~~~~~~~~~~~~~~~~~~
warning: this is a long line

In file {long_file.name}:1:
This is a long file
~~~~~~~~~~~~~~~~~~~ This is a long file...
note: suggested fix applied but is too long to display

In file {long_file.name}:1:
This is a long file
~~~~~~~~~~~~~~~~~~|
warning: this is a long file

"""
)