1use crate::nodes::{LastStatement, ReturnStatement, Statement, Token, Trivia, TriviaKind};
2
3#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct BlockTokens {
14 pub semicolons: Vec<Option<Token>>,
15 pub last_semicolon: Option<Token>,
16 pub final_token: Option<Token>,
17}
18
19impl BlockTokens {
20 super::impl_token_fns!(
21 iter = [last_semicolon, final_token]
22 iter_flatten = [semicolons]
23 );
24}
25
26#[derive(Clone, Debug, PartialEq, Eq)]
28pub struct Block {
29 statements: Vec<Statement>,
30 last_statement: Option<LastStatement>,
31 tokens: Option<Box<BlockTokens>>,
32}
33
34impl Block {
35 pub fn new(statements: Vec<Statement>, last_statement: Option<LastStatement>) -> Self {
37 Self {
38 statements,
39 last_statement,
40 tokens: None,
41 }
42 }
43
44 pub fn with_tokens(mut self, tokens: BlockTokens) -> Self {
46 self.tokens = Some(tokens.into());
47 self
48 }
49
50 #[inline]
52 pub fn set_tokens(&mut self, tokens: BlockTokens) {
53 self.tokens = Some(tokens.into());
54 }
55
56 #[inline]
58 pub fn get_tokens(&self) -> Option<&BlockTokens> {
59 self.tokens.as_ref().map(|tokens| tokens.as_ref())
60 }
61
62 #[inline]
64 pub fn mutate_tokens(&mut self) -> Option<&mut BlockTokens> {
65 self.tokens.as_mut().map(|tokens| tokens.as_mut())
66 }
67
68 pub fn push_statement<T: Into<Statement>>(&mut self, statement: T) {
72 if let Some(tokens) = &mut self.tokens {
73 if self.statements.len() == tokens.semicolons.len() {
74 tokens.semicolons.push(None);
75 }
76 }
77 self.statements.push(statement.into());
78 }
79
80 pub fn remove_statement(&mut self, index: usize) {
84 let statements_len = self.statements.len();
85 if index < statements_len {
86 let mut removed = self.statements.remove(index);
87
88 fn filter_trivia(trivia: Trivia) -> Option<Trivia> {
89 if trivia.kind() == TriviaKind::Whitespace {
90 None
91 } else {
92 Some(trivia)
93 }
94 }
95
96 let mut trivia: Vec<_> = removed
97 .mutate_first_token()
98 .drain_leading_trivia()
99 .filter_map(filter_trivia)
100 .collect();
101
102 let mut drain_trailing_token = true;
103
104 if let Some(tokens) = &mut self.tokens {
105 if tokens.semicolons.len() == statements_len {
106 let removed_semicolon = tokens.semicolons.remove(index);
107
108 if let Some(mut semicolon) = removed_semicolon {
109 trivia.extend(semicolon.drain_trailing_trivia().filter_map(filter_trivia));
110 drain_trailing_token = false;
111 }
112 }
113 }
114
115 if drain_trailing_token {
116 trivia.extend(
117 removed
118 .mutate_last_token()
119 .drain_trailing_trivia()
120 .filter_map(filter_trivia),
121 );
122 }
123
124 if !trivia.is_empty() {
125 let next_statement = self.statements.get_mut(index);
126
127 let token = if let Some(next_statement) = next_statement {
128 next_statement.mutate_first_token()
129 } else if let Some(last) = self.last_statement.as_mut() {
130 last.mutate_first_token()
131 } else {
132 self.set_default_tokens();
133
134 self.tokens.as_mut().unwrap().final_token.as_mut().unwrap()
135 };
136
137 let line_numbers: Vec<_> = trivia.iter().map(|t| t.get_line_number()).collect();
138
139 let mut previous = None;
140 let mut offset = 0;
141
142 for (index, (trivia, line_number)) in
143 trivia.into_iter().zip(line_numbers).enumerate()
144 {
145 if let (Some(previous), Some(next_line)) = (previous, line_number) {
146 let gap = next_line.saturating_sub(previous);
147 if gap != 0 {
148 token.insert_leading_trivia(
149 index + offset,
150 TriviaKind::Whitespace.with_content("\n".repeat(gap)),
151 );
152 offset += gap;
153 }
154 }
155
156 token.insert_leading_trivia(index + offset, trivia.clone());
157
158 if line_number.is_some() {
159 previous = line_number;
160 }
161 }
162 }
163 }
164 }
165
166 pub fn with_statement<T: Into<Statement>>(mut self, statement: T) -> Self {
168 self.statements.push(statement.into());
169 self
170 }
171
172 pub fn insert_statement(&mut self, index: usize, statement: impl Into<Statement>) {
177 if index > self.statements.len() {
178 self.push_statement(statement.into());
179 } else {
180 self.statements.insert(index, statement.into());
181
182 if let Some(tokens) = &mut self.tokens {
183 if index <= tokens.semicolons.len() {
184 tokens.semicolons.insert(index, None);
185 }
186 }
187 }
188 }
189
190 #[inline]
192 pub fn set_last_statement(&mut self, last_statement: impl Into<LastStatement>) {
193 self.last_statement = Some(last_statement.into());
194 }
195
196 pub fn with_last_statement(mut self, last_statement: impl Into<LastStatement>) -> Self {
198 self.last_statement = Some(last_statement.into());
199 self
200 }
201
202 #[inline]
204 pub fn is_empty(&self) -> bool {
205 self.last_statement.is_none() && self.statements.is_empty()
206 }
207
208 #[inline]
210 pub fn statements_len(&self) -> usize {
211 self.statements.len()
212 }
213
214 #[inline]
216 pub fn iter_statements(&self) -> impl Iterator<Item = &Statement> {
217 self.statements.iter()
218 }
219
220 #[inline]
222 pub fn reverse_iter_statements(&self) -> impl Iterator<Item = &Statement> {
223 self.statements.iter().rev()
224 }
225
226 #[inline]
228 pub fn get_last_statement(&self) -> Option<&LastStatement> {
229 self.last_statement.as_ref()
230 }
231
232 pub fn filter_statements<F>(&mut self, mut f: F)
237 where
238 F: FnMut(&Statement) -> bool,
239 {
240 let mut i = 0;
241
242 while i != self.statements.len() {
243 if f(&self.statements[i]) {
244 i += 1;
245 } else {
246 self.remove_statement(i);
247 }
248 }
249 }
250
251 pub fn filter_mut_statements<F>(&mut self, mut f: F)
256 where
257 F: FnMut(&mut Statement) -> bool,
258 {
259 let mut i = 0;
260
261 while i != self.statements.len() {
262 if f(&mut self.statements[i]) {
263 i += 1;
264 } else {
265 self.remove_statement(i);
266 }
267 }
268 }
269
270 pub fn truncate(&mut self, length: usize) {
274 self.statements.truncate(length);
275 if let Some(tokens) = &mut self.tokens {
276 tokens.semicolons.truncate(length);
277 }
278 }
279
280 #[inline]
282 pub fn iter_mut_statements(&mut self) -> impl Iterator<Item = &mut Statement> {
283 self.statements.iter_mut()
284 }
285
286 #[inline]
288 pub fn first_statement(&self) -> Option<&Statement> {
289 self.statements.first()
290 }
291
292 #[inline]
294 pub fn first_mut_statement(&mut self) -> Option<&mut Statement> {
295 self.statements.first_mut()
296 }
297
298 pub fn take_statements(&mut self) -> Vec<Statement> {
302 if let Some(tokens) = &mut self.tokens {
303 tokens.semicolons.clear();
304 }
305 self.statements.drain(..).collect()
306 }
307
308 pub fn take_last_statement(&mut self) -> Option<LastStatement> {
312 if let Some(tokens) = &mut self.tokens {
313 tokens.last_semicolon.take();
314 }
315 self.last_statement.take()
316 }
317
318 pub fn set_statements(&mut self, statements: Vec<Statement>) {
322 self.statements = statements;
323
324 if let Some(tokens) = &mut self.tokens {
325 tokens.semicolons.clear();
326 }
327 }
328
329 #[inline]
331 pub fn mutate_last_statement(&mut self) -> Option<&mut LastStatement> {
332 self.last_statement.as_mut()
333 }
334
335 #[inline]
337 pub fn replace_last_statement<S: Into<LastStatement>>(
338 &mut self,
339 statement: S,
340 ) -> Option<LastStatement> {
341 self.last_statement.replace(statement.into())
342 }
343
344 pub fn clear(&mut self) {
348 self.statements.clear();
349 self.last_statement.take();
350
351 if let Some(tokens) = &mut self.tokens {
352 tokens.semicolons.clear();
353 tokens.last_semicolon = None;
354 }
355 }
356
357 pub fn mutate_first_token(&mut self) -> &mut Token {
360 if self.is_empty() {
361 self.set_default_tokens();
362 return self.tokens.as_mut().unwrap().final_token.as_mut().unwrap();
363 }
364
365 if !self.statements.is_empty() {
366 return self
367 .first_mut_statement()
368 .expect("first statement should exist")
369 .mutate_first_token();
370 }
371
372 match self
373 .mutate_last_statement()
374 .expect("non-empty block should have a last statement")
375 {
376 LastStatement::Break(token) => {
377 if token.is_none() {
378 *token = Some(Token::from_content("break"));
379 }
380 token.as_mut().unwrap()
381 }
382 LastStatement::Continue(token) => {
383 if token.is_none() {
384 *token = Some(Token::from_content("continue"));
385 }
386 token.as_mut().unwrap()
387 }
388 LastStatement::Return(return_stmt) => return_stmt.mutate_first_token(),
389 }
390 }
391
392 pub fn mutate_last_token(&mut self) -> &mut Token {
395 if self.is_empty() {
396 self.set_default_tokens();
397 return self.tokens.as_mut().unwrap().final_token.as_mut().unwrap();
398 }
399
400 if let Some(last_stmt) = self.last_statement.as_mut() {
401 return last_stmt.mutate_last_token();
402 }
403
404 self.statements.last_mut().unwrap().mutate_last_token()
405 }
406
407 fn set_default_tokens(&mut self) {
408 if self.get_tokens().is_none() {
409 self.set_tokens(BlockTokens {
410 semicolons: Vec::new(),
411 last_semicolon: None,
412 final_token: Some(Token::from_content("")),
413 });
414 } else {
415 let tokens = self.tokens.as_mut().unwrap();
416 if tokens.final_token.is_none() {
417 tokens.final_token = Some(Token::from_content(""));
418 }
419 }
420 }
421
422 super::impl_token_fns!(iter = [tokens]);
423}
424
425impl Default for Block {
426 fn default() -> Self {
427 Self::new(Vec::new(), None)
428 }
429}
430
431impl<IntoStatement: Into<Statement>> From<IntoStatement> for Block {
432 fn from(statement: IntoStatement) -> Block {
433 Block::new(vec![statement.into()], None)
434 }
435}
436
437impl From<LastStatement> for Block {
438 fn from(statement: LastStatement) -> Block {
439 Block::new(Vec::new(), Some(statement))
440 }
441}
442
443impl From<ReturnStatement> for Block {
444 fn from(statement: ReturnStatement) -> Block {
445 Block::new(Vec::new(), Some(statement.into()))
446 }
447}
448
449#[cfg(test)]
450mod test {
451 use super::*;
452 use crate::{
453 generator::{LuaGenerator, TokenBasedLuaGenerator},
454 nodes::{DoStatement, RepeatStatement},
455 Parser,
456 };
457
458 fn parse_block_with_tokens(lua: &str) -> Block {
459 let parser = Parser::default().preserve_tokens();
460 parser.parse(lua).expect("code should parse")
461 }
462
463 fn parse_statement_with_tokens(lua: &str) -> Statement {
464 let mut block = parse_block_with_tokens(lua);
465 assert!(block.get_last_statement().is_none());
466 let statements = block.take_statements();
467 assert_eq!(statements.len(), 1);
468 statements.into_iter().next().unwrap()
469 }
470
471 #[test]
472 fn default_block_is_empty() {
473 let block = Block::default();
474
475 assert!(block.is_empty());
476 }
477
478 #[test]
479 fn is_empty_is_true_when_block_has_no_statements_or_last_statement() {
480 let block = Block::new(Vec::new(), None);
481
482 assert!(block.is_empty());
483 }
484
485 #[test]
486 fn is_empty_is_false_when_block_has_a_last_statement() {
487 let block = Block::default().with_last_statement(LastStatement::new_break());
488
489 assert!(!block.is_empty());
490 }
491
492 #[test]
493 fn is_empty_is_false_when_block_a_statement() {
494 let block = Block::default().with_statement(DoStatement::default());
495
496 assert!(!block.is_empty());
497 }
498
499 #[test]
500 fn clear_removes_statements() {
501 let mut block = Block::default().with_statement(DoStatement::default());
502 block.clear();
503
504 assert!(block.is_empty());
505 }
506
507 #[test]
508 fn clear_removes_last_statement() {
509 let mut block = Block::default().with_last_statement(LastStatement::new_break());
510 block.clear();
511
512 assert!(block.is_empty());
513 assert_eq!(block.get_last_statement(), None);
514 }
515
516 #[test]
517 fn set_last_statement() {
518 let mut block = Block::default();
519 let continue_statement = LastStatement::new_continue();
520 block.set_last_statement(continue_statement.clone());
521
522 assert_eq!(block.get_last_statement(), Some(&continue_statement));
523 }
524
525 #[test]
526 fn insert_statement_at_index_0() {
527 let mut block = Block::default().with_statement(DoStatement::default());
528
529 let new_statement = RepeatStatement::new(Block::default(), false);
530 block.insert_statement(0, new_statement.clone());
531
532 assert_eq!(
533 block,
534 Block::default()
535 .with_statement(new_statement)
536 .with_statement(DoStatement::default())
537 );
538 }
539
540 #[test]
541 fn insert_statement_at_index_0_with_tokens() {
542 let mut block = parse_block_with_tokens("do end;");
543
544 block.insert_statement(0, RepeatStatement::new(Block::default(), false));
545
546 insta::assert_debug_snapshot!("insert_statement_at_index_0_with_tokens", block);
547 }
548
549 #[test]
550 fn insert_statement_at_upper_bound() {
551 let mut block = Block::default().with_statement(DoStatement::default());
552
553 let new_statement = RepeatStatement::new(Block::default(), false);
554 block.insert_statement(1, new_statement.clone());
555
556 assert_eq!(
557 block,
558 Block::default()
559 .with_statement(DoStatement::default())
560 .with_statement(new_statement)
561 );
562 }
563
564 #[test]
565 fn insert_statement_after_statement_upper_bound() {
566 let mut block = Block::default().with_statement(DoStatement::default());
567
568 let new_statement = RepeatStatement::new(Block::default(), false);
569 block.insert_statement(4, new_statement.clone());
570
571 assert_eq!(
572 block,
573 Block::default()
574 .with_statement(DoStatement::default())
575 .with_statement(new_statement)
576 );
577 }
578
579 #[test]
580 fn insert_statement_after_statement_upper_bound_with_tokens() {
581 let mut block = parse_block_with_tokens("do end;");
582
583 block.insert_statement(4, RepeatStatement::new(Block::default(), false));
584
585 insta::assert_debug_snapshot!(
586 "insert_statement_after_statement_upper_bound_with_tokens",
587 block
588 );
589 }
590
591 #[test]
592 fn push_statement_with_tokens() {
593 let mut block = parse_block_with_tokens("");
594
595 let new_statement = parse_statement_with_tokens("while true do end");
596 block.push_statement(new_statement);
597
598 pretty_assertions::assert_eq!(
599 block.get_tokens(),
600 Some(&BlockTokens {
601 semicolons: vec![None],
602 last_semicolon: None,
603 final_token: None,
604 })
605 );
606 }
607
608 #[test]
609 fn attempt_to_remove_statement_from_empty_block() {
610 let mut block = parse_block_with_tokens("");
611
612 block.remove_statement(0);
613
614 assert!(block.is_empty());
615 }
616
617 #[test]
618 fn remove_first_and_only_statement() {
619 let mut block = parse_block_with_tokens("while true do end");
620
621 block.remove_statement(0);
622
623 assert!(block.is_empty());
624 }
625
626 #[test]
627 fn remove_first_statement() {
628 let mut block = parse_block_with_tokens("while true do end ; do end");
629
630 block.remove_statement(0);
631
632 insta::assert_debug_snapshot!("remove_first_statement", block);
633 }
634
635 #[test]
636 fn attempt_to_remove_statement_out_of_bounds() {
637 let mut block = parse_block_with_tokens("while true do end");
638 let original = block.clone();
639
640 block.remove_statement(1);
641 block.remove_statement(2);
642
643 assert_eq!(block, original);
644 }
645
646 #[test]
647 fn clear_removes_semicolon_tokens() {
648 let mut block = Block::default()
649 .with_statement(DoStatement::default())
650 .with_tokens(BlockTokens {
651 semicolons: vec![Some(Token::from_content(";"))],
652 last_semicolon: None,
653 final_token: None,
654 });
655 block.clear();
656
657 assert!(block.get_tokens().unwrap().semicolons.is_empty());
658 }
659
660 #[test]
661 fn clear_removes_last_semicolon_token() {
662 let mut block = Block::default()
663 .with_last_statement(LastStatement::new_break())
664 .with_tokens(BlockTokens {
665 semicolons: Vec::new(),
666 last_semicolon: Some(Token::from_content(";")),
667 final_token: None,
668 });
669 block.clear();
670
671 assert!(block.get_tokens().unwrap().last_semicolon.is_none());
672 }
673
674 #[test]
675 fn set_statements_clear_semicolon_tokens() {
676 let mut block = Block::default()
677 .with_statement(DoStatement::default())
678 .with_tokens(BlockTokens {
679 semicolons: vec![Some(Token::from_content(";"))],
680 last_semicolon: None,
681 final_token: None,
682 });
683 block.set_statements(Vec::new());
684
685 assert!(block.get_tokens().unwrap().semicolons.is_empty());
686 }
687
688 #[test]
689 fn take_last_statement_clear_semicolon_token() {
690 let mut block = Block::default()
691 .with_last_statement(LastStatement::new_break())
692 .with_tokens(BlockTokens {
693 semicolons: Vec::new(),
694 last_semicolon: Some(Token::from_content(";")),
695 final_token: None,
696 });
697
698 assert_eq!(
699 block.take_last_statement(),
700 Some(LastStatement::new_break())
701 );
702
703 assert!(block.get_tokens().unwrap().last_semicolon.is_none());
704 }
705
706 #[test]
707 fn filter_statements_does_not_panic_when_semicolons_do_not_match() {
708 let mut block = Block::default()
709 .with_statement(DoStatement::default())
710 .with_statement(DoStatement::default())
711 .with_tokens(BlockTokens {
712 semicolons: vec![Some(Token::from_content(";"))],
713 last_semicolon: None,
714 final_token: None,
715 });
716
717 block.filter_statements(|_statement| false);
718
719 pretty_assertions::assert_eq!(
720 block,
721 Block::default().with_tokens(BlockTokens {
722 semicolons: Vec::new(),
723 last_semicolon: None,
724 final_token: None,
725 })
726 );
727 }
728
729 #[test]
730 fn filter_mut_statements_does_not_panic_when_semicolons_do_not_match() {
731 let mut block = Block::default()
732 .with_statement(DoStatement::default())
733 .with_statement(DoStatement::default())
734 .with_tokens(BlockTokens {
735 semicolons: vec![Some(Token::from_content(";"))],
736 last_semicolon: None,
737 final_token: None,
738 });
739
740 block.filter_mut_statements(|_statement| false);
741
742 pretty_assertions::assert_eq!(
743 block,
744 Block::default().with_tokens(BlockTokens {
745 semicolons: Vec::new(),
746 last_semicolon: None,
747 final_token: None,
748 })
749 );
750 }
751
752 mod statement_removal {
753 use super::*;
754
755 fn remove_statement_test(index: usize, code: &str) -> String {
756 let mut block = parse_block_with_tokens(code);
757
758 block.remove_statement(index);
759
760 let mut generator = TokenBasedLuaGenerator::new(code);
761
762 generator.write_block(&block);
763
764 generator.into_string()
765 }
766
767 #[test]
768 fn remove_single_statement_preserves_leading_comments() {
769 let lua_code = remove_statement_test(0, "-- comment\nlocal a = 1");
770
771 insta::assert_snapshot!(lua_code, @"-- comment");
772 }
773
774 #[test]
775 fn remove_single_statement_preserves_trailing_comments() {
776 let lua_code = remove_statement_test(0, "local a = 1 -- comment");
777
778 insta::assert_snapshot!(lua_code, @"-- comment");
779 }
780
781 #[test]
782 fn remove_statement_preserves_comments() {
783 let lua_code = remove_statement_test(0, "local a = 1 -- comment\nlocal b = 2");
784
785 insta::assert_snapshot!(lua_code, @r###"
786 -- comment
787 local b = 2
788 "###);
789 }
790
791 #[test]
792 fn remove_statement_preserves_comments_before_return() {
793 let lua_code = remove_statement_test(0, "local a = 1 -- comment\nreturn");
794
795 insta::assert_snapshot!(lua_code, @r###"
796 -- comment
797 return
798 "###);
799 }
800
801 #[test]
802 fn remove_statement_preserves_comments_before_break() {
803 let lua_code = remove_statement_test(0, "--first\nlocal a = 1 -- comment\nbreak");
804
805 insta::assert_snapshot!(lua_code, @r###"
806 --first
807 -- comment
808 break
809 "###);
810 }
811
812 #[test]
813 fn remove_statement_preserves_comments_before_continue() {
814 let lua_code = remove_statement_test(0, "local a = 1 -- comment\ncontinue");
815
816 insta::assert_snapshot!(lua_code, @r###"
817 -- comment
818 continue
819 "###);
820 }
821
822 #[test]
823 fn remove_statement_preserves_comments_after_semicolon() {
824 let lua_code = remove_statement_test(0, "local a = 1; -- comment\nlocal b = 2;");
825
826 insta::assert_snapshot!(lua_code, @r###"
827 -- comment
828 local b = 2;
829 "###);
830 }
831 }
832}