Split an FSTRING_MIDDLE/TSTRING_MIDDLE token containing `{{`/`}}` into multiple unescaped sub-tokens. Returns vec of (type, string, start_line, start_col, end_line, end_col).
(
raw: &str,
token_type: u8,
start_line: usize,
start_col: usize,
)
| 499 | /// into multiple unescaped sub-tokens. |
| 500 | /// Returns vec of (type, string, start_line, start_col, end_line, end_col). |
| 501 | fn split_fstring_middle( |
| 502 | raw: &str, |
| 503 | token_type: u8, |
| 504 | start_line: usize, |
| 505 | start_col: usize, |
| 506 | ) -> Vec<(u8, String, usize, usize, usize, usize)> { |
| 507 | let mut parts = Vec::new(); |
| 508 | let mut current = String::new(); |
| 509 | // Track source position (line, col) — these correspond to the |
| 510 | // original source positions (with {{ and }} still doubled) |
| 511 | let mut cur_line = start_line; |
| 512 | let mut cur_col = start_col; |
| 513 | // Track the start position of the current accumulating part |
| 514 | let mut part_start_line = cur_line; |
| 515 | let mut part_start_col = cur_col; |
| 516 | let mut chars = raw.chars().peekable(); |
| 517 | |
| 518 | // Compute end position of the current accumulated text |
| 519 | let end_pos = |current: &str, start_line: usize, start_col: usize| -> (usize, usize) { |
| 520 | let mut el = start_line; |
| 521 | let mut ec = start_col; |
| 522 | for ch in current.chars() { |
| 523 | if ch == '\n' { |
| 524 | el += 1; |
| 525 | ec = 0; |
| 526 | } else { |
| 527 | ec += ch.len_utf8(); |
| 528 | } |
| 529 | } |
| 530 | (el, ec) |
| 531 | }; |
| 532 | |
| 533 | while let Some(ch) = chars.next() { |
| 534 | if ch == '{' && chars.peek() == Some(&'{') { |
| 535 | chars.next(); |
| 536 | current.push('{'); |
| 537 | cur_col += 2; // skip both {{ in source |
| 538 | } else if ch == '}' && chars.peek() == Some(&'}') { |
| 539 | chars.next(); |
| 540 | // Flush accumulated text before }} |
| 541 | if !current.is_empty() { |
| 542 | let (el, ec) = end_pos(¤t, part_start_line, part_start_col); |
| 543 | parts.push(( |
| 544 | token_type, |
| 545 | core::mem::take(&mut current), |
| 546 | part_start_line, |
| 547 | part_start_col, |
| 548 | el, |
| 549 | ec, |
| 550 | )); |
| 551 | } |
| 552 | // Emit unescaped '}' at source position of }} |
| 553 | parts.push(( |
| 554 | token_type, |
| 555 | "}".to_string(), |
| 556 | cur_line, |
| 557 | cur_col, |
| 558 | cur_line, |