1use std::{iter::FusedIterator, ops::Deref};
2
3use super::{Token, TokenKind};
4use ruff_python_trivia::{CommentRanges, ParenthesizedExpressions, TriviaRanges};
5use ruff_text_size::{Ranged as _, TextRange, TextSize};
6use rustc_hash::FxHashSet;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))]
11pub struct Tokens {
12 raw: Vec<Token>,
13}
14
15impl Tokens {
16 pub fn new(tokens: Vec<Token>) -> Tokens {
17 Tokens { raw: tokens }
18 }
19
20 pub fn iter_with_context(&self) -> TokenIterWithContext<'_> {
22 TokenIterWithContext::new(&self.raw)
23 }
24
25 fn binary_search_by_start(&self, offset: TextSize) -> Result<usize, usize> {
31 let partition_point = self.partition_point(|token| token.start() < offset);
32
33 let after = &self[partition_point..];
34
35 if after.first().is_some_and(|first| first.start() == offset) {
36 Ok(partition_point)
37 } else {
38 Err(partition_point)
39 }
40 }
41
42 pub fn in_range(&self, range: TextRange) -> &[Token] {
83 let tokens_after_start = self.after(range.start());
84
85 Self::before_impl(tokens_after_start, range.end())
86 }
87
88 pub fn at_offset(&self, offset: TextSize) -> TokenAt {
93 match self.binary_search_by_start(offset) {
94 Ok(index) => {
100 let token = self[index];
101 if let Some(previous) = index.checked_sub(1).map(|idx| self[idx]) {
104 if previous.end() == offset {
105 return TokenAt::Between(previous, token);
106 }
107 }
108
109 TokenAt::Single(token)
110 }
111
112 Err(index) => {
123 if let Some(previous) = index.checked_sub(1).map(|idx| self[idx]) {
124 if previous.range().contains_inclusive(offset) {
125 return TokenAt::Single(previous);
126 }
127 }
128
129 TokenAt::None
130 }
131 }
132 }
133
134 pub fn before(&self, offset: TextSize) -> &[Token] {
145 Self::before_impl(&self.raw, offset)
146 }
147
148 fn before_impl(tokens: &[Token], offset: TextSize) -> &[Token] {
149 let partition_point = tokens.partition_point(|token| token.start() < offset);
150 let before = &tokens[..partition_point];
151
152 if let Some(last) = before.last() {
153 assert!(
157 offset >= last.end(),
158 "Offset {offset:?} is inside token `{last:?}`",
159 );
160 }
161 before
162 }
163
164 pub fn after(&self, offset: TextSize) -> &[Token] {
175 let partition_point = self.partition_point(|token| token.end() <= offset);
176 let after = &self[partition_point..];
177
178 if let Some(first) = after.first() {
179 assert!(
182 offset <= first.start(),
183 "Offset {offset:?} is inside token `{first:?}`",
184 );
185 }
186
187 after
188 }
189
190 pub fn split_at(&self, offset: TextSize) -> (&[Token], &[Token]) {
205 let partition_point = self.partition_point(|token| token.start() < offset);
206 let (before, after) = &self.raw.split_at(partition_point);
207
208 if let Some(last) = before.last() {
209 assert!(
210 offset >= last.end(),
211 "Offset {offset:?} is inside token `{last:?}`"
212 );
213 }
214 (before, after)
215 }
216
217 pub fn token_range(&self, offset: TextSize) -> TextRange {
222 match self.at_offset(offset) {
223 TokenAt::Single(token) => token.range(),
224 TokenAt::None | TokenAt::Between(..) => TextRange::empty(offset),
225 }
226 }
227}
228
229impl IntoIterator for Tokens {
230 type Item = Token;
231 type IntoIter = std::vec::IntoIter<Token>;
232
233 fn into_iter(self) -> Self::IntoIter {
234 self.raw.into_iter()
235 }
236}
237
238impl<'a> IntoIterator for &'a Tokens {
239 type Item = &'a Token;
240 type IntoIter = std::slice::Iter<'a, Token>;
241
242 fn into_iter(self) -> Self::IntoIter {
243 self.iter()
244 }
245}
246
247impl Deref for Tokens {
248 type Target = [Token];
249
250 fn deref(&self) -> &Self::Target {
251 &self.raw
252 }
253}
254
255#[derive(Debug, Clone)]
257pub enum TokenAt {
258 None,
260
261 Single(Token),
263
264 Between(Token, Token),
267}
268
269impl Iterator for TokenAt {
270 type Item = Token;
271
272 fn next(&mut self) -> Option<Self::Item> {
273 match *self {
274 TokenAt::None => None,
275 TokenAt::Single(token) => {
276 *self = TokenAt::None;
277 Some(token)
278 }
279 TokenAt::Between(first, second) => {
280 *self = TokenAt::Single(second);
281 Some(first)
282 }
283 }
284 }
285}
286
287impl FusedIterator for TokenAt {}
288
289impl From<&Tokens> for CommentRanges {
290 fn from(tokens: &Tokens) -> Self {
291 let mut ranges = vec![];
292
293 for token in tokens {
294 if token.kind() == TokenKind::Comment {
295 ranges.push(token.range());
296 }
297 }
298
299 CommentRanges::new(ranges)
300 }
301}
302
303impl From<&Tokens> for TriviaRanges {
304 fn from(tokens: &Tokens) -> Self {
305 let mut comments = vec![];
306 let mut parenthesized = FxHashSet::default();
307 let mut stack = Vec::<Option<TextSize>>::new();
308 let mut previous_end = None;
309
310 for token in tokens {
311 if token.kind() == TokenKind::Comment {
312 comments.push(token.range());
313 }
314
315 if token.kind().is_trivia() {
316 continue;
317 }
318
319 match token.kind() {
320 TokenKind::Lpar => {
321 if let Some(start) = stack.last_mut() {
322 start.get_or_insert(token.start());
323 }
324 stack.push(None);
325 }
326 TokenKind::Rpar => {
327 if let (Some(Some(start)), Some(end)) = (stack.pop(), previous_end) {
328 parenthesized.insert(TextRange::new(start, end));
329 }
330 }
331 _ => {
332 if let Some(start) = stack.last_mut() {
333 start.get_or_insert(token.start());
334 }
335 }
336 }
337
338 previous_end = Some(token.end());
339 }
340
341 TriviaRanges::new(
342 CommentRanges::new(comments),
343 ParenthesizedExpressions::new(parenthesized),
344 )
345 }
346}
347
348#[derive(Debug, Clone)]
355pub struct TokenIterWithContext<'a> {
356 inner: std::slice::Iter<'a, Token>,
357 nesting: u32,
358}
359
360impl<'a> TokenIterWithContext<'a> {
361 fn new(tokens: &'a [Token]) -> TokenIterWithContext<'a> {
362 TokenIterWithContext {
363 inner: tokens.iter(),
364 nesting: 0,
365 }
366 }
367
368 pub const fn nesting(&self) -> u32 {
370 self.nesting
371 }
372
373 pub const fn in_parenthesized_context(&self) -> bool {
375 self.nesting > 0
376 }
377
378 pub fn peek(&self) -> Option<&'a Token> {
380 self.clone().next()
381 }
382}
383
384impl<'a> Iterator for TokenIterWithContext<'a> {
385 type Item = &'a Token;
386
387 fn next(&mut self) -> Option<Self::Item> {
388 let token = self.inner.next()?;
389
390 match token.kind() {
391 TokenKind::Lpar | TokenKind::Lbrace | TokenKind::Lsqb => self.nesting += 1,
392 TokenKind::Rpar | TokenKind::Rbrace | TokenKind::Rsqb => {
393 self.nesting = self.nesting.saturating_sub(1);
394 }
395 TokenKind::Newline if self.nesting > 0 => {
399 self.nesting = 0;
400 }
401 _ => {}
402 }
403
404 Some(token)
405 }
406}
407
408impl FusedIterator for TokenIterWithContext<'_> {}
409
410#[cfg(test)]
411mod tests {
412 use std::ops::Range;
413
414 use ruff_text_size::TextSize;
415
416 use crate::token::{Token, TokenFlags, TokenKind};
417
418 use super::*;
419
420 const TEST_CASE_WITH_GAP: [(TokenKind, Range<u32>); 10] = [
424 (TokenKind::Def, 0..3),
425 (TokenKind::Name, 4..7),
426 (TokenKind::Lpar, 7..8),
427 (TokenKind::Rpar, 8..9),
428 (TokenKind::Colon, 9..10),
429 (TokenKind::Newline, 10..11),
430 (TokenKind::Comment, 15..24),
432 (TokenKind::NonLogicalNewline, 24..25),
433 (TokenKind::Indent, 25..29),
434 (TokenKind::Pass, 29..33),
435 ];
437
438 fn new_tokens(tokens: impl Iterator<Item = (TokenKind, Range<u32>)>) -> Tokens {
440 Tokens::new(
441 tokens
442 .map(|(kind, range)| {
443 Token::new(
444 kind,
445 TextRange::new(TextSize::new(range.start), TextSize::new(range.end)),
446 TokenFlags::empty(),
447 )
448 })
449 .collect(),
450 )
451 }
452
453 #[test]
454 fn tokens_after_offset_at_token_start() {
455 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
456 let after = tokens.after(TextSize::new(8));
457 assert_eq!(after.len(), 7);
458 assert_eq!(after.first().unwrap().kind(), TokenKind::Rpar);
459 }
460
461 #[test]
462 fn tokens_after_offset_at_token_end() {
463 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
464 let after = tokens.after(TextSize::new(11));
465 assert_eq!(after.len(), 4);
466 assert_eq!(after.first().unwrap().kind(), TokenKind::Comment);
467 }
468
469 #[test]
470 fn tokens_after_offset_between_tokens() {
471 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
472 let after = tokens.after(TextSize::new(13));
473 assert_eq!(after.len(), 4);
474 assert_eq!(after.first().unwrap().kind(), TokenKind::Comment);
475 }
476
477 #[test]
478 fn tokens_after_offset_at_last_token_end() {
479 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
480 let after = tokens.after(TextSize::new(33));
481 assert_eq!(after.len(), 0);
482 }
483
484 #[test]
485 #[should_panic(expected = "Offset 5 is inside token `Name 4..7`")]
486 fn tokens_after_offset_inside_token() {
487 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
488 tokens.after(TextSize::new(5));
489 }
490
491 #[test]
492 fn tokens_before_offset_at_first_token_start() {
493 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
494 let before = tokens.before(TextSize::new(0));
495 assert_eq!(before.len(), 0);
496 }
497
498 #[test]
499 fn tokens_before_offset_after_first_token_gap() {
500 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
501 let before = tokens.before(TextSize::new(3));
502 assert_eq!(before.len(), 1);
503 assert_eq!(before.last().unwrap().kind(), TokenKind::Def);
504 }
505
506 #[test]
507 fn tokens_before_offset_at_second_token_start() {
508 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
509 let before = tokens.before(TextSize::new(4));
510 assert_eq!(before.len(), 1);
511 assert_eq!(before.last().unwrap().kind(), TokenKind::Def);
512 }
513
514 #[test]
515 fn tokens_before_offset_at_token_start() {
516 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
517 let before = tokens.before(TextSize::new(8));
518 assert_eq!(before.len(), 3);
519 assert_eq!(before.last().unwrap().kind(), TokenKind::Lpar);
520 }
521
522 #[test]
523 fn tokens_before_offset_at_token_end() {
524 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
525 let before = tokens.before(TextSize::new(11));
526 assert_eq!(before.len(), 6);
527 assert_eq!(before.last().unwrap().kind(), TokenKind::Newline);
528 }
529
530 #[test]
531 fn tokens_before_offset_between_tokens() {
532 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
533 let before = tokens.before(TextSize::new(13));
534 assert_eq!(before.len(), 6);
535 assert_eq!(before.last().unwrap().kind(), TokenKind::Newline);
536 }
537
538 #[test]
539 fn tokens_before_offset_at_last_token_end() {
540 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
541 let before = tokens.before(TextSize::new(33));
542 assert_eq!(before.len(), 10);
543 assert_eq!(before.last().unwrap().kind(), TokenKind::Pass);
544 }
545
546 #[test]
547 #[should_panic(expected = "Offset 5 is inside token `Name 4..7`")]
548 fn tokens_before_offset_inside_token() {
549 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
550 tokens.before(TextSize::new(5));
551 }
552
553 #[test]
554 fn tokens_in_range_at_token_offset() {
555 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
556 let in_range = tokens.in_range(TextRange::new(4.into(), 10.into()));
557 assert_eq!(in_range.len(), 4);
558 assert_eq!(in_range.first().unwrap().kind(), TokenKind::Name);
559 assert_eq!(in_range.last().unwrap().kind(), TokenKind::Colon);
560 }
561
562 #[test]
563 fn tokens_in_range_start_offset_at_token_end() {
564 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
565 let in_range = tokens.in_range(TextRange::new(11.into(), 29.into()));
566 assert_eq!(in_range.len(), 3);
567 assert_eq!(in_range.first().unwrap().kind(), TokenKind::Comment);
568 assert_eq!(in_range.last().unwrap().kind(), TokenKind::Indent);
569 }
570
571 #[test]
572 fn tokens_in_range_end_offset_at_token_start() {
573 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
574 let in_range = tokens.in_range(TextRange::new(8.into(), 15.into()));
575 assert_eq!(in_range.len(), 3);
576 assert_eq!(in_range.first().unwrap().kind(), TokenKind::Rpar);
577 assert_eq!(in_range.last().unwrap().kind(), TokenKind::Newline);
578 }
579
580 #[test]
581 fn tokens_in_range_start_offset_between_tokens() {
582 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
583 let in_range = tokens.in_range(TextRange::new(13.into(), 29.into()));
584 assert_eq!(in_range.len(), 3);
585 assert_eq!(in_range.first().unwrap().kind(), TokenKind::Comment);
586 assert_eq!(in_range.last().unwrap().kind(), TokenKind::Indent);
587 }
588
589 #[test]
590 fn tokens_in_range_end_offset_between_tokens() {
591 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
592 let in_range = tokens.in_range(TextRange::new(9.into(), 13.into()));
593 assert_eq!(in_range.len(), 2);
594 assert_eq!(in_range.first().unwrap().kind(), TokenKind::Colon);
595 assert_eq!(in_range.last().unwrap().kind(), TokenKind::Newline);
596 }
597
598 #[test]
599 #[should_panic(expected = "Offset 5 is inside token `Name 4..7`")]
600 fn tokens_in_range_start_offset_inside_token() {
601 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
602 tokens.in_range(TextRange::new(5.into(), 10.into()));
603 }
604
605 #[test]
606 #[should_panic(expected = "Offset 6 is inside token `Name 4..7`")]
607 fn tokens_in_range_end_offset_inside_token() {
608 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
609 tokens.in_range(TextRange::new(0.into(), 6.into()));
610 }
611
612 #[test]
613 fn tokens_split_at_first_token_start() {
614 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
615 let (before, after) = tokens.split_at(TextSize::new(0));
616 assert_eq!(before.len(), 0);
617 assert_eq!(after.len(), 10);
618 }
619
620 #[test]
621 fn tokens_split_at_last_token_end() {
622 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
623 let (before, after) = tokens.split_at(TextSize::new(33));
624 assert_eq!(before.len(), 10);
625 assert_eq!(after.len(), 0);
626 }
627
628 #[test]
629 fn tokens_split_at_inside_gap() {
630 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
631 let (before, after) = tokens.split_at(TextSize::new(13));
632 assert_eq!(before.len(), 6);
633 assert_eq!(after.len(), 4);
634 }
635
636 #[test]
637 #[should_panic(expected = "Offset 18 is inside token `Comment 15..24`")]
638 fn tokens_split_at_inside_token() {
639 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
640 tokens.split_at(TextSize::new(18));
641 }
642
643 #[test]
644 fn tokens_split_at_matches_before_and_after() {
645 let offset = TextSize::new(15);
646 let tokens = new_tokens(TEST_CASE_WITH_GAP.into_iter());
647 let (before, after) = tokens.split_at(offset);
648 assert_eq!(before, tokens.before(offset));
649 assert_eq!(after, tokens.after(offset));
650 }
651
652 #[test]
653 #[should_panic(expected = "Contents of after slice different when offset at dedent")]
654 fn tokens_split_at_matches_before_and_after_zero_length() {
655 let offset = TextSize::new(13);
656 let tokens = new_tokens(
657 [
658 (TokenKind::If, 0..2),
659 (TokenKind::Name, 3..4),
660 (TokenKind::Colon, 4..5),
661 (TokenKind::Newline, 5..6),
662 (TokenKind::Indent, 6..7),
663 (TokenKind::Pass, 7..11),
664 (TokenKind::Newline, 11..12),
665 (TokenKind::NonLogicalNewline, 12..13),
666 (TokenKind::Dedent, 13..13),
667 (TokenKind::Name, 13..14),
668 (TokenKind::Newline, 14..14),
669 ]
670 .into_iter(),
671 );
672 let (before, after) = tokens.split_at(offset);
673 assert_eq!(before, tokens.before(offset));
674 assert!(
675 after == tokens.after(offset),
676 "Contents of after slice different when offset at dedent"
677 );
678 }
679}