lkj 0.1.46__tar.gz → 0.1.47__tar.gz
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.
- {lkj-0.1.46 → lkj-0.1.47}/PKG-INFO +1 -1
- {lkj-0.1.46 → lkj-0.1.47}/lkj/strings.py +49 -11
- lkj-0.1.47/lkj/tests/__init__.py +1 -0
- lkj-0.1.47/lkj/tests/test_strings.py +48 -0
- {lkj-0.1.46 → lkj-0.1.47}/lkj.egg-info/PKG-INFO +1 -1
- {lkj-0.1.46 → lkj-0.1.47}/lkj.egg-info/SOURCES.txt +3 -1
- {lkj-0.1.46 → lkj-0.1.47}/setup.cfg +1 -1
- {lkj-0.1.46 → lkj-0.1.47}/LICENSE +0 -0
- {lkj-0.1.46 → lkj-0.1.47}/README.md +0 -0
- {lkj-0.1.46 → lkj-0.1.47}/lkj/__init__.py +0 -0
- {lkj-0.1.46 → lkj-0.1.47}/lkj/chunking.py +0 -0
- {lkj-0.1.46 → lkj-0.1.47}/lkj/dicts.py +0 -0
- {lkj-0.1.46 → lkj-0.1.47}/lkj/filesys.py +0 -0
- {lkj-0.1.46 → lkj-0.1.47}/lkj/funcs.py +0 -0
- {lkj-0.1.46 → lkj-0.1.47}/lkj/importing.py +0 -0
- {lkj-0.1.46 → lkj-0.1.47}/lkj/iterables.py +0 -0
- {lkj-0.1.46 → lkj-0.1.47}/lkj/loggers.py +0 -0
- {lkj-0.1.46 → lkj-0.1.47}/lkj/misc.py +0 -0
- {lkj-0.1.46 → lkj-0.1.47}/lkj.egg-info/dependency_links.txt +0 -0
- {lkj-0.1.46 → lkj-0.1.47}/lkj.egg-info/not-zip-safe +0 -0
- {lkj-0.1.46 → lkj-0.1.47}/lkj.egg-info/top_level.txt +0 -0
- {lkj-0.1.46 → lkj-0.1.47}/setup.py +0 -0
|
@@ -633,21 +633,59 @@ class FindReplaceTool:
|
|
|
633
633
|
print(highlight)
|
|
634
634
|
print("-" * 40)
|
|
635
635
|
else:
|
|
636
|
+
# In non-line mode, a naive snippet (characters around the match) can
|
|
637
|
+
# include newlines. If we simply print that snippet and then print the
|
|
638
|
+
# highlight line relative to the snippet start, the visual caret
|
|
639
|
+
# markers may appear under a different printed line. To avoid this,
|
|
640
|
+
# find the full line that contains the match and print the context as
|
|
641
|
+
# lines: preceding context lines, the matched line, then the highlight
|
|
642
|
+
# directly under the matched line, then the following context lines.
|
|
636
643
|
snippet_radius = 20
|
|
637
644
|
for idx, m in enumerate(self._matches):
|
|
638
645
|
start, end = m["start"], m["end"]
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
646
|
+
# Find the boundaries of the line containing the match
|
|
647
|
+
line_start = current_text.rfind("\n", 0, start) + 1
|
|
648
|
+
line_end = current_text.find("\n", end)
|
|
649
|
+
if line_end == -1:
|
|
650
|
+
line_end = len(current_text)
|
|
651
|
+
|
|
652
|
+
# Context window (characters) around the matched line
|
|
653
|
+
context_start = max(0, line_start - snippet_radius)
|
|
654
|
+
context_end = min(len(current_text), line_end + snippet_radius)
|
|
655
|
+
context_text = current_text[context_start:context_end]
|
|
656
|
+
|
|
657
|
+
# Split into lines while preserving newlines so output looks natural
|
|
658
|
+
context_lines = context_text.splitlines(True)
|
|
659
|
+
|
|
660
|
+
# Determine which line in context_lines contains the match
|
|
661
|
+
acc = 0
|
|
662
|
+
match_line_idx = 0
|
|
663
|
+
rel_pos_in_context = line_start - context_start
|
|
664
|
+
for i, line in enumerate(context_lines):
|
|
665
|
+
if acc + len(line) > rel_pos_in_context:
|
|
666
|
+
match_line_idx = i
|
|
667
|
+
break
|
|
668
|
+
acc += len(line)
|
|
669
|
+
|
|
645
670
|
print(f"Match {idx} (around line {m['line_number']+1}):")
|
|
646
|
-
print
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
)
|
|
650
|
-
|
|
671
|
+
# Print each context line. For the match line, print a highlight
|
|
672
|
+
# line immediately after it so the caret markers line up under
|
|
673
|
+
# the matched text.
|
|
674
|
+
for i, line in enumerate(context_lines):
|
|
675
|
+
# Print the context line as-is (it may or may not contain a newline)
|
|
676
|
+
print(line, end="")
|
|
677
|
+
if i == match_line_idx:
|
|
678
|
+
# If the printed line did not end with a newline, ensure the
|
|
679
|
+
# caret highlight appears on the next line so it lines up
|
|
680
|
+
# visually beneath the matched characters.
|
|
681
|
+
if not line.endswith("\n"):
|
|
682
|
+
print()
|
|
683
|
+
# position of match within the printed line
|
|
684
|
+
pos_in_line = start - line_start
|
|
685
|
+
highlight = " " * pos_in_line + self.highlight_char * (
|
|
686
|
+
end - start
|
|
687
|
+
)
|
|
688
|
+
print(highlight)
|
|
651
689
|
print("-" * 40)
|
|
652
690
|
|
|
653
691
|
def replace_one(self, match_index: int, replacement: Replacement) -> None:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Tests for lkj"""
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import sys
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
from lkj.strings import FindReplaceTool
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def capture_print(func, *args, **kwargs):
|
|
9
|
+
old_stdout = sys.stdout
|
|
10
|
+
try:
|
|
11
|
+
sys.stdout = io.StringIO()
|
|
12
|
+
func(*args, **kwargs)
|
|
13
|
+
return sys.stdout.getvalue()
|
|
14
|
+
finally:
|
|
15
|
+
sys.stdout = old_stdout
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_find_and_print_matches_highlight_under_match():
|
|
19
|
+
text = "apple banana apple\nsome other line\n"
|
|
20
|
+
tool = FindReplaceTool(text, line_mode=False)
|
|
21
|
+
out = capture_print(tool.find_and_print_matches, r"apple")
|
|
22
|
+
|
|
23
|
+
# We expect for the first match that the matched line appears, then the
|
|
24
|
+
# highlight line directly under it, then following context lines. Ensure the
|
|
25
|
+
# highlight caret appears on its own line immediately after the matched line.
|
|
26
|
+
# Locate the first occurrence of the matched line and the caret line that follows.
|
|
27
|
+
lines = out.splitlines()
|
|
28
|
+
|
|
29
|
+
# Find the index of the line that contains the first printed snippet for match 0
|
|
30
|
+
# It should contain 'apple banana apple'
|
|
31
|
+
match0_idx = None
|
|
32
|
+
for i, line in enumerate(lines):
|
|
33
|
+
if "apple banana apple" in line:
|
|
34
|
+
# Ensure the next non-empty line is the highlight
|
|
35
|
+
match0_idx = i
|
|
36
|
+
break
|
|
37
|
+
|
|
38
|
+
assert match0_idx is not None, "Did not find the matched line in output"
|
|
39
|
+
|
|
40
|
+
# The next line should be the caret highlight (contains at least one '^')
|
|
41
|
+
assert any(
|
|
42
|
+
c == "^" for c in lines[match0_idx + 1]
|
|
43
|
+
), "Highlight not directly under matched line"
|
|
44
|
+
|
|
45
|
+
# For completeness, ensure that the text 'some other line' appears after the caret
|
|
46
|
+
assert "some other line" in "\n".join(
|
|
47
|
+
lines[match0_idx + 2 :]
|
|
48
|
+
), "Following context not printed after highlight"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|