1818from collections .abc import Sequence
1919from math import gcd
2020from typing import NamedTuple
21+ import re
2122
2223from .cst_kit import IdentifierFinder
2324
2425from .line_kit import get_line_indent_count , extract_indentation
2526
27+ relative_indent_prefix = re .compile (r'^\s*@(-?\d+):(.*)' )
28+
29+
2630class IndentationInfo (NamedTuple ):
2731 """
2832 A class to represent and manage indentation information.
@@ -276,9 +280,12 @@ def adjust_line(line: str) -> str:
276280 return new_indent + line .lstrip ()
277281 return adjust_line
278282
279- def apply_relative_indents (self , content : str | Sequence [str ], context_indent_count : int = 0 ) -> list [str ]:
283+ def apply_relative_indents (
284+ self , content : str | Sequence [str ], reference_indent_count : int = 0 ,
285+ treat_unprefixed_line_as_relative : bool = False
286+ ) -> list [str ]:
280287 """
281- Apply relative indentation based on annotations in the content.
288+ Apply relative indentation based on optional annotations in the content.
282289
283290 This method processes the input content, interpreting special annotations
284291 to apply relative indentation. It uses '@' followed by a number to indicate
@@ -287,7 +294,7 @@ def apply_relative_indents(self, content: str | Sequence[str], context_indent_co
287294 Args:
288295 content (str | Sequence[str]): The content to process. Can be a string
289296 or a sequence of strings.
290- context_indent_count (int, optional): The base indentation count of the
297+ reference_indent_count (int, optional): The base indentation count of the
291298 context. Defaults to 0.
292299
293300 Returns:
@@ -312,23 +319,29 @@ def apply_relative_indents(self, content: str | Sequence[str], context_indent_co
312319 [' def example():', ' print('Hello')', ' if True:', ' print('World')']
313320 """
314321 # TODO Always send str?
315- lines = [l .lstrip () for l in content .splitlines () if l .strip ()] if isinstance (content , str ) else content
316-
317- context_indent_level = self .char_count_to_level (context_indent_count )
322+ lines = [l for l in content .strip ('\n ' ).splitlines ()] if isinstance (content , str ) else content
323+ reference_indent_level = self .char_count_to_level (reference_indent_count )
318324 for i in range (len (lines )):
319325 line = lines [i ]
320- parts = line .split (':' , 1 )
321- if len (parts ) == 2 and parts [0 ].startswith ('@' ):
322- relative_indent_level = int (parts [0 ][1 :])
323- absolute_indent_level = context_indent_level + relative_indent_level
324- assert absolute_indent_level >= 0 , (
325- f"Final indentation for line `{ line .strip ()} ` cannot be negative "
326- f"({ absolute_indent_level } )"
327- )
328- lines [i ] = self .level_to_chars (absolute_indent_level ) + parts [1 ].lstrip ()
329- else :
330- absolute_indent_level = context_indent_level
331- lines [i ] = self .level_to_chars (absolute_indent_level ) + line .lstrip ()
326+ match relative_indent_prefix .match (line ):
327+ case re .Match () as m :
328+ relative_indent_level , line = m .groups ()
329+ relative_indent_level = int (relative_indent_level )
330+ line = line .lstrip ()
331+ absolute_indent_level = reference_indent_level + relative_indent_level
332+ case _:
333+ if treat_unprefixed_line_as_relative :
334+ line = line .lstrip ()
335+ relative_indent_level = self .char_count_to_level (get_line_indent_count (line ))
336+ absolute_indent_level = reference_indent_level + relative_indent_level
337+ else :
338+ absolute_indent_level = 0
339+
340+ assert absolute_indent_level >= 0 , (
341+ f"Final indent level for line `{ line .strip ()} ` cannot be negative "
342+ f"({ absolute_indent_level } )"
343+ )
344+ lines [i ] = self .level_to_chars (absolute_indent_level ) + line
332345
333346 return lines
334347
0 commit comments