Merges one string group where the first string in the group is `LL[string_idx]`. Returns: A tuple of `(num_of_strings, leaf)` where `num_of_strings` is the number of strings merged and `leaf` is the newly merged string to be replaced in t
(
self, LL: list[Leaf], string_idx: int, is_valid_index: Callable[[int], bool]
)
| 599 | return Ok(new_line) |
| 600 | |
| 601 | def _merge_one_string_group( |
| 602 | self, LL: list[Leaf], string_idx: int, is_valid_index: Callable[[int], bool] |
| 603 | ) -> tuple[int, Leaf]: |
| 604 | """ |
| 605 | Merges one string group where the first string in the group is |
| 606 | `LL[string_idx]`. |
| 607 | |
| 608 | Returns: |
| 609 | A tuple of `(num_of_strings, leaf)` where `num_of_strings` is the |
| 610 | number of strings merged and `leaf` is the newly merged string |
| 611 | to be replaced in the new line. |
| 612 | """ |
| 613 | # If the string group is wrapped inside an Atom node, we must make sure |
| 614 | # to later replace that Atom with our new (merged) string leaf. |
| 615 | atom_node = LL[string_idx].parent |
| 616 | |
| 617 | # We will place BREAK_MARK in between every two substrings that we |
| 618 | # merge. We will then later go through our final result and use the |
| 619 | # various instances of BREAK_MARK we find to add the right values to |
| 620 | # the custom split map. |
| 621 | BREAK_MARK = "@@@@@ BLACK BREAKPOINT MARKER @@@@@" |
| 622 | |
| 623 | QUOTE = LL[string_idx].value[-1] |
| 624 | |
| 625 | def make_naked(string: str, string_prefix: str) -> str: |
| 626 | """Strip @string (i.e. make it a "naked" string) |
| 627 | |
| 628 | Pre-conditions: |
| 629 | * assert_is_leaf_string(@string) |
| 630 | |
| 631 | Returns: |
| 632 | A string that is identical to @string except that |
| 633 | @string_prefix has been stripped, the surrounding QUOTE |
| 634 | characters have been removed, and any remaining QUOTE |
| 635 | characters have been escaped. |
| 636 | """ |
| 637 | assert_is_leaf_string(string) |
| 638 | if "f" in string_prefix: |
| 639 | f_expressions = [ |
| 640 | string[span[0] + 1 : span[1] - 1] # +-1 to get rid of curly braces |
| 641 | for span in iter_fexpr_spans(string) |
| 642 | ] |
| 643 | debug_expressions_contain_visible_quotes = any( |
| 644 | re.search(r".*[\'\"].*(?<![!:=])={1}(?!=)(?![^\s:])", expression) |
| 645 | for expression in f_expressions |
| 646 | ) |
| 647 | if not debug_expressions_contain_visible_quotes: |
| 648 | # We don't want to toggle visible quotes in debug f-strings, as |
| 649 | # that would modify the AST |
| 650 | string = _toggle_fexpr_quotes(string, QUOTE) |
| 651 | # After quotes toggling, quotes in expressions won't be escaped |
| 652 | # because quotes can't be reused in f-strings. So we can simply |
| 653 | # let the escaping logic below run without knowing f-string |
| 654 | # expressions. |
| 655 | |
| 656 | RE_EVEN_BACKSLASHES = r"(?:(?<!\\)(?:\\\\)*)" |
| 657 | naked_string = string[len(string_prefix) + 1 : -1] |
| 658 | naked_string = re.sub( |
no test coverage detected