Skip to content

Commit db7e628

Browse files
yarikopticclaude
andcommitted
Fix get_paths losing class/def context across blank and continuation lines
Two bugs in the indentation path stack: 1. Blank lines computed indent as 0, wiping the entire stack — now skipped so enclosing class/function context is preserved. 2. Closing-bracket continuation lines (e.g. `) -> str:` from a multi-line def signature) at the same indent as the opening statement replaced it in the stack — now treated as continuations that only pop deeper entries, not same-indent ones. Co-Authored-By: Claude Code 2.1.81 / Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6fb141d commit db7e628

3 files changed

Lines changed: 88 additions & 3 deletions

File tree

bin/show-paths

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,14 +194,30 @@ def get_paths(lines):
194194

195195
for i, line in enumerate(lines):
196196
stripped = line.lstrip()
197+
198+
# Skip blank lines — they have indent 0 which would
199+
# incorrectly pop the entire path stack
200+
if not stripped:
201+
paths.append((i, tuple(path_stack)))
202+
continue
203+
197204
indent = len(line) - len(stripped)
198205

206+
# Lines starting with closing brackets/parens or commas are
207+
# continuations of a previous statement — they should not
208+
# replace the current entry at the same indent level.
209+
is_continuation = stripped[0] in ")]},"
210+
199211
# Adjust the stack based on indentation
200-
while path_stack and path_stack[-1][1] >= indent:
201-
path_stack.pop()
212+
if is_continuation:
213+
while path_stack and path_stack[-1][1] > indent:
214+
path_stack.pop()
215+
else:
216+
while path_stack and path_stack[-1][1] >= indent:
217+
path_stack.pop()
202218

203219
# Extract the "key" (first non-space word before :, {, etc.)
204-
if stripped:
220+
if not is_continuation:
205221
key = stripped.split()[0].rstrip(":{")
206222
if key:
207223
path_stack.append((key, indent, i))

tests/data/sample_multiline.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class Storage:
2+
"""A storage class."""
3+
4+
def upload(
5+
self,
6+
name,
7+
content_type=None,
8+
) -> str:
9+
if name:
10+
return self.client.call(
11+
Method='put_object',
12+
)

tests/test_show_paths.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,3 +910,60 @@ def test_show_paths_github_markdown_filename_name_linked():
910910
assert (
911911
len(header_lines) > 0
912912
), f"Expected linked filename header for sample.py: {output}"
913+
914+
915+
@pytest.mark.ai_generated
916+
def test_show_paths_blank_lines_preserve_context():
917+
"""Test that blank lines do not destroy the indentation path stack.
918+
919+
Blank lines have zero indentation which previously caused the path
920+
stack to be wiped, losing class/function context.
921+
"""
922+
result = run_show_paths(
923+
str(DATA_DIR / "sample_multiline.py"),
924+
"-e",
925+
"put_object",
926+
"-f",
927+
"full-lines",
928+
"--color",
929+
"off",
930+
)
931+
assert result.returncode == 0
932+
output = result.stdout
933+
lines = output.strip().split("\n")
934+
texts = [line.split(maxsplit=1)[1] if " " in line else "" for line in lines]
935+
# The class definition must appear in the context — blank line inside
936+
# the docstring must not wipe it from the stack.
937+
assert any("class Storage" in t for t in texts), (
938+
f"class definition missing from context:\n{output}"
939+
)
940+
941+
942+
@pytest.mark.ai_generated
943+
def test_show_paths_continuation_lines_preserve_context():
944+
"""Test that closing-paren continuation lines do not replace the def.
945+
946+
A multi-line function signature like:
947+
def upload(
948+
self,
949+
) -> str:
950+
has ') -> str:' at the same indent as 'def upload('. The closing
951+
paren line must not replace the def in the path stack.
952+
"""
953+
result = run_show_paths(
954+
str(DATA_DIR / "sample_multiline.py"),
955+
"-e",
956+
"put_object",
957+
"-f",
958+
"full-lines",
959+
"--color",
960+
"off",
961+
)
962+
assert result.returncode == 0
963+
output = result.stdout
964+
lines = output.strip().split("\n")
965+
texts = [line.split(maxsplit=1)[1] if " " in line else "" for line in lines]
966+
# The def line must appear, not ') -> str:'
967+
assert any("def upload" in t for t in texts), (
968+
f"def line missing from context (replaced by continuation?):\n{output}"
969+
)

0 commit comments

Comments
 (0)