Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

switch to arena for pretty printer

author
Giacomo Cavalieri
committer
Louis Pilfold
date (Jun 15, 2026, 12:37 PM +0100) commit 65d8d3e1 parent 2905bc76 change-id stoxtsuv
+2812 -985
+7
Cargo.lock
··· 1257 1257 "time", 1258 1258 "toml 0.9.11+spec-1.1.0", 1259 1259 "tracing", 1260 + "typed-arena", 1260 1261 "unicode-segmentation", 1261 1262 "vec1", 1262 1263 "xxhash-rust", ··· 3810 3811 dependencies = [ 3811 3812 "rustc-hash 1.1.0", 3812 3813 ] 3814 + 3815 + [[package]] 3816 + name = "typed-arena" 3817 + version = "2.0.2" 3818 + source = "registry+https://github.com/rust-lang/crates.io-index" 3819 + checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" 3813 3820 3814 3821 [[package]] 3815 3822 name = "typed-path"
+2
compiler-core/Cargo.toml
··· 51 51 bitvec = { version = "1", features = ["serde"] } 52 52 # Sourcemap generation 53 53 sourcemap = "9" 54 + # Reference arena 55 + typed-arena = "2.0.2" 54 56 55 57 async-trait.workspace = true 56 58 base16.workspace = true
+5 -2
compiler-core/src/fix.rs
··· 3 3 4 4 use crate::{ 5 5 Error, Result, 6 - format::{Formatter, Intermediate}, 6 + format::{Formatter, FormatterCache, Intermediate}, 7 + pretty_ref_arena::DocumentArena, 7 8 warning::WarningEmitter, 8 9 }; 9 10 use camino::Utf8Path; ··· 25 26 26 27 // Format 27 28 let mut buffer = String::new(); 29 + let arena = DocumentArena::new(); 30 + let cache = FormatterCache::allocate(&arena); 28 31 Formatter::with_comments(&intermediate) 29 - .module(&module) 32 + .module(&arena, &cache, &module) 30 33 .pretty_print(80, &mut buffer)?; 31 34 32 35 Ok(buffer)
+1755 -982
compiler-core/src/format.rs
··· 11 11 TypeAstTuple, TypeAstVar, *, 12 12 }, 13 13 build::Target, 14 - docvec, 14 + docvec_ref_arena, 15 15 io::Utf8Writer, 16 16 parse::extra::{Comment, ModuleExtra}, 17 - pretty::{self, *}, 17 + pretty_ref_arena::*, 18 18 warning::WarningEmitter, 19 19 }; 20 20 use ecow::{EcoString, eco_format}; ··· 27 27 28 28 const INDENT: isize = 2; 29 29 30 + #[derive(Debug)] 31 + pub struct FormatterCache<'doc, 'a> { 32 + nil: Document<'doc, 'a>, 33 + space: Document<'doc, 'a>, 34 + 35 + empty_break: Document<'doc, 'a>, 36 + breakable_space: Document<'doc, 'a>, 37 + 38 + line: Document<'doc, 'a>, 39 + two_lines: Document<'doc, 'a>, 40 + } 41 + 42 + impl<'doc, 'a> FormatterCache<'doc, 'a> { 43 + pub fn allocate(arena: &'doc DocumentArena<'a, 'doc>) -> Self { 44 + FormatterCache { 45 + nil: arena.nil(), 46 + space: " ".to_doc(arena), 47 + empty_break: arena.break_("", ""), 48 + breakable_space: arena.break_("", " "), 49 + line: arena.line(), 50 + two_lines: arena.lines(2), 51 + } 52 + } 53 + } 54 + 30 55 pub fn pretty(writer: &mut impl Utf8Writer, src: &EcoString, path: &Utf8Path) -> Result<()> { 31 56 let parsed = crate::parse::parse_module(path.to_owned(), src, &WarningEmitter::null()) 32 57 .map_err(|error| Error::Parse { ··· 35 60 error: Box::new(error), 36 61 })?; 37 62 let intermediate = Intermediate::from_extra(&parsed.extra, src); 63 + let arena = DocumentArena::new(); 64 + let cache = FormatterCache::allocate(&arena); 65 + 38 66 Formatter::with_comments(&intermediate) 39 - .module(&parsed.module) 67 + .module(&arena, &cache, &parsed.module) 40 68 .pretty_print(80, writer) 41 69 } 42 70 ··· 101 129 type UntypedRecordUpdatePiece<'a> = RecordUpdatePiece<'a, UntypedExpr>; 102 130 103 131 /// Hayleigh's bane 104 - #[derive(Debug, Clone, Default)] 132 + #[derive(Debug)] 105 133 pub struct Formatter<'a> { 106 134 comments: &'a [Comment<'a>], 107 135 doc_comments: &'a [Comment<'a>], ··· 111 139 trailing_commas: &'a [u32], 112 140 } 113 141 114 - impl<'a> Formatter<'a> { 115 - pub fn new() -> Self { 116 - Default::default() 117 - } 118 - 142 + impl<'a, 'doc> Formatter<'a> { 119 143 pub(crate) fn with_comments(extra: &'a Intermediate<'a>) -> Self { 120 144 Self { 121 145 comments: &extra.comments, ··· 205 229 end != 0 206 230 } 207 231 208 - fn targeted_definition(&mut self, definition: &'a TargetedDefinition) -> Document<'a> { 232 + fn targeted_definition( 233 + &mut self, 234 + arena: &'doc DocumentArena<'a, 'doc>, 235 + cache: &'doc FormatterCache<'a, 'doc>, 236 + definition: &'a TargetedDefinition, 237 + ) -> Document<'a, 'doc> { 209 238 let target = definition.target; 210 239 let definition = &definition.definition; 211 240 let start = definition.location().start; 212 241 213 242 let comments = self.pop_comments_with_position(start); 214 - let comments = self.printed_documented_comments(comments); 215 - let document = self.documented_definition(definition); 243 + let comments = self.printed_documented_comments(arena, cache, comments); 244 + let document = self.documented_definition(arena, cache, definition); 216 245 let document = match target { 217 246 None => document, 218 - Some(Target::Erlang) => docvec!["@target(erlang)", line(), document], 219 - Some(Target::JavaScript) => docvec!["@target(javascript)", line(), document], 247 + Some(Target::Erlang) => { 248 + docvec_ref_arena![arena, "@target(erlang)", cache.line, document] 249 + } 250 + Some(Target::JavaScript) => { 251 + docvec_ref_arena![arena, "@target(javascript)", cache.line, document] 252 + } 220 253 }; 221 254 222 - comments.to_doc().append(document.group()) 255 + comments 256 + .to_doc(&arena) 257 + .append(&arena, document.group(&arena)) 223 258 } 224 259 225 - pub(crate) fn module(&mut self, module: &'a UntypedModule) -> Document<'a> { 260 + pub(crate) fn module( 261 + &mut self, 262 + arena: &'doc DocumentArena<'a, 'doc>, 263 + cache: &'doc FormatterCache<'a, 'doc>, 264 + module: &'a UntypedModule, 265 + ) -> Document<'a, 'doc> { 226 266 let mut documents = vec![]; 227 267 let mut previous_was_a_definition = false; 228 268 ··· 235 275 { 236 276 if is_import_group { 237 277 if previous_was_a_definition { 238 - documents.push(lines(2)); 278 + documents.push(cache.two_lines); 239 279 } 240 - documents.append(&mut self.imports(definitions.collect_vec())); 280 + documents.append(&mut self.imports(arena, cache, definitions.collect_vec())); 241 281 previous_was_a_definition = false; 242 282 } else { 243 283 for definition in definitions { 244 284 if !documents.is_empty() { 245 - documents.push(lines(2)); 285 + documents.push(cache.two_lines); 246 286 } 247 - documents.push(self.targeted_definition(definition)); 287 + documents.push(self.targeted_definition(arena, cache, definition)); 248 288 } 249 289 previous_was_a_definition = true; 250 290 } 251 291 } 252 292 253 - let definitions = concat(documents); 293 + let definitions = arena.concat(documents); 254 294 255 295 // Now that definitions has been collected, only freestanding comments (//) 256 296 // and doc comments (///) remain. Freestanding comments aren't associated 257 297 // with any statement, and are moved to the bottom of the module. 258 - let doc_comments = join( 259 - self.doc_comments 260 - .iter() 261 - .map(|comment| "///".to_doc().append(EcoString::from(comment.content))), 262 - line(), 298 + let doc_comments = arena.join( 299 + self.doc_comments.iter().map(|comment| { 300 + "///" 301 + .to_doc(&arena) 302 + .append(&arena, EcoString::from(comment.content)) 303 + }), 304 + cache.line, 263 305 ); 264 306 265 - let comments = match printed_comments(self.pop_comments(u32::MAX), false) { 307 + let comments = self.pop_comments(u32::MAX); 308 + let comments = match printed_comments(arena, cache, comments, false) { 266 309 Some(comments) => comments, 267 - None => nil(), 310 + None => cache.nil, 268 311 }; 269 312 270 313 let module_comments = if !self.module_comments.is_empty() { 271 - let comments = self 272 - .module_comments 273 - .iter() 274 - .map(|s| "////".to_doc().append(EcoString::from(s.content))); 275 - join(comments, line()).append(line()) 314 + let comments = self.module_comments.iter().map(|s| { 315 + "////" 316 + .to_doc(&arena) 317 + .append(&arena, EcoString::from(s.content)) 318 + }); 319 + arena.join(comments, cache.line).append(&arena, cache.line) 276 320 } else { 277 - nil() 321 + cache.nil 278 322 }; 279 323 280 324 let non_empty = vec![module_comments, definitions, doc_comments, comments] 281 325 .into_iter() 282 - .filter(|doc| !doc.is_empty()); 326 + .filter(|doc| !doc.is_empty(&arena)); 283 327 284 - join(non_empty, line()).append(line()) 328 + arena.join(non_empty, cache.line).append(&arena, cache.line) 285 329 } 286 330 287 331 /// Separates the imports in groups delimited by comments or empty lines and ··· 306 350 /// import wibble 307 351 /// import wobble 308 352 /// ``` 309 - fn imports(&mut self, imports: Vec<&'a TargetedDefinition>) -> Vec<Document<'a>> { 353 + fn imports( 354 + &mut self, 355 + arena: &'doc DocumentArena<'a, 'doc>, 356 + cache: &'doc FormatterCache<'a, 'doc>, 357 + imports: Vec<&'a TargetedDefinition>, 358 + ) -> Vec<Document<'a, 'doc>> { 310 359 let mut import_groups_docs = vec![]; 311 360 let mut current_group = vec![]; 312 - let mut current_group_delimiter = nil(); 361 + let mut current_group_delimiter = cache.nil; 313 362 314 363 for import in imports { 315 364 let start = import.definition.location().start; ··· 321 370 // First we print the previous group and clear it out to start a 322 371 // new empty group containing the import we've just ran into. 323 372 if !current_group.is_empty() { 324 - import_groups_docs.push(docvec![ 373 + import_groups_docs.push(docvec_ref_arena![ 374 + arena, 325 375 current_group_delimiter, 326 - self.sorted_import_group(&current_group) 376 + self.sorted_import_group(arena, cache, &current_group) 327 377 ]); 328 378 current_group.clear(); 329 379 } ··· 336 386 337 387 let comments = self.pop_comments(start); 338 388 let _ = self.pop_empty_lines(start); 339 - current_group_delimiter = printed_comments(comments, true).unwrap_or(nil()); 389 + current_group_delimiter = 390 + printed_comments(arena, cache, comments, true).unwrap_or(cache.nil); 340 391 } 341 392 // Lastly we add the import to the group. 342 393 current_group.push(import); ··· 344 395 345 396 // Let's not forget about the last import group! 346 397 if !current_group.is_empty() { 347 - import_groups_docs.push(docvec![ 398 + import_groups_docs.push(docvec_ref_arena![ 399 + arena, 348 400 current_group_delimiter, 349 - self.sorted_import_group(&current_group) 401 + self.sorted_import_group(arena, cache, &current_group) 350 402 ]); 351 403 } 352 404 353 405 // We want all consecutive import groups to be separated by an empty line. 354 - // This should really be `.intersperse(line())` but I can't do that 406 + // This should really be `.intersperse(cache.line)` but I can't do that 355 407 // because of https://github.com/rust-lang/rust/issues/48919. 356 - Itertools::intersperse(import_groups_docs.into_iter(), lines(2)).collect_vec() 408 + Itertools::intersperse(import_groups_docs.into_iter(), cache.two_lines).collect_vec() 357 409 } 358 410 359 411 /// Prints the imports as a single sorted group of import statements. 360 412 /// 361 - fn sorted_import_group(&mut self, imports: &[&'a TargetedDefinition]) -> Document<'a> { 413 + fn sorted_import_group( 414 + &mut self, 415 + arena: &'doc DocumentArena<'a, 'doc>, 416 + cache: &'doc FormatterCache<'a, 'doc>, 417 + imports: &[&'a TargetedDefinition], 418 + ) -> Document<'a, 'doc> { 362 419 let imports = imports 363 420 .iter() 364 421 .sorted_by(|one, other| match (&one.definition, &other.definition) { ··· 369 426 // we just return a default value. 370 427 _ => Ordering::Equal, 371 428 }) 372 - .map(|import| self.targeted_definition(import)); 429 + .map(|import| self.targeted_definition(arena, cache, import)); 373 430 374 - // This should really be `.intersperse(line())` but I can't do that 375 - // because of https://github.com/rust-lang/rust/issues/48919. 376 - Itertools::intersperse(imports, line()) 377 - .collect_vec() 378 - .to_doc() 431 + arena.join(imports, cache.line) 379 432 } 380 433 381 - fn definition(&mut self, statement: &'a UntypedDefinition) -> Document<'a> { 434 + fn definition( 435 + &mut self, 436 + arena: &'doc DocumentArena<'a, 'doc>, 437 + cache: &'doc FormatterCache<'a, 'doc>, 438 + statement: &'a UntypedDefinition, 439 + ) -> Document<'a, 'doc> { 382 440 match statement { 383 - Definition::Function(function) => self.statement_fn(function), 441 + Definition::Function(function) => self.statement_fn(arena, cache, function), 384 442 385 - Definition::TypeAlias(alias) => self.type_alias(alias), 443 + Definition::TypeAlias(alias) => self.type_alias(arena, cache, alias), 386 444 387 - Definition::CustomType(custom_type) => self.custom_type(custom_type), 445 + Definition::CustomType(custom_type) => self.custom_type(arena, cache, custom_type), 388 446 389 447 Definition::Import(Import { 390 448 module, ··· 394 452 .. 395 453 }) => { 396 454 let second = if unqualified_values.is_empty() && unqualified_types.is_empty() { 397 - nil() 455 + cache.nil 398 456 } else { 399 457 let unqualified_types = unqualified_types 400 458 .iter() 401 459 .sorted_by(|a, b| a.name.cmp(&b.name)) 402 - .map(|type_| docvec!["type ", type_]); 460 + .map(|type_| docvec_ref_arena![arena, "type ", type_]); 403 461 let unqualified_values = unqualified_values 404 462 .iter() 405 463 .sorted_by(|a, b| a.name.cmp(&b.name)) 406 - .map(|value| value.to_doc()); 407 - let unqualified = join( 464 + .map(|value| value.to_doc(&arena)); 465 + let unqualified = arena.join( 408 466 unqualified_types.chain(unqualified_values), 409 - flex_break(",", ", "), 467 + arena.flex_break(",", ", "), 410 468 ); 411 - let unqualified = break_("", "") 412 - .append(unqualified) 413 - .nest(INDENT) 414 - .append(break_(",", "")) 415 - .group(); 416 - ".{".to_doc().append(unqualified).append("}") 469 + let unqualified = arena 470 + .break_("", "") 471 + .append(&arena, unqualified) 472 + .nest(&arena, INDENT) 473 + .append(&arena, arena.break_(",", "")) 474 + .group(&arena); 475 + ".{".to_doc(&arena) 476 + .append(&arena, unqualified) 477 + .append(&arena, "}") 417 478 }; 418 479 419 - let doc = docvec!["import ", module.as_str(), second]; 480 + let doc = docvec_ref_arena![arena, "import ", module.as_str(), second]; 420 481 let default_module_access_name = module.split('/').next_back().map(EcoString::from); 421 482 match (default_module_access_name, as_name) { 422 483 // If the `as name` is the same as the module name that would be ··· 432 493 } 433 494 (_, None) => doc, 434 495 (_, Some((AssignName::Variable(name) | AssignName::Discard(name), _))) => { 435 - doc.append(" as ").append(name) 496 + doc.append(&arena, " as ").append(&arena, name) 436 497 } 437 498 } 438 499 } ··· 452 513 let attributes = AttributesPrinter::new() 453 514 .set_internal(*publicity) 454 515 .set_deprecation(deprecation) 455 - .to_doc(); 516 + .to_doc(&arena); 456 517 let head = attributes 457 - .append(pub_(*publicity)) 458 - .append("const ") 459 - .append(name.as_str()); 518 + .append(&arena, pub_(arena, cache, *publicity)) 519 + .append(&arena, "const ") 520 + .append(&arena, name.as_str()); 460 521 let head = match annotation { 461 522 None => head, 462 - Some(type_) => head.append(": ").append(self.type_ast(type_)), 523 + Some(type_) => head 524 + .append(&arena, ": ") 525 + .append(&arena, self.type_ast(arena, cache, type_)), 463 526 }; 464 - head.append(" = ").append(self.const_expr(value).group()) 527 + head.append(&arena, " = ") 528 + .append(&arena, self.const_expr(arena, cache, value).group(&arena)) 465 529 } 466 530 } 467 531 } 468 532 469 - fn const_expr<A>(&mut self, value: &'a Constant<A>) -> Document<'a> { 533 + fn const_expr<A>( 534 + &mut self, 535 + arena: &'doc DocumentArena<'a, 'doc>, 536 + cache: &'doc FormatterCache<'a, 'doc>, 537 + value: &'a Constant<A>, 538 + ) -> Document<'a, 'doc> { 470 539 let comments = self.pop_comments(value.location().start); 471 540 let document = match value { 472 - Constant::Todo { message, .. } => { 473 - self.append_as_message_constant("todo".to_doc(), message.as_deref()) 474 - } 541 + Constant::Todo { message, .. } => self.append_as_message_constant( 542 + arena, 543 + cache, 544 + "todo".to_doc(&arena), 545 + message.as_deref(), 546 + ), 475 547 476 - Constant::Int { value, .. } => self.int(value), 548 + Constant::Int { value, .. } => self.int(arena, cache, value), 477 549 478 - Constant::Float { value, .. } => self.float(value), 550 + Constant::Float { value, .. } => self.float(arena, cache, value), 479 551 480 - Constant::String { value, .. } => self.string(value), 552 + Constant::String { value, .. } => self.string(arena, cache, value), 481 553 482 554 Constant::List { 483 555 elements, 484 556 location, 485 557 tail, 486 558 .. 487 - } => self.const_list(elements, location, tail), 559 + } => self.const_list(arena, cache, elements, location, tail), 488 560 489 561 Constant::Tuple { 490 562 elements, location, .. 491 - } => self.const_tuple(elements, location), 563 + } => self.const_tuple(arena, cache, elements, location), 492 564 493 565 Constant::BitArray { 494 566 segments, location, .. ··· 496 568 let segment_docs = segments 497 569 .iter() 498 570 .map(|segment| { 499 - bit_array_segment(segment, |expression| self.const_expr(expression)) 571 + bit_array_segment(arena, cache, segment, |expression| { 572 + self.const_expr(arena, cache, expression) 573 + }) 500 574 }) 501 575 .collect_vec(); 502 576 ··· 507 581 *location, 508 582 ); 509 583 510 - self.bit_array(segment_docs, packing, location) 584 + self.bit_array(arena, cache, segment_docs, packing, location) 511 585 } 512 586 513 587 Constant::Record { ··· 515 589 arguments: None, 516 590 module: None, 517 591 .. 518 - } => name.to_doc(), 592 + } => name.to_doc(&arena), 519 593 520 594 Constant::Record { 521 595 name, 522 596 arguments: None, 523 597 module: Some((module, _)), 524 598 .. 525 - } => module.to_doc().append(".").append(name.as_str()), 599 + } => module 600 + .to_doc(&arena) 601 + .append(&arena, ".") 602 + .append(&arena, name.as_str()), 526 603 527 604 Constant::Record { 528 605 name, ··· 533 610 } => { 534 611 let arguments = arguments 535 612 .iter() 536 - .map(|argument| self.constant_call_arg(argument)) 613 + .map(|argument| self.constant_call_arg(arena, cache, argument)) 537 614 .collect_vec(); 538 - name.to_doc() 539 - .append(self.wrap_arguments(arguments, location.end)) 540 - .group() 615 + name.to_doc(&arena) 616 + .append( 617 + &arena, 618 + self.wrap_arguments(arena, cache, arguments, location.end), 619 + ) 620 + .group(&arena) 541 621 } 542 622 543 623 Constant::Record { ··· 549 629 } => { 550 630 let arguments = arguments 551 631 .iter() 552 - .map(|argument| self.constant_call_arg(argument)) 632 + .map(|argument| self.constant_call_arg(arena, cache, argument)) 553 633 .collect_vec(); 554 634 module 555 - .to_doc() 556 - .append(".") 557 - .append(name.as_str()) 558 - .append(self.wrap_arguments(arguments, location.end)) 559 - .group() 635 + .to_doc(&arena) 636 + .append(&arena, ".") 637 + .append(&arena, name.as_str()) 638 + .append( 639 + &arena, 640 + self.wrap_arguments(arena, cache, arguments, location.end), 641 + ) 642 + .group(&arena) 560 643 } 561 644 562 645 Constant::Var { 563 646 name, module: None, .. 564 - } => name.to_doc(), 647 + } => name.to_doc(&arena), 565 648 566 649 Constant::Var { 567 650 name, 568 651 module: Some((module, _)), 569 652 .. 570 - } => docvec![module, ".", name], 653 + } => docvec_ref_arena![arena, module, ".", name], 571 654 572 655 Constant::StringConcatenation { left, right, .. } => self 573 - .const_expr(left) 574 - .append(break_("", " ").append("<>".to_doc())) 575 - .nest(INDENT) 576 - .append(" ") 577 - .append(self.const_expr(right)), 656 + .const_expr(arena, cache, left) 657 + .append( 658 + &arena, 659 + cache.breakable_space.append(&arena, "<>".to_doc(&arena)), 660 + ) 661 + .nest(&arena, INDENT) 662 + .append(&arena, cache.space) 663 + .append(&arena, self.const_expr(arena, cache, right)), 578 664 579 665 Constant::RecordUpdate { 580 666 module, ··· 583 669 arguments, 584 670 location, 585 671 .. 586 - } => self.const_record_update(module, name, record, arguments, location), 672 + } => self.const_record_update(arena, cache, module, name, record, arguments, location), 587 673 588 674 Constant::Invalid { .. } => panic!("invalid constants can not be in an untyped ast"), 589 675 }; 590 - commented(document, comments) 676 + commented(arena, cache, document, comments) 591 677 } 592 678 593 679 fn const_list<A>( 594 680 &mut self, 681 + arena: &'doc DocumentArena<'a, 'doc>, 682 + cache: &'doc FormatterCache<'a, 'doc>, 595 683 elements: &'a [Constant<A>], 596 684 location: &SrcSpan, 597 685 tail: &'a Option<Box<Constant<A>>>, 598 - ) -> Document<'a> { 686 + ) -> Document<'a, 'doc> { 599 687 if elements.is_empty() { 600 688 // We take all comments that come _before_ the end of the list, 601 689 // that is all comments that are inside "[" and "]", if there's 602 690 // any comment we want to put it inside the empty list! 603 - return match printed_comments(self.pop_comments(location.end), false) { 604 - None => "[]".to_doc(), 691 + let comments = self.pop_comments(location.end); 692 + return match printed_comments(arena, cache, comments, false) { 693 + None => "[]".to_doc(&arena), 605 694 Some(comments) => "[" 606 - .to_doc() 607 - .append(break_("", "").nest(INDENT)) 608 - .append(comments) 609 - .append(break_("", "")) 610 - .append("]") 695 + .to_doc(&arena) 696 + .append(&arena, cache.empty_break.nest(&arena, INDENT)) 697 + .append(&arena, comments) 698 + .append(&arena, cache.empty_break) 699 + .append(&arena, "]") 611 700 // vvv We want to make sure the comments are on a separate 612 701 // line from the opening and closing brackets so we 613 702 // force the breaks to be split on newlines. 614 - .force_break(), 703 + .force_break(&arena), 615 704 }; 616 705 } 617 706 ··· 622 711 *location, 623 712 ); 624 713 let comma = match list_packing { 625 - ItemsPacking::FitMultiplePerLine => flex_break(",", ", "), 626 - ItemsPacking::FitOnePerLine | ItemsPacking::BreakOnePerLine => break_(",", ", "), 714 + ItemsPacking::FitMultiplePerLine => arena.flex_break(",", ", "), 715 + ItemsPacking::FitOnePerLine | ItemsPacking::BreakOnePerLine => arena.break_(",", ", "), 627 716 }; 628 717 629 - let mut elements_doc = nil(); 718 + let mut elements_doc = cache.nil; 630 719 for element in elements.iter() { 631 720 let empty_lines = self.pop_empty_lines(element.location().start); 632 - let element_doc = self.const_expr(element); 721 + let element_doc = self.const_expr(arena, cache, element); 633 722 634 - elements_doc = if elements_doc.is_empty() { 723 + elements_doc = if elements_doc.is_empty(&arena) { 635 724 element_doc 636 725 } else if empty_lines { 637 726 // If there's empty lines before the list item we want to add an 638 727 // empty line here. Notice how we're making sure no nesting is 639 728 // added after the comma, otherwise we would be adding needless 640 729 // whitespace in the empty line! 641 - docvec![ 730 + docvec_ref_arena![ 731 + arena, 642 732 elements_doc, 643 - comma.clone().set_nesting(0), 644 - line(), 733 + comma.clone().set_nesting(&arena, 0), 734 + cache.line, 645 735 element_doc 646 736 ] 647 737 } else { 648 - docvec![elements_doc, comma.clone(), element_doc] 738 + docvec_ref_arena![arena, elements_doc, comma.clone(), element_doc] 649 739 }; 650 740 } 651 - elements_doc = elements_doc.next_break_fits(NextBreakFitsMode::Disabled); 741 + elements_doc = elements_doc.next_break_fits(&arena, NextBreakFitsMode::Disabled); 652 742 653 - let doc = break_("[", "[").append(elements_doc); 743 + let doc = arena.break_("[", "[").append(&arena, elements_doc); 654 744 let (doc, final_break) = match tail { 655 - None => (doc.nest(INDENT), break_(",", "")), 745 + None => (doc.nest(&arena, INDENT), arena.break_(",", "")), 656 746 Some(tail) => { 657 747 let comments = self.pop_comments(tail.location().start); 658 748 659 - let tail = commented(docvec!["..", self.const_expr(tail)], comments); 749 + let tail = commented( 750 + arena, 751 + cache, 752 + docvec_ref_arena![arena, "..", self.const_expr(arena, cache, tail)], 753 + comments, 754 + ); 660 755 ( 661 - doc.append(break_(",", ", ")).append(tail).nest(INDENT), 662 - break_("", ""), 756 + doc.append(&arena, arena.break_(",", ", ")) 757 + .append(&arena, tail) 758 + .nest(&arena, INDENT), 759 + cache.empty_break, 663 760 ) 664 761 } 665 762 }; ··· 670 767 // of moving those out of the list. 671 768 // Otherwise those would be moved out of the list. 672 769 let comments = self.pop_comments(location.end); 673 - let doc = match printed_comments(comments, false) { 674 - None => doc.append(final_break).append("]"), 770 + let doc = match printed_comments(arena, cache, comments, false) { 771 + None => doc.append(&arena, final_break).append(&arena, "]"), 675 772 Some(comment) => doc 676 - .append(final_break.nest(INDENT)) 773 + .append(&arena, final_break.nest(&arena, INDENT)) 677 774 // ^ See how here we're adding the missing indentation to the 678 775 // final break so that the final comment is as indented as the 679 776 // list's items. 680 - .append(comment) 681 - .append(line()) 682 - .append("]") 683 - .force_break(), 777 + .append(&arena, comment) 778 + .append(&arena, cache.line) 779 + .append(&arena, "]") 780 + .force_break(&arena), 684 781 }; 685 782 686 783 match list_packing { 687 - ItemsPacking::FitOnePerLine | ItemsPacking::FitMultiplePerLine => doc.group(), 688 - ItemsPacking::BreakOnePerLine => doc.force_break(), 784 + ItemsPacking::FitOnePerLine | ItemsPacking::FitMultiplePerLine => doc.group(&arena), 785 + ItemsPacking::BreakOnePerLine => doc.force_break(&arena), 689 786 } 690 787 } 691 788 692 789 pub fn const_tuple<A>( 693 790 &mut self, 791 + arena: &'doc DocumentArena<'a, 'doc>, 792 + cache: &'doc FormatterCache<'a, 'doc>, 694 793 elements: &'a [Constant<A>], 695 794 location: &SrcSpan, 696 - ) -> Document<'a> { 795 + ) -> Document<'a, 'doc> { 697 796 if elements.is_empty() { 698 797 // We take all comments that come _before_ the end of the tuple, 699 798 // that is all comments that are inside "#(" and ")", if there's 700 799 // any comment we want to put it inside the empty list! 701 - return match printed_comments(self.pop_comments(location.end), false) { 702 - None => "#()".to_doc(), 800 + let comments = self.pop_comments(location.end); 801 + return match printed_comments(arena, cache, comments, false) { 802 + None => "#()".to_doc(&arena), 703 803 Some(comments) => "#(" 704 - .to_doc() 705 - .append(break_("", "").nest(INDENT)) 706 - .append(comments) 707 - .append(break_("", "")) 708 - .append(")") 804 + .to_doc(&arena) 805 + .append(&arena, cache.empty_break.nest(&arena, INDENT)) 806 + .append(&arena, comments) 807 + .append(&arena, cache.empty_break) 808 + .append(&arena, ")") 709 809 // vvv We want to make sure the comments are on a separate 710 810 // line from the opening and closing parentheses so we 711 811 // force the breaks to be split on newlines. 712 - .force_break(), 812 + .force_break(&arena), 713 813 }; 714 814 } 715 815 716 - let arguments_docs = elements.iter().map(|element| self.const_expr(element)); 717 - let tuple_doc = break_("#(", "#(") 816 + let arguments_docs = elements 817 + .iter() 818 + .map(|element| self.const_expr(arena, cache, element)); 819 + let tuple_doc = arena 820 + .break_("#(", "#(") 718 821 .append( 719 - join(arguments_docs, break_(",", ", ")) 720 - .next_break_fits(NextBreakFitsMode::Disabled), 822 + &arena, 823 + arena 824 + .join(arguments_docs, arena.break_(",", ", ")) 825 + .next_break_fits(&arena, NextBreakFitsMode::Disabled), 721 826 ) 722 - .nest(INDENT); 827 + .nest(&arena, INDENT); 723 828 724 829 let comments = self.pop_comments(location.end); 725 - match printed_comments(comments, false) { 726 - None => tuple_doc.append(break_(",", "")).append(")").group(), 830 + match printed_comments(arena, cache, comments, false) { 831 + None => tuple_doc 832 + .append(&arena, arena.break_(",", "")) 833 + .append(&arena, ")") 834 + .group(&arena), 727 835 Some(comments) => tuple_doc 728 - .append(break_(",", "").nest(INDENT)) 729 - .append(comments) 730 - .append(line()) 731 - .append(")") 732 - .force_break(), 836 + .append(&arena, arena.break_(",", "").nest(&arena, INDENT)) 837 + .append(&arena, comments) 838 + .append(&arena, cache.line) 839 + .append(&arena, ")") 840 + .force_break(&arena), 733 841 } 734 842 } 735 843 736 - fn documented_definition(&mut self, definition: &'a UntypedDefinition) -> Document<'a> { 737 - let comments = self.doc_comments(definition.location().start); 738 - comments.append(self.definition(definition).group()).group() 844 + fn documented_definition( 845 + &mut self, 846 + arena: &'doc DocumentArena<'a, 'doc>, 847 + cache: &'doc FormatterCache<'a, 'doc>, 848 + definition: &'a UntypedDefinition, 849 + ) -> Document<'a, 'doc> { 850 + let comments = self.doc_comments(arena, cache, definition.location().start); 851 + comments 852 + .append( 853 + &arena, 854 + self.definition(arena, cache, definition).group(&arena), 855 + ) 856 + .group(&arena) 739 857 } 740 858 741 - fn doc_comments(&mut self, limit: u32) -> Document<'a> { 859 + fn doc_comments( 860 + &mut self, 861 + arena: &'doc DocumentArena<'a, 'doc>, 862 + cache: &'doc FormatterCache<'a, 'doc>, 863 + limit: u32, 864 + ) -> Document<'a, 'doc> { 742 865 let mut comments = self.pop_doc_comments(limit).peekable(); 743 866 match comments.peek() { 744 - None => nil(), 745 - Some(_) => join( 746 - comments.map(|comment| match comment { 747 - Some(comment) => "///".to_doc().append(EcoString::from(comment)), 748 - None => unreachable!("empty lines dropped by pop_doc_comments"), 749 - }), 750 - line(), 751 - ) 752 - .append(line()) 753 - .force_break(), 867 + None => cache.nil, 868 + Some(_) => arena 869 + .join( 870 + comments.map(|comment| match comment { 871 + Some(comment) => "///" 872 + .to_doc(&arena) 873 + .append(&arena, EcoString::from(comment)), 874 + None => unreachable!("empty lines dropped by pop_doc_comments"), 875 + }), 876 + cache.line, 877 + ) 878 + .append(&arena, cache.line) 879 + .force_break(&arena), 754 880 } 755 881 } 756 882 757 883 fn type_ast_constructor( 758 884 &mut self, 885 + arena: &'doc DocumentArena<'a, 'doc>, 886 + cache: &'doc FormatterCache<'a, 'doc>, 759 887 name: &'a TypeAstConstructorName, 760 888 arguments: &'a [TypeAst], 761 889 location: &SrcSpan, 762 - ) -> Document<'a> { 890 + ) -> Document<'a, 'doc> { 763 891 let head = match name { 764 - TypeAstConstructorName::Unqualified { name, .. } => name.to_doc(), 892 + TypeAstConstructorName::Unqualified { name, .. } => name.to_doc(&arena), 765 893 TypeAstConstructorName::Qualified { module, name, .. } => { 766 - module.to_doc().append(".").append( 894 + module.to_doc(&arena).append(&arena, ".").append( 895 + &arena, 767 896 name.as_ref() 768 - .map_or(nil(), |(name, _name_location)| name.to_doc()), 897 + .map_or(cache.nil, |(name, _name_location)| name.to_doc(&arena)), 769 898 ) 770 899 } 771 900 }; ··· 773 902 if arguments.is_empty() { 774 903 head 775 904 } else { 776 - head.append(self.type_arguments(arguments, location)) 905 + head.append( 906 + &arena, 907 + self.type_arguments(arena, cache, arguments, location), 908 + ) 777 909 } 778 910 } 779 911 780 - fn type_ast(&mut self, type_: &'a TypeAst) -> Document<'a> { 912 + fn type_ast( 913 + &mut self, 914 + arena: &'doc DocumentArena<'a, 'doc>, 915 + cache: &'doc FormatterCache<'a, 'doc>, 916 + type_: &'a TypeAst, 917 + ) -> Document<'a, 'doc> { 781 918 let comments = self.pop_comments(type_.location().start); 782 919 783 920 let type_ = match type_ { 784 - TypeAst::Hole(TypeAstHole { name, .. }) => name.to_doc(), 921 + TypeAst::Hole(TypeAstHole { name, .. }) => name.to_doc(&arena), 785 922 786 923 TypeAst::Constructor(TypeAstConstructor { 787 924 name, 788 925 arguments, 789 926 location, 790 927 start_parentheses: _, 791 - }) => self.type_ast_constructor(name, arguments, location), 928 + }) => self.type_ast_constructor(arena, cache, name, arguments, location), 792 929 793 930 TypeAst::Fn(TypeAstFn { 794 931 arguments, 795 932 return_, 796 933 location, 797 934 }) => "fn" 798 - .to_doc() 799 - .append(self.type_arguments(arguments, location)) 800 - .group() 801 - .append(" ->") 935 + .to_doc(&arena) 802 936 .append( 803 - break_("", " ") 804 - .append(self.type_ast(return_)) 805 - .group() 806 - .nest(INDENT), 937 + &arena, 938 + self.type_arguments(arena, cache, arguments, location), 939 + ) 940 + .group(&arena) 941 + .append(&arena, " ->") 942 + .append( 943 + &arena, 944 + cache 945 + .breakable_space 946 + .append(&arena, self.type_ast(arena, cache, return_)) 947 + .group(&arena) 948 + .nest(&arena, INDENT), 807 949 ), 808 950 809 - TypeAst::Var(TypeAstVar { name, .. }) => name.to_doc(), 951 + TypeAst::Var(TypeAstVar { name, .. }) => name.to_doc(&arena), 810 952 811 - TypeAst::Tuple(TypeAstTuple { elements, location }) => { 812 - "#".to_doc().append(self.type_arguments(elements, location)) 813 - } 953 + TypeAst::Tuple(TypeAstTuple { elements, location }) => "#".to_doc(&arena).append( 954 + &arena, 955 + self.type_arguments(arena, cache, elements, location), 956 + ), 814 957 }; 815 958 816 - commented(type_.group(), comments) 959 + commented(arena, cache, type_.group(&arena), comments) 817 960 } 818 961 819 - fn type_arguments(&mut self, arguments: &'a [TypeAst], location: &SrcSpan) -> Document<'a> { 962 + fn type_arguments( 963 + &mut self, 964 + arena: &'doc DocumentArena<'a, 'doc>, 965 + cache: &'doc FormatterCache<'a, 'doc>, 966 + arguments: &'a [TypeAst], 967 + location: &SrcSpan, 968 + ) -> Document<'a, 'doc> { 820 969 let arguments = arguments 821 970 .iter() 822 - .map(|type_| self.type_ast(type_)) 971 + .map(|type_| self.type_ast(arena, cache, type_)) 823 972 .collect_vec(); 824 - self.wrap_arguments(arguments, location.end) 973 + self.wrap_arguments(arena, cache, arguments, location.end) 825 974 } 826 975 827 - pub fn type_alias<A>(&mut self, alias: &'a TypeAlias<A>) -> Document<'a> { 976 + pub fn type_alias<A>( 977 + &mut self, 978 + arena: &'doc DocumentArena<'a, 'doc>, 979 + cache: &'doc FormatterCache<'a, 'doc>, 980 + alias: &'a TypeAlias<A>, 981 + ) -> Document<'a, 'doc> { 828 982 let TypeAlias { 829 983 alias: name, 830 984 parameters: arguments, ··· 840 994 let attributes = AttributesPrinter::new() 841 995 .set_deprecation(deprecation) 842 996 .set_internal(*publicity) 843 - .to_doc(); 997 + .to_doc(&arena); 844 998 845 - let head = docvec![attributes, pub_(*publicity), "type ", name]; 999 + let head = docvec_ref_arena![ 1000 + arena, 1001 + attributes, 1002 + pub_(arena, cache, *publicity), 1003 + "type ", 1004 + name 1005 + ]; 846 1006 let head = if arguments.is_empty() { 847 1007 head 848 1008 } else { 849 - let arguments = arguments.iter().map(|(_, e)| e.to_doc()).collect_vec(); 850 - head.append(self.wrap_arguments(arguments, location.end).group()) 1009 + let arguments = arguments 1010 + .iter() 1011 + .map(|(_, e)| e.to_doc(&arena)) 1012 + .collect_vec(); 1013 + head.append( 1014 + &arena, 1015 + self.wrap_arguments(arena, cache, arguments, location.end) 1016 + .group(&arena), 1017 + ) 851 1018 }; 852 1019 853 - head.append(" =") 854 - .append(line().append(self.type_ast(type_)).group().nest(INDENT)) 1020 + head.append(&arena, " =").append( 1021 + &arena, 1022 + arena 1023 + .line() 1024 + .append(&arena, self.type_ast(arena, cache, type_)) 1025 + .group(&arena) 1026 + .nest(&arena, INDENT), 1027 + ) 855 1028 } 856 1029 857 - fn fn_arg<A>(&mut self, argument: &'a Arg<A>) -> Document<'a> { 1030 + fn fn_arg<A>( 1031 + &mut self, 1032 + arena: &'doc DocumentArena<'a, 'doc>, 1033 + cache: &'doc FormatterCache<'a, 'doc>, 1034 + argument: &'a Arg<A>, 1035 + ) -> Document<'a, 'doc> { 858 1036 let comments = self.pop_comments(argument.location.start); 859 1037 let doc = match &argument.annotation { 860 - None => argument.names.to_doc(), 1038 + None => argument.names.to_doc(&arena), 861 1039 Some(type_) => argument 862 1040 .names 863 - .to_doc() 864 - .append(": ") 865 - .append(self.type_ast(type_)), 1041 + .to_doc(&arena) 1042 + .append(&arena, ": ") 1043 + .append(&arena, self.type_ast(arena, cache, type_)), 866 1044 } 867 - .group(); 868 - commented(doc, comments) 1045 + .group(&arena); 1046 + commented(arena, cache, doc, comments) 869 1047 } 870 1048 871 - fn statement_fn(&mut self, function: &'a UntypedFunction) -> Document<'a> { 1049 + fn statement_fn( 1050 + &mut self, 1051 + arena: &'doc DocumentArena<'a, 'doc>, 1052 + cache: &'doc FormatterCache<'a, 'doc>, 1053 + function: &'a UntypedFunction, 1054 + ) -> Document<'a, 'doc> { 872 1055 let Function { 873 1056 location, 874 1057 body_start: _, ··· 892 1075 .set_internal(*publicity) 893 1076 .set_external_erlang(external_erlang) 894 1077 .set_external_javascript(external_javascript) 895 - .to_doc(); 1078 + .to_doc(&arena); 896 1079 897 1080 // Fn name and args 898 1081 let arguments = arguments 899 1082 .iter() 900 - .map(|argument| self.fn_arg(argument)) 1083 + .map(|argument| self.fn_arg(arena, cache, argument)) 901 1084 .collect_vec(); 902 - let signature = pub_(*publicity) 903 - .append("fn ") 1085 + let signature = pub_(arena, cache, *publicity) 1086 + .append(&arena, "fn ") 904 1087 .append( 1088 + &arena, 905 1089 &name 906 1090 .as_ref() 907 1091 .expect("Function in a statement must be named") 908 1092 .1, 909 1093 ) 910 1094 .append( 1095 + &arena, 911 1096 self.wrap_arguments( 1097 + arena, 1098 + cache, 912 1099 arguments, 913 1100 // Calculate end location of arguments to not consume comments in 914 1101 // return annotation ··· 920 1107 921 1108 // Add return annotation 922 1109 let signature = match &return_annotation { 923 - Some(annotation) => signature.append(" -> ").append(self.type_ast(annotation)), 1110 + Some(annotation) => signature 1111 + .append(&arena, " -> ") 1112 + .append(&arena, self.type_ast(arena, cache, annotation)), 924 1113 None => signature, 925 1114 }; 926 1115 927 1116 if body.is_empty() { 928 - return docvec![attributes, signature.group()]; 1117 + return docvec_ref_arena![arena, attributes, signature.group(&arena)]; 929 1118 } 930 1119 931 1120 // Format body and add any trailing comments 932 - let body = self.statements(body); 933 - let body = match printed_comments(self.pop_comments(*end_position), false) { 934 - Some(comments) => body.append(line()).append(comments), 1121 + let body = self.statements(arena, cache, body); 1122 + let comments = self.pop_comments(*end_position); 1123 + let body = match printed_comments(arena, cache, comments, false) { 1124 + Some(comments) => body.append(&arena, cache.line).append(&arena, comments), 935 1125 None => body, 936 1126 }; 937 1127 938 1128 // Stick it all together 939 1129 let function = signature 940 - .append(" {") 941 - .group() 942 - .append(line().append(body).nest(INDENT).group()) 943 - .append(line()) 944 - .append("}"); 1130 + .append(&arena, " {") 1131 + .group(&arena) 1132 + .append( 1133 + &arena, 1134 + arena 1135 + .line() 1136 + .append(&arena, body) 1137 + .nest(&arena, INDENT) 1138 + .group(&arena), 1139 + ) 1140 + .append(&arena, cache.line) 1141 + .append(&arena, "}"); 945 1142 946 - docvec![attributes, function] 1143 + docvec_ref_arena![arena, attributes, function] 947 1144 } 948 1145 949 1146 fn expr_fn( 950 1147 &mut self, 1148 + arena: &'doc DocumentArena<'a, 'doc>, 1149 + cache: &'doc FormatterCache<'a, 'doc>, 951 1150 arguments: &'a [UntypedArg], 952 1151 return_annotation: Option<&'a TypeAst>, 953 1152 body: &'a Vec1<UntypedStatement>, 954 1153 location: &SrcSpan, 955 1154 end_of_head_byte_index: &u32, 956 - ) -> Document<'a> { 1155 + ) -> Document<'a, 'doc> { 957 1156 let arguments_docs = arguments 958 1157 .iter() 959 - .map(|argument| self.fn_arg(argument)) 1158 + .map(|argument| self.fn_arg(arena, cache, argument)) 960 1159 .collect_vec(); 961 1160 let arguments = self 962 - .wrap_arguments(arguments_docs, *end_of_head_byte_index) 963 - .group() 964 - .next_break_fits(NextBreakFitsMode::Disabled); 1161 + .wrap_arguments(arena, cache, arguments_docs, *end_of_head_byte_index) 1162 + .group(&arena) 1163 + .next_break_fits(&arena, NextBreakFitsMode::Disabled); 965 1164 // ^^^ We add this so that when an expression function is passed as 966 1165 // the last argument of a function and it goes over the line 967 1166 // limit with just its arguments we don't get some strange ··· 978 1177 // These are some of the ways we could tweak the look of expression 979 1178 // functions in the future if people are not satisfied with it. 980 1179 981 - let header = "fn".to_doc().append(arguments); 1180 + let header = "fn".to_doc(&arena).append(&arena, arguments); 982 1181 983 1182 let header = match return_annotation { 984 1183 None => header, 985 1184 Some(return_annotation) => header 986 - .append(" -> ") 987 - .append(self.type_ast(return_annotation)), 1185 + .append(&arena, " -> ") 1186 + .append(&arena, self.type_ast(arena, cache, return_annotation)), 988 1187 }; 989 1188 990 - let statements = self.statements(body.as_vec()); 991 - let body = match printed_comments(self.pop_comments(location.end), false) { 1189 + let statements = self.statements(arena, cache, body.as_vec()); 1190 + let body = match printed_comments(arena, cache, self.pop_comments(location.end), false) { 992 1191 None => statements, 993 - Some(comments) => statements.append(line()).append(comments).force_break(), 1192 + Some(comments) => statements 1193 + .append(&arena, cache.line) 1194 + .append(&arena, comments) 1195 + .force_break(&arena), 994 1196 }; 995 1197 996 - header.append(" ").append(wrap_block(body)).group() 1198 + header 1199 + .append(&arena, cache.space) 1200 + .append(&arena, wrap_block(arena, cache, body)) 1201 + .group(&arena) 997 1202 } 998 1203 999 - fn statements(&mut self, statements: &'a [UntypedStatement]) -> Document<'a> { 1204 + fn statements( 1205 + &mut self, 1206 + arena: &'doc DocumentArena<'a, 'doc>, 1207 + cache: &'doc FormatterCache<'a, 'doc>, 1208 + statements: &'a [UntypedStatement], 1209 + ) -> Document<'a, 'doc> { 1000 1210 let mut previous_position = 0; 1001 1211 let count = statements.len(); 1002 1212 let mut documents = Vec::with_capacity(count * 2); 1003 1213 for (i, statement) in statements.iter().enumerate() { 1004 1214 let preceding_newline = self.pop_empty_lines(previous_position + 1); 1005 1215 if i != 0 && preceding_newline { 1006 - documents.push(lines(2)); 1216 + documents.push(cache.two_lines); 1007 1217 } else if i != 0 { 1008 - documents.push(line()); 1218 + documents.push(cache.line); 1009 1219 } 1010 1220 previous_position = statement.location().end; 1011 - documents.push(self.statement(statement).group()); 1221 + documents.push(self.statement(arena, cache, statement).group(&arena)); 1012 1222 1013 1223 // If the last statement is a use we make sure it's followed by a 1014 1224 // todo to make it explicit it has an unimplemented callback. 1015 1225 if statement.is_use() && i == count - 1 { 1016 - documents.push(line()); 1017 - documents.push("todo".to_doc()); 1226 + documents.push(cache.line); 1227 + documents.push("todo".to_doc(&arena)); 1018 1228 } 1019 1229 } 1020 1230 ··· 1023 1233 .first() 1024 1234 .is_some_and(|statement| statement.is_expression()) 1025 1235 { 1026 - documents.to_doc() 1236 + documents.to_doc(&arena) 1027 1237 } else { 1028 - documents.to_doc().force_break() 1238 + documents.to_doc(&arena).force_break(&arena) 1029 1239 } 1030 1240 } 1031 1241 1032 - fn assignment(&mut self, assignment: &'a UntypedAssignment) -> Document<'a> { 1242 + fn assignment( 1243 + &mut self, 1244 + arena: &'doc DocumentArena<'a, 'doc>, 1245 + cache: &'doc FormatterCache<'a, 'doc>, 1246 + assignment: &'a UntypedAssignment, 1247 + ) -> Document<'a, 'doc> { 1033 1248 let comments = self.pop_comments(assignment.location.start); 1034 1249 let Assignment { 1035 1250 pattern, ··· 1046 1261 AssignmentKind::Assert { message, .. } => ("let assert ", message.as_ref()), 1047 1262 }; 1048 1263 1049 - let pattern = self.pattern(pattern); 1264 + let pattern = self.pattern(arena, cache, pattern); 1050 1265 1051 - let annotation = annotation 1052 - .as_ref() 1053 - .map(|annotation| ": ".to_doc().append(self.type_ast(annotation))); 1266 + let annotation = annotation.as_ref().map(|annotation| { 1267 + ": ".to_doc(&arena) 1268 + .append(&arena, self.type_ast(arena, cache, annotation)) 1269 + }); 1054 1270 1055 1271 let doc = keyword 1056 - .to_doc() 1057 - .append(pattern.append(annotation).group()) 1058 - .append(" =") 1059 - .append(self.assigned_value(value)); 1272 + .to_doc(&arena) 1273 + .append(&arena, pattern.append(&arena, annotation).group(&arena)) 1274 + .append(&arena, " =") 1275 + .append(&arena, self.assigned_value(arena, cache, value)); 1060 1276 1061 1277 commented( 1062 - self.append_as_message_expression(doc, PrecedingAs::Expression, message), 1278 + arena, 1279 + cache, 1280 + self.append_as_message_expression(arena, cache, doc, PrecedingAs::Expression, message), 1063 1281 comments, 1064 1282 ) 1065 1283 } 1066 1284 1067 - fn expr(&mut self, expression: &'a UntypedExpr) -> Document<'a> { 1285 + fn expr( 1286 + &mut self, 1287 + arena: &'doc DocumentArena<'a, 'doc>, 1288 + cache: &'doc FormatterCache<'a, 'doc>, 1289 + expression: &'a UntypedExpr, 1290 + ) -> Document<'a, 'doc> { 1068 1291 let comments = self.pop_comments(expression.start_byte_index()); 1069 1292 1070 1293 let document = match expression { 1071 1294 UntypedExpr::Panic { message, .. } => self.append_as_message_expression( 1072 - "panic".to_doc(), 1295 + arena, 1296 + cache, 1297 + "panic".to_doc(&arena), 1073 1298 PrecedingAs::Keyword, 1074 1299 message.as_deref(), 1075 1300 ), 1076 1301 1077 1302 UntypedExpr::Todo { message, .. } => self.append_as_message_expression( 1078 - "todo".to_doc(), 1303 + arena, 1304 + cache, 1305 + "todo".to_doc(&arena), 1079 1306 PrecedingAs::Keyword, 1080 1307 message.as_deref(), 1081 1308 ), ··· 1085 1312 location: _, 1086 1313 keyword_end: _, 1087 1314 message, 1088 - } => self.echo(expression, message), 1315 + } => self.echo(arena, cache, expression, message), 1089 1316 1090 - UntypedExpr::PipeLine { expressions, .. } => self.pipeline(expressions, false), 1317 + UntypedExpr::PipeLine { expressions, .. } => { 1318 + self.pipeline(arena, cache, expressions, false) 1319 + } 1091 1320 1092 - UntypedExpr::Int { value, .. } => self.int(value), 1321 + UntypedExpr::Int { value, .. } => self.int(arena, cache, value), 1093 1322 1094 - UntypedExpr::Float { value, .. } => self.float(value), 1323 + UntypedExpr::Float { value, .. } => self.float(arena, cache, value), 1095 1324 1096 - UntypedExpr::String { value, .. } => self.string(value), 1325 + UntypedExpr::String { value, .. } => self.string(arena, cache, value), 1097 1326 1098 1327 UntypedExpr::Block { 1099 1328 statements, 1100 1329 location, 1101 1330 .. 1102 - } => self.block(location, statements, false), 1331 + } => self.block(arena, cache, location, statements, false), 1103 1332 1104 - UntypedExpr::Var { name, .. } if name == CAPTURE_VARIABLE => "_".to_doc(), 1333 + UntypedExpr::Var { name, .. } if name == CAPTURE_VARIABLE => "_".to_doc(&arena), 1105 1334 1106 - UntypedExpr::Var { name, .. } => name.to_doc(), 1335 + UntypedExpr::Var { name, .. } => name.to_doc(&arena), 1107 1336 1108 - UntypedExpr::TupleIndex { tuple, index, .. } => self.tuple_index(tuple, *index), 1337 + UntypedExpr::TupleIndex { tuple, index, .. } => { 1338 + self.tuple_index(arena, cache, tuple, *index) 1339 + } 1109 1340 1110 - UntypedExpr::NegateInt { value, .. } => self.negate_int(value), 1341 + UntypedExpr::NegateInt { value, .. } => self.negate_int(arena, cache, value), 1111 1342 1112 - UntypedExpr::NegateBool { value, .. } => self.negate_bool(value), 1343 + UntypedExpr::NegateBool { value, .. } => self.negate_bool(arena, cache, value), 1113 1344 1114 1345 UntypedExpr::Fn { kind, body, .. } if kind.is_capture() => { 1115 - self.fn_capture(body, FnCapturePosition::EverywhereElse) 1346 + self.fn_capture(arena, cache, body, FnCapturePosition::EverywhereElse) 1116 1347 } 1117 1348 1118 1349 UntypedExpr::Fn { ··· 1123 1354 end_of_head_byte_index, 1124 1355 .. 1125 1356 } => self.expr_fn( 1357 + arena, 1358 + cache, 1126 1359 arguments, 1127 1360 return_annotation.as_ref(), 1128 1361 body, ··· 1134 1367 elements, 1135 1368 tail, 1136 1369 location, 1137 - } => self.list(elements, tail.as_deref(), location), 1370 + } => self.list(arena, cache, elements, tail.as_deref(), location), 1138 1371 1139 1372 UntypedExpr::Call { 1140 1373 fun, 1141 1374 arguments, 1142 1375 location, 1143 1376 .. 1144 - } => self.call(fun, arguments, location), 1377 + } => self.call(arena, cache, fun, arguments, location), 1145 1378 1146 1379 UntypedExpr::BinOp { 1147 1380 operator, 1148 1381 left, 1149 1382 right, 1150 1383 .. 1151 - } => self.bin_op(operator, left, right, false), 1384 + } => self.bin_op(arena, cache, operator, left, right, false), 1152 1385 1153 1386 UntypedExpr::Case { 1154 1387 subjects, 1155 1388 clauses, 1156 1389 location, 1157 - } => self.case(subjects, clauses.as_deref().unwrap_or_default(), location), 1390 + } => self.case( 1391 + arena, 1392 + cache, 1393 + subjects, 1394 + clauses.as_deref().unwrap_or_default(), 1395 + location, 1396 + ), 1158 1397 1159 1398 UntypedExpr::FieldAccess { 1160 1399 label, container, .. 1161 - } => self.expr(container).append(".").append(label.as_str()), 1400 + } => self 1401 + .expr(arena, cache, container) 1402 + .append(&arena, ".") 1403 + .append(&arena, label.as_str()), 1162 1404 1163 - UntypedExpr::Tuple { elements, location } => self.tuple(elements, location), 1405 + UntypedExpr::Tuple { elements, location } => { 1406 + self.tuple(arena, cache, elements, location) 1407 + } 1164 1408 1165 1409 UntypedExpr::BitArray { 1166 1410 segments, location, .. 1167 1411 } => { 1168 1412 let segment_docs = segments 1169 1413 .iter() 1170 - .map(|segment| bit_array_segment(segment, |e| self.bit_array_segment_expr(e))) 1414 + .map(|segment| { 1415 + bit_array_segment(arena, cache, segment, |e| { 1416 + self.bit_array_segment_expr(arena, cache, e) 1417 + }) 1418 + }) 1171 1419 .collect_vec(); 1172 1420 1173 1421 let packing = self.items_sequence_packing( ··· 1177 1425 *location, 1178 1426 ); 1179 1427 1180 - self.bit_array(segment_docs, packing, location) 1428 + self.bit_array(arena, cache, segment_docs, packing, location) 1181 1429 } 1182 1430 UntypedExpr::RecordUpdate { 1183 1431 constructor, ··· 1185 1433 arguments, 1186 1434 location, 1187 1435 .. 1188 - } => self.record_update(constructor, record, arguments, location), 1436 + } => self.record_update(arena, cache, constructor, record, arguments, location), 1189 1437 }; 1190 - commented(document, comments) 1438 + commented(arena, cache, document, comments) 1191 1439 } 1192 1440 1193 - fn string(&self, string: &'a EcoString) -> Document<'a> { 1194 - let doc = string.to_doc().surround("\"", "\""); 1441 + fn string( 1442 + &self, 1443 + arena: &'doc DocumentArena<'a, 'doc>, 1444 + _cache: &'doc FormatterCache<'a, 'doc>, 1445 + string: &'a EcoString, 1446 + ) -> Document<'a, 'doc> { 1447 + let doc = string.to_doc(&arena).surround(&arena, "\"", "\""); 1195 1448 if string.contains('\n') { 1196 - doc.force_break() 1449 + doc.force_break(&arena) 1197 1450 } else { 1198 1451 doc 1199 1452 } 1200 1453 } 1201 1454 1202 - fn bin_op_string(&self, string: &'a EcoString) -> Document<'a> { 1455 + fn bin_op_string( 1456 + &self, 1457 + arena: &'doc DocumentArena<'a, 'doc>, 1458 + cache: &'doc FormatterCache<'a, 'doc>, 1459 + string: &'a EcoString, 1460 + ) -> Document<'a, 'doc> { 1203 1461 let lines = string.split('\n').collect_vec(); 1204 1462 match lines.as_slice() { 1205 - [] | [_] => string.to_doc().surround("\"", "\""), 1463 + [] | [_] => string.to_doc(&arena).surround(&arena, "\"", "\""), 1206 1464 [first_line, lines @ ..] => { 1207 - let mut doc = docvec!["\"", first_line]; 1465 + let mut doc = docvec_ref_arena![arena, "\"", *first_line]; 1208 1466 for line in lines { 1209 1467 doc = doc 1210 - .append(pretty::line().set_nesting(0)) 1211 - .append(line.to_doc()) 1468 + .append(&arena, cache.line.set_nesting(&arena, 0)) 1469 + .append(&arena, line.to_doc(&arena)) 1212 1470 } 1213 - doc.append("\"".to_doc()).group() 1471 + doc.append(&arena, "\"".to_doc(&arena)).group(&arena) 1214 1472 } 1215 1473 } 1216 1474 } 1217 1475 1218 - fn float(&self, value: &'a str) -> Document<'a> { 1476 + fn float( 1477 + &self, 1478 + arena: &'doc DocumentArena<'a, 'doc>, 1479 + cache: &'doc FormatterCache<'a, 'doc>, 1480 + value: &'a str, 1481 + ) -> Document<'a, 'doc> { 1219 1482 // Create parts 1220 1483 let mut parts = value.split('.'); 1221 1484 let integer_part = parts.next().unwrap_or_default(); 1222 1485 let floating_part = parts.next().unwrap_or_default(); 1223 - let integer_doc = self.underscore_integer_string(integer_part); 1224 - let dot_doc = ".".to_doc(); 1486 + let integer_doc = self.underscore_integer_string(arena, cache, integer_part); 1487 + let dot_doc = ".".to_doc(&arena); 1225 1488 1226 1489 // Split fp_part into a regular fractional and maybe a scientific part 1227 1490 let (fractional_part, scientific_part) = floating_part.split_at( ··· 1240 1503 let float_doc = fractional_part.chars().collect::<EcoString>(); 1241 1504 1242 1505 integer_doc 1243 - .append(dot_doc) 1244 - .append(float_doc) 1245 - .append(scientific_part) 1506 + .append(&arena, dot_doc) 1507 + .append(&arena, float_doc) 1508 + .append(&arena, scientific_part) 1246 1509 } 1247 1510 1248 - fn int(&self, value: &'a str) -> Document<'a> { 1511 + fn int( 1512 + &self, 1513 + arena: &'doc DocumentArena<'a, 'doc>, 1514 + cache: &'doc FormatterCache<'a, 'doc>, 1515 + value: &'a str, 1516 + ) -> Document<'a, 'doc> { 1249 1517 if value.starts_with("0x") || value.starts_with("0b") || value.starts_with("0o") { 1250 - return value.to_doc(); 1518 + return value.to_doc(&arena); 1251 1519 } 1252 1520 1253 - self.underscore_integer_string(value) 1521 + self.underscore_integer_string(arena, cache, value) 1254 1522 } 1255 1523 1256 - fn underscore_integer_string(&self, value: &'a str) -> Document<'a> { 1524 + fn underscore_integer_string( 1525 + &self, 1526 + arena: &'doc DocumentArena<'a, 'doc>, 1527 + _cache: &'doc FormatterCache<'a, 'doc>, 1528 + value: &'a str, 1529 + ) -> Document<'a, 'doc> { 1257 1530 let underscore = '_'; 1258 1531 let minus = '-'; 1259 1532 ··· 1277 1550 j += 1; 1278 1551 } 1279 1552 1280 - new_value.chars().rev().collect::<EcoString>().to_doc() 1553 + new_value 1554 + .chars() 1555 + .rev() 1556 + .collect::<EcoString>() 1557 + .to_doc(&arena) 1281 1558 } 1282 1559 1283 1560 fn pattern_constructor( 1284 1561 &mut self, 1562 + arena: &'doc DocumentArena<'a, 'doc>, 1563 + cache: &'doc FormatterCache<'a, 'doc>, 1285 1564 name: &'a str, 1286 1565 arguments: &'a [CallArg<UntypedPattern>], 1287 1566 module: &'a Option<(EcoString, SrcSpan)>, 1288 1567 spread: Option<SrcSpan>, 1289 1568 location: &SrcSpan, 1290 - ) -> Document<'a> { 1569 + ) -> Document<'a, 'doc> { 1291 1570 fn is_breakable(expression: &UntypedPattern) -> bool { 1292 1571 match expression { 1293 1572 Pattern::Tuple { .. } | Pattern::List { .. } | Pattern::BitArray { .. } => true, ··· 1305 1584 } 1306 1585 1307 1586 let name = match module { 1308 - Some((module, _)) => module.to_doc().append(".").append(name), 1309 - None => name.to_doc(), 1587 + Some((module, _)) => module 1588 + .to_doc(&arena) 1589 + .append(&arena, ".") 1590 + .append(&arena, name), 1591 + None => name.to_doc(&arena), 1310 1592 }; 1311 1593 1312 1594 if arguments.is_empty() && spread.is_some() { 1313 - name.append("(..)") 1595 + name.append(&arena, "(..)") 1314 1596 } else if arguments.is_empty() { 1315 1597 name 1316 1598 } else if spread.is_some() { 1317 1599 let arguments = arguments 1318 1600 .iter() 1319 - .map(|argument| self.pattern_call_arg(argument)) 1601 + .map(|argument| self.pattern_call_arg(arena, cache, argument)) 1320 1602 .collect_vec(); 1321 - name.append(self.wrap_arguments_with_spread(arguments, location.end)) 1322 - .group() 1603 + name.append( 1604 + &arena, 1605 + self.wrap_arguments_with_spread(arena, cache, arguments, location.end), 1606 + ) 1607 + .group(&arena) 1323 1608 } else { 1324 1609 match arguments { 1325 1610 [argument] if is_breakable(&argument.value) => name 1326 - .append("(") 1327 - .append(self.pattern_call_arg(argument)) 1328 - .append(")") 1329 - .group(), 1611 + .append(&arena, "(") 1612 + .append(&arena, self.pattern_call_arg(arena, cache, argument)) 1613 + .append(&arena, ")") 1614 + .group(&arena), 1330 1615 1331 1616 _ => { 1332 1617 let arguments = arguments 1333 1618 .iter() 1334 - .map(|argument| self.pattern_call_arg(argument)) 1619 + .map(|argument| self.pattern_call_arg(arena, cache, argument)) 1335 1620 .collect_vec(); 1336 - name.append(self.wrap_arguments(arguments, location.end)) 1337 - .group() 1621 + name.append( 1622 + &arena, 1623 + self.wrap_arguments(arena, cache, arguments, location.end), 1624 + ) 1625 + .group(&arena) 1338 1626 } 1339 1627 } 1340 1628 } ··· 1342 1630 1343 1631 fn call( 1344 1632 &mut self, 1633 + arena: &'doc DocumentArena<'a, 'doc>, 1634 + cache: &'doc FormatterCache<'a, 'doc>, 1345 1635 function: &'a UntypedExpr, 1346 1636 arguments: &'a [CallArg<UntypedExpr>], 1347 1637 location: &SrcSpan, 1348 - ) -> Document<'a> { 1638 + ) -> Document<'a, 'doc> { 1349 1639 let expression = match function { 1350 - UntypedExpr::PipeLine { .. } => break_block(self.expr(function)), 1640 + UntypedExpr::PipeLine { .. } => { 1641 + break_block(arena, cache, self.expr(arena, cache, function)) 1642 + } 1351 1643 1352 1644 UntypedExpr::BinOp { .. } 1353 1645 | UntypedExpr::Int { .. } ··· 1368 1660 | UntypedExpr::BitArray { .. } 1369 1661 | UntypedExpr::RecordUpdate { .. } 1370 1662 | UntypedExpr::NegateBool { .. } 1371 - | UntypedExpr::NegateInt { .. } => self.expr(function), 1663 + | UntypedExpr::NegateInt { .. } => self.expr(arena, cache, function), 1372 1664 }; 1373 1665 1374 1666 let arity = arguments.len(); 1375 1667 self.append_inlinable_wrapped_arguments( 1668 + arena, 1669 + cache, 1376 1670 expression, 1377 1671 arguments, 1378 1672 location, 1379 1673 |argument| is_breakable_argument(&argument.value, arguments.len()), 1380 - |self_, argument| self_.call_arg(argument, arity), 1674 + |self_, argument| self_.call_arg(arena, cache, argument, arity), 1381 1675 ) 1382 1676 } 1383 1677 1384 - fn tuple(&mut self, elements: &'a [UntypedExpr], location: &SrcSpan) -> Document<'a> { 1678 + fn tuple( 1679 + &mut self, 1680 + arena: &'doc DocumentArena<'a, 'doc>, 1681 + cache: &'doc FormatterCache<'a, 'doc>, 1682 + elements: &'a [UntypedExpr], 1683 + location: &SrcSpan, 1684 + ) -> Document<'a, 'doc> { 1385 1685 if elements.is_empty() { 1386 1686 // We take all comments that come _before_ the end of the tuple, 1387 1687 // that is all comments that are inside "#(" and ")", if there's 1388 1688 // any comment we want to put it inside the empty tuple! 1389 - return match printed_comments(self.pop_comments(location.end), false) { 1390 - None => "#()".to_doc(), 1689 + return match printed_comments(arena, cache, self.pop_comments(location.end), false) { 1690 + None => "#()".to_doc(&arena), 1391 1691 Some(comments) => "#(" 1392 - .to_doc() 1393 - .append(break_("", "").nest(INDENT)) 1394 - .append(comments) 1395 - .append(break_("", "")) 1396 - .append(")") 1692 + .to_doc(&arena) 1693 + .append(&arena, cache.empty_break.nest(&arena, INDENT)) 1694 + .append(&arena, comments) 1695 + .append(&arena, cache.empty_break) 1696 + .append(&arena, ")") 1397 1697 // vvv We want to make sure the comments are on a separate 1398 1698 // line from the opening and closing parentheses so we 1399 1699 // force the breaks to be split on newlines. 1400 - .force_break(), 1700 + .force_break(&arena), 1401 1701 }; 1402 1702 } 1403 1703 1404 1704 self.append_inlinable_wrapped_arguments( 1405 - "#".to_doc(), 1705 + arena, 1706 + cache, 1707 + "#".to_doc(&arena), 1406 1708 elements, 1407 1709 location, 1408 1710 |expression| { 1409 1711 !expression.is_tuple() && is_breakable_argument(expression, elements.len()) 1410 1712 }, 1411 - |self_, expression| self_.comma_separated_item(expression, elements.len()), 1713 + |self_, expression| { 1714 + self_.comma_separated_item(arena, cache, expression, elements.len()) 1715 + }, 1412 1716 ) 1413 1717 } 1414 1718 ··· 1419 1723 // This is used for function calls and tuples. 1420 1724 fn append_inlinable_wrapped_arguments<'b, T, Predicate, ToDoc>( 1421 1725 &mut self, 1422 - doc: Document<'a>, 1726 + arena: &'doc DocumentArena<'a, 'doc>, 1727 + cache: &'doc FormatterCache<'a, 'doc>, 1728 + doc: Document<'a, 'doc>, 1423 1729 values: &'b [T], 1424 1730 location: &SrcSpan, 1425 1731 is_breakable_argument: Predicate, 1426 1732 to_doc: ToDoc, 1427 - ) -> Document<'a> 1733 + ) -> Document<'a, 'doc> 1428 1734 where 1429 1735 T: HasLocation, 1430 1736 T: std::fmt::Debug, 1431 1737 Predicate: Fn(&T) -> bool, 1432 - ToDoc: Fn(&mut Self, &'b T) -> Document<'a>, 1738 + ToDoc: Fn(&mut Self, &'b T) -> Document<'a, 'doc>, 1433 1739 { 1434 1740 match init_and_last(values) { 1435 1741 Some((initial_values, last_value)) ··· 1443 1749 .collect_vec(); 1444 1750 1445 1751 let last_value_doc = to_doc(self, last_value) 1446 - .group() 1447 - .next_break_fits(NextBreakFitsMode::Enabled); 1752 + .group(&arena) 1753 + .next_break_fits(&arena, NextBreakFitsMode::Enabled); 1448 1754 1449 1755 docs.append(&mut vec![last_value_doc]); 1450 1756 1451 - doc.append(self.wrap_function_call_arguments(docs, location)) 1452 - .next_break_fits(NextBreakFitsMode::Disabled) 1453 - .group() 1757 + doc.append( 1758 + &arena, 1759 + self.wrap_function_call_arguments(arena, cache, docs, location), 1760 + ) 1761 + .next_break_fits(&arena, NextBreakFitsMode::Disabled) 1762 + .group(&arena) 1454 1763 } 1455 1764 1456 1765 Some(_) | None => { 1457 1766 let docs = values.iter().map(|value| to_doc(self, value)).collect_vec(); 1458 - doc.append(self.wrap_function_call_arguments(docs, location)) 1459 - .group() 1767 + doc.append( 1768 + &arena, 1769 + self.wrap_function_call_arguments(arena, cache, docs, location), 1770 + ) 1771 + .group(&arena) 1460 1772 } 1461 1773 } 1462 1774 } 1463 1775 1464 1776 pub fn case( 1465 1777 &mut self, 1778 + arena: &'doc DocumentArena<'a, 'doc>, 1779 + cache: &'doc FormatterCache<'a, 'doc>, 1466 1780 subjects: &'a [UntypedExpr], 1467 1781 clauses: &'a [UntypedClause], 1468 1782 location: &'a SrcSpan, 1469 - ) -> Document<'a> { 1470 - let subjects_doc = break_("case", "case ") 1471 - .append(join( 1472 - subjects.iter().map(|subject| self.expr(subject).group()), 1473 - break_(",", ", "), 1474 - )) 1475 - .nest(INDENT) 1476 - .append(break_("", " ")) 1477 - .append("{") 1478 - .next_break_fits(NextBreakFitsMode::Disabled) 1479 - .group(); 1783 + ) -> Document<'a, 'doc> { 1784 + let subjects_doc = arena 1785 + .break_("case", "case ") 1786 + .append( 1787 + &arena, 1788 + arena.join( 1789 + subjects 1790 + .iter() 1791 + .map(|subject| self.expr(arena, cache, subject).group(&arena)), 1792 + arena.break_(",", ", "), 1793 + ), 1794 + ) 1795 + .nest(&arena, INDENT) 1796 + .append(&arena, cache.breakable_space) 1797 + .append(&arena, "{") 1798 + .next_break_fits(&arena, NextBreakFitsMode::Disabled) 1799 + .group(&arena); 1480 1800 1481 - let clauses_doc = concat( 1801 + let clauses_doc = arena.concat( 1482 1802 clauses 1483 1803 .iter() 1484 1804 .enumerate() 1485 - .map(|(i, clause)| self.clause(clause, i as u32).group()), 1805 + .map(|(i, clause)| self.clause(arena, cache, clause, i as u32).group(&arena)), 1486 1806 ); 1487 1807 1488 1808 // We get all remaining comments that come before the case's closing ··· 1490 1810 // instead of moving those out of the case expression. 1491 1811 // Otherwise those would be moved out of the case expression. 1492 1812 let comments = self.pop_comments(location.end); 1493 - let closing_bracket = match printed_comments(comments, false) { 1494 - None => docvec![line(), "}"], 1495 - Some(comment) => docvec![line(), comment] 1496 - .nest(INDENT) 1497 - .append(line()) 1498 - .append("}"), 1813 + let closing_bracket = match printed_comments(arena, cache, comments, false) { 1814 + None => docvec_ref_arena![arena, cache.line, "}"], 1815 + Some(comment) => docvec_ref_arena![arena, cache.line, comment] 1816 + .nest(&arena, INDENT) 1817 + .append(&arena, cache.line) 1818 + .append(&arena, "}"), 1499 1819 }; 1500 1820 1501 1821 subjects_doc 1502 - .append(line().append(clauses_doc).nest(INDENT)) 1503 - .append(closing_bracket) 1504 - .force_break() 1822 + .append( 1823 + &arena, 1824 + arena 1825 + .line() 1826 + .append(&arena, clauses_doc) 1827 + .nest(&arena, INDENT), 1828 + ) 1829 + .append(&arena, closing_bracket) 1830 + .force_break(&arena) 1505 1831 } 1506 1832 1507 1833 pub fn record_update( 1508 1834 &mut self, 1835 + arena: &'doc DocumentArena<'a, 'doc>, 1836 + cache: &'doc FormatterCache<'a, 'doc>, 1509 1837 constructor: &'a UntypedExpr, 1510 1838 record: &'a RecordBeingUpdated<UntypedExpr>, 1511 1839 arguments: &'a [UntypedRecordUpdateArg], 1512 1840 location: &SrcSpan, 1513 - ) -> Document<'a> { 1514 - let constructor_doc: Document<'a> = self.expr(constructor); 1841 + ) -> Document<'a, 'doc> { 1842 + let constructor_doc: Document<'a, 'doc> = self.expr(arena, cache, constructor); 1515 1843 let pieces = std::iter::once(UntypedRecordUpdatePiece::Record(record)) 1516 1844 .chain(arguments.iter().map(UntypedRecordUpdatePiece::Argument)) 1517 1845 .collect_vec(); 1518 1846 1519 1847 self.append_inlinable_wrapped_arguments( 1848 + arena, 1849 + cache, 1520 1850 constructor_doc, 1521 1851 &pieces, 1522 1852 location, ··· 1528 1858 is_breakable_argument(expression, pieces.len()) 1529 1859 }, 1530 1860 |this, argument| match argument { 1531 - UntypedRecordUpdatePiece::Argument(arg) => this.record_update_arg(arg), 1861 + UntypedRecordUpdatePiece::Argument(arg) => { 1862 + this.record_update_arg(arena, cache, arg) 1863 + } 1532 1864 UntypedRecordUpdatePiece::Record(record) => { 1533 1865 let comments = this.pop_comments(record.location.start); 1534 - commented("..".to_doc().append(this.expr(&record.base)), comments) 1866 + commented( 1867 + arena, 1868 + cache, 1869 + "..".to_doc(&arena) 1870 + .append(&arena, this.expr(arena, cache, &record.base)), 1871 + comments, 1872 + ) 1535 1873 } 1536 1874 }, 1537 1875 ) ··· 1539 1877 1540 1878 pub fn const_record_update<A>( 1541 1879 &mut self, 1880 + arena: &'doc DocumentArena<'a, 'doc>, 1881 + cache: &'doc FormatterCache<'a, 'doc>, 1542 1882 module: &Option<(EcoString, SrcSpan)>, 1543 1883 name: &'a EcoString, 1544 1884 record: &'a RecordBeingUpdated<Constant<A>>, 1545 1885 arguments: &'a [RecordUpdateArg<Constant<A>>], 1546 1886 location: &SrcSpan, 1547 - ) -> Document<'a> { 1887 + ) -> Document<'a, 'doc> { 1548 1888 let constructor_doc = match module { 1549 - Some((m, _)) => m.to_doc().append(".").append(name.as_str()), 1550 - None => name.to_doc(), 1889 + Some((m, _)) => m 1890 + .to_doc(&arena) 1891 + .append(&arena, ".") 1892 + .append(&arena, name.as_str()), 1893 + None => name.to_doc(&arena), 1551 1894 }; 1552 1895 1553 1896 let pieces = std::iter::once(RecordUpdatePiece::Record(record)) ··· 1561 1904 let comments = self.pop_comments(argument.location.start); 1562 1905 let doc = match argument { 1563 1906 _ if argument.uses_label_shorthand() => { 1564 - argument.label.as_str().to_doc().append(":") 1907 + argument.label.as_str().to_doc(&arena).append(&arena, ":") 1565 1908 } 1566 1909 _ => argument 1567 1910 .label 1568 1911 .as_str() 1569 - .to_doc() 1570 - .append(": ") 1571 - .append(self.const_expr(&argument.value)) 1572 - .group(), 1912 + .to_doc(&arena) 1913 + .append(&arena, ": ") 1914 + .append(&arena, self.const_expr(arena, cache, &argument.value)) 1915 + .group(&arena), 1573 1916 }; 1574 - commented(doc, comments) 1917 + commented(arena, cache, doc, comments) 1575 1918 } 1576 1919 RecordUpdatePiece::Record(record) => { 1577 1920 let comments = self.pop_comments(record.location.start); 1578 1921 commented( 1579 - "..".to_doc().append(self.const_expr(&record.base)), 1922 + arena, 1923 + cache, 1924 + "..".to_doc(&arena) 1925 + .append(&arena, self.const_expr(arena, cache, &record.base)), 1580 1926 comments, 1581 1927 ) 1582 1928 } ··· 1584 1930 .collect_vec(); 1585 1931 1586 1932 constructor_doc 1587 - .append(self.wrap_arguments(docs, location.end)) 1588 - .group() 1933 + .append( 1934 + &arena, 1935 + self.wrap_arguments(arena, cache, docs, location.end), 1936 + ) 1937 + .group(&arena) 1589 1938 } 1590 1939 1591 1940 pub fn bin_op( 1592 1941 &mut self, 1942 + arena: &'doc DocumentArena<'a, 'doc>, 1943 + cache: &'doc FormatterCache<'a, 'doc>, 1593 1944 name: &'a BinOp, 1594 1945 left: &'a UntypedExpr, 1595 1946 right: &'a UntypedExpr, 1596 1947 nest_steps: bool, 1597 - ) -> Document<'a> { 1598 - let left_side = self.bin_op_side(name, left, nest_steps); 1948 + ) -> Document<'a, 'doc> { 1949 + let left_side = self.bin_op_side(arena, cache, name, left, nest_steps); 1599 1950 1600 1951 let comments = self.pop_comments(right.start_byte_index()); 1601 - let name_doc = break_("", " ").append(commented(name.to_doc(), comments)); 1952 + let name_doc = cache.breakable_space.append( 1953 + &arena, 1954 + commented(arena, cache, name.to_doc(&arena), comments), 1955 + ); 1602 1956 1603 - let right_side = self.bin_op_side(name, right, nest_steps); 1957 + let right_side = self.bin_op_side(arena, cache, name, right, nest_steps); 1604 1958 1605 1959 left_side 1606 - .append(if nest_steps { 1607 - name_doc.nest(INDENT) 1608 - } else { 1609 - name_doc 1610 - }) 1611 - .append(" ") 1612 - .append(right_side) 1960 + .append( 1961 + &arena, 1962 + if nest_steps { 1963 + name_doc.nest(&arena, INDENT) 1964 + } else { 1965 + name_doc 1966 + }, 1967 + ) 1968 + .append(&arena, " ") 1969 + .append(&arena, right_side) 1613 1970 } 1614 1971 1615 1972 fn bin_op_side( 1616 1973 &mut self, 1974 + arena: &'doc DocumentArena<'a, 'doc>, 1975 + cache: &'doc FormatterCache<'a, 'doc>, 1617 1976 operator: &'a BinOp, 1618 1977 side: &'a UntypedExpr, 1619 1978 nest_steps: bool, 1620 - ) -> Document<'a> { 1979 + ) -> Document<'a, 'doc> { 1621 1980 let side_doc = match side { 1622 - UntypedExpr::String { value, .. } => self.bin_op_string(value), 1981 + UntypedExpr::String { value, .. } => self.bin_op_string(arena, cache, value), 1623 1982 UntypedExpr::BinOp { 1624 1983 operator, 1625 1984 left, 1626 1985 right, 1627 1986 .. 1628 - } => self.bin_op(operator, left, right, nest_steps), 1987 + } => self.bin_op(arena, cache, operator, left, right, nest_steps), 1629 1988 UntypedExpr::Int { .. } 1630 1989 | UntypedExpr::Float { .. } 1631 1990 | UntypedExpr::Block { .. } ··· 1644 2003 | UntypedExpr::BitArray { .. } 1645 2004 | UntypedExpr::RecordUpdate { .. } 1646 2005 | UntypedExpr::NegateBool { .. } 1647 - | UntypedExpr::NegateInt { .. } => self.expr(side), 2006 + | UntypedExpr::NegateInt { .. } => self.expr(arena, cache, side), 1648 2007 }; 1649 2008 match side.bin_op_name() { 1650 2009 // In case the other side is a binary operation as well and it can ··· 1658 2017 // broken independently of other pieces of the binary operations 1659 2018 // chain. 1660 2019 _ => self.operator_side( 1661 - side_doc.group(), 2020 + arena, 2021 + cache, 2022 + side_doc.group(&arena), 1662 2023 operator.precedence(), 1663 2024 side.bin_op_precedence(), 1664 2025 ), 1665 2026 } 1666 2027 } 1667 2028 1668 - pub fn operator_side(&self, doc: Document<'a>, op: u8, side: u8) -> Document<'a> { 2029 + pub fn operator_side( 2030 + &self, 2031 + arena: &'doc DocumentArena<'a, 'doc>, 2032 + cache: &'doc FormatterCache<'a, 'doc>, 2033 + doc: Document<'a, 'doc>, 2034 + op: u8, 2035 + side: u8, 2036 + ) -> Document<'a, 'doc> { 1669 2037 if op > side { 1670 - wrap_block(doc).group() 2038 + wrap_block(arena, cache, doc).group(&arena) 1671 2039 } else { 1672 2040 doc 1673 2041 } ··· 1707 2075 .is_ok() 1708 2076 } 1709 2077 1710 - fn pipeline(&mut self, expressions: &'a Vec1<UntypedExpr>, nest_pipe: bool) -> Document<'a> { 2078 + fn pipeline( 2079 + &mut self, 2080 + arena: &'doc DocumentArena<'a, 'doc>, 2081 + cache: &'doc FormatterCache<'a, 'doc>, 2082 + expressions: &'a Vec1<UntypedExpr>, 2083 + nest_pipe: bool, 2084 + ) -> Document<'a, 'doc> { 1711 2085 let mut docs = Vec::with_capacity(expressions.len() * 3); 1712 2086 let first = expressions.first(); 1713 2087 let first_precedence = first.bin_op_precedence(); 1714 - let first = self.expr(first).group(); 1715 - docs.push(self.operator_side(first, 5, first_precedence)); 2088 + let first = self.expr(arena, cache, first).group(&arena); 2089 + docs.push(self.operator_side(arena, cache, first, 5, first_precedence)); 1716 2090 1717 2091 let pipeline_start = expressions.first().location().start; 1718 2092 let pipeline_end = expressions.last().location().end; ··· 1723 2097 let doc = if let UntypedExpr::Fn { kind, body, .. } = expression 1724 2098 && kind.is_capture() 1725 2099 { 1726 - self.fn_capture(body, FnCapturePosition::RightHandSideOfPipe) 2100 + self.fn_capture(arena, cache, body, FnCapturePosition::RightHandSideOfPipe) 1727 2101 } else { 1728 - self.expr(expression) 2102 + self.expr(arena, cache, expression) 1729 2103 }; 1730 - let doc = if nest_pipe { doc.nest(INDENT) } else { doc }; 2104 + let doc = if nest_pipe { 2105 + doc.nest(&arena, INDENT) 2106 + } else { 2107 + doc 2108 + }; 1731 2109 let space = if try_to_keep_on_one_line { 1732 - break_("", " ") 2110 + cache.breakable_space 1733 2111 } else { 1734 - line() 2112 + cache.line 1735 2113 }; 1736 - let pipe = space.append(commented("|> ".to_doc(), comments)); 1737 - let pipe = if nest_pipe { pipe.nest(INDENT) } else { pipe }; 2114 + let pipe = space.append( 2115 + &arena, 2116 + commented(arena, cache, "|> ".to_doc(&arena), comments), 2117 + ); 2118 + let pipe = if nest_pipe { 2119 + pipe.nest(&arena, INDENT) 2120 + } else { 2121 + pipe 2122 + }; 1738 2123 docs.push(pipe); 1739 - docs.push(self.operator_side(doc, 4, expression.bin_op_precedence())); 2124 + docs.push(self.operator_side(arena, cache, doc, 4, expression.bin_op_precedence())); 1740 2125 } 1741 2126 1742 2127 if try_to_keep_on_one_line { 1743 - docs.to_doc() 2128 + docs.to_doc(&arena) 1744 2129 } else { 1745 - docs.to_doc().force_break() 2130 + docs.to_doc(&arena).force_break(&arena) 1746 2131 } 1747 2132 } 1748 2133 1749 2134 fn fn_capture( 1750 2135 &mut self, 2136 + arena: &'doc DocumentArena<'a, 'doc>, 2137 + cache: &'doc FormatterCache<'a, 'doc>, 1751 2138 call: &'a [UntypedStatement], 1752 2139 position: FnCapturePosition, 1753 - ) -> Document<'a> { 2140 + ) -> Document<'a, 'doc> { 1754 2141 // The body of a capture being multiple statements shouldn't be possible... 1755 2142 if call.len() != 1 { 1756 2143 panic!("Function capture found not to have a single statement call"); ··· 1781 2168 ( 1782 2169 FnCapturePosition::RightHandSideOfPipe | FnCapturePosition::EverywhereElse, 1783 2170 [argument], 1784 - ) if argument.is_capture_hole() && argument.label.is_none() => self.expr(fun), 2171 + ) if argument.is_capture_hole() && argument.label.is_none() => { 2172 + self.expr(arena, cache, fun) 2173 + } 1785 2174 1786 2175 // The capture is on the right hand side of a pipe and its first 1787 2176 // argument it an unlabelled capture hole: ··· 1795 2184 (FnCapturePosition::RightHandSideOfPipe, [argument, rest @ ..]) 1796 2185 if argument.is_capture_hole() && argument.label.is_none() => 1797 2186 { 1798 - let expression = self.expr(fun); 2187 + let expression = self.expr(arena, cache, fun); 1799 2188 let arity = rest.len(); 1800 2189 self.append_inlinable_wrapped_arguments( 2190 + arena, 2191 + cache, 1801 2192 expression, 1802 2193 rest, 1803 2194 location, 1804 2195 |argument| is_breakable_argument(&argument.value, rest.len()), 1805 - |self_, argument| self_.call_arg(argument, arity), 2196 + |self_, argument| self_.call_arg(arena, cache, argument, arity), 1806 2197 ) 1807 2198 } 1808 2199 ··· 1813 2204 FnCapturePosition::RightHandSideOfPipe | FnCapturePosition::EverywhereElse, 1814 2205 arguments, 1815 2206 ) => { 1816 - let expression = self.expr(fun); 2207 + let expression = self.expr(arena, cache, fun); 1817 2208 let arity = arguments.len(); 1818 2209 self.append_inlinable_wrapped_arguments( 2210 + arena, 2211 + cache, 1819 2212 expression, 1820 2213 arguments, 1821 2214 location, 1822 2215 |argument| is_breakable_argument(&argument.value, arguments.len()), 1823 - |self_, argument| self_.call_arg(argument, arity), 2216 + |self_, argument| self_.call_arg(arena, cache, argument, arity), 1824 2217 ) 1825 2218 } 1826 2219 } 1827 2220 } 1828 2221 1829 - pub fn record_constructor<A>(&mut self, constructor: &'a RecordConstructor<A>) -> Document<'a> { 2222 + pub fn record_constructor<A>( 2223 + &mut self, 2224 + arena: &'doc DocumentArena<'a, 'doc>, 2225 + cache: &'doc FormatterCache<'a, 'doc>, 2226 + constructor: &'a RecordConstructor<A>, 2227 + ) -> Document<'a, 'doc> { 1830 2228 let comments = self.pop_comments(constructor.location.start); 1831 - let doc_comments = self.doc_comments(constructor.location.start); 2229 + let doc_comments = self.doc_comments(arena, cache, constructor.location.start); 1832 2230 let attributes = AttributesPrinter::new() 1833 2231 .set_deprecation(&constructor.deprecation) 1834 - .to_doc(); 2232 + .to_doc(&arena); 1835 2233 1836 2234 let doc = if constructor.arguments.is_empty() { 1837 2235 if self.any_comments(constructor.location.end) { 1838 2236 attributes 1839 - .append(constructor.name.as_str().to_doc()) 1840 - .append(self.wrap_arguments(vec![], constructor.location.end)) 1841 - .group() 2237 + .append(&arena, constructor.name.as_str().to_doc(&arena)) 2238 + .append( 2239 + &arena, 2240 + self.wrap_arguments(arena, cache, vec![], constructor.location.end), 2241 + ) 2242 + .group(&arena) 1842 2243 } else { 1843 - attributes.append(constructor.name.as_str().to_doc()) 2244 + attributes.append(&arena, constructor.name.as_str().to_doc(&arena)) 1844 2245 } 1845 2246 } else { 1846 2247 let arguments = constructor ··· 1855 2256 }| { 1856 2257 let arg_comments = self.pop_comments(location.start); 1857 2258 let argument = match label { 1858 - Some((_, label)) => { 1859 - label.to_doc().append(": ").append(self.type_ast(ast)) 1860 - } 1861 - None => self.type_ast(ast), 2259 + Some((_, label)) => label 2260 + .to_doc(&arena) 2261 + .append(&arena, ": ") 2262 + .append(&arena, self.type_ast(arena, cache, ast)), 2263 + None => self.type_ast(arena, cache, ast), 1862 2264 }; 1863 2265 1864 2266 commented( 1865 - self.doc_comments(location.start).append(argument).group(), 2267 + arena, 2268 + cache, 2269 + self.doc_comments(arena, cache, location.start) 2270 + .append(&arena, argument) 2271 + .group(&arena), 1866 2272 arg_comments, 1867 2273 ) 1868 2274 }, ··· 1870 2276 .collect_vec(); 1871 2277 1872 2278 attributes 1873 - .append(constructor.name.as_str().to_doc()) 2279 + .append(&arena, constructor.name.as_str().to_doc(&arena)) 1874 2280 .append( 1875 - self.wrap_arguments(arguments, constructor.location.end) 1876 - .group(), 2281 + &arena, 2282 + self.wrap_arguments(arena, cache, arguments, constructor.location.end) 2283 + .group(&arena), 1877 2284 ) 1878 2285 }; 1879 2286 1880 - commented(doc_comments.append(doc).group(), comments) 2287 + commented( 2288 + arena, 2289 + cache, 2290 + doc_comments.append(&arena, doc).group(&arena), 2291 + comments, 2292 + ) 1881 2293 } 1882 2294 1883 - pub fn custom_type<A>(&mut self, type_: &'a CustomType<A>) -> Document<'a> { 2295 + pub fn custom_type<A>( 2296 + &mut self, 2297 + arena: &'doc DocumentArena<'a, 'doc>, 2298 + cache: &'doc FormatterCache<'a, 'doc>, 2299 + type_: &'a CustomType<A>, 2300 + ) -> Document<'a, 'doc> { 1884 2301 let CustomType { 1885 2302 location, 1886 2303 end_position, ··· 1904 2321 .set_internal(*publicity) 1905 2322 .set_external_erlang(external_erlang) 1906 2323 .set_external_javascript(external_javascript) 1907 - .to_doc(); 2324 + .to_doc(&arena); 1908 2325 1909 2326 let doc = attributes 1910 - .append(pub_(*publicity)) 1911 - .append(if *opaque { "opaque type " } else { "type " }) 1912 - .append(if parameters.is_empty() { 1913 - name.clone().to_doc() 1914 - } else { 1915 - let arguments = type_ 1916 - .parameters 1917 - .iter() 1918 - .map(|(_, e)| e.to_doc()) 1919 - .collect_vec(); 1920 - type_ 1921 - .name 1922 - .clone() 1923 - .to_doc() 1924 - .append(self.wrap_arguments(arguments, location.end)) 1925 - .group() 1926 - }); 2327 + .append(&arena, pub_(arena, cache, *publicity)) 2328 + .append(&arena, if *opaque { "opaque type " } else { "type " }) 2329 + .append( 2330 + &arena, 2331 + if parameters.is_empty() { 2332 + name.clone().to_doc(&arena) 2333 + } else { 2334 + let arguments = type_ 2335 + .parameters 2336 + .iter() 2337 + .map(|(_, e)| e.to_doc(&arena)) 2338 + .collect_vec(); 2339 + type_ 2340 + .name 2341 + .clone() 2342 + .to_doc(&arena) 2343 + .append( 2344 + &arena, 2345 + self.wrap_arguments(arena, cache, arguments, location.end), 2346 + ) 2347 + .group(&arena) 2348 + }, 2349 + ); 1927 2350 1928 2351 if constructors.is_empty() { 1929 2352 return doc; 1930 2353 } 1931 - let doc = doc.append(" {"); 2354 + let doc = doc.append(&arena, " {"); 1932 2355 1933 - let inner = concat(constructors.iter().map(|c| { 2356 + let inner = arena.concat(constructors.iter().map(|c| { 1934 2357 if self.pop_empty_lines(c.location.start) { 1935 - lines(2) 2358 + cache.two_lines 1936 2359 } else { 1937 - line() 2360 + cache.line 1938 2361 } 1939 - .append(self.record_constructor(c)) 2362 + .append(&arena, self.record_constructor(arena, cache, c)) 1940 2363 })); 1941 2364 1942 2365 // Add any trailing comments 1943 - let inner = match printed_comments(self.pop_comments(*end_position), false) { 1944 - Some(comments) => inner.append(line()).append(comments), 2366 + let inner = match printed_comments(arena, cache, self.pop_comments(*end_position), false) { 2367 + Some(comments) => inner.append(&arena, cache.line).append(&arena, comments), 1945 2368 None => inner, 1946 2369 } 1947 - .nest(INDENT) 1948 - .group(); 2370 + .nest(&arena, INDENT) 2371 + .group(&arena); 1949 2372 1950 - doc.append(inner).append(line()).append("}") 2373 + doc.append(&arena, inner) 2374 + .append(&arena, cache.line) 2375 + .append(&arena, "}") 1951 2376 } 1952 2377 1953 - fn call_arg(&mut self, argument: &'a CallArg<UntypedExpr>, arity: usize) -> Document<'a> { 1954 - self.format_call_arg(argument, expr_call_arg_formatting, |this, value| { 1955 - this.comma_separated_item(value, arity) 1956 - }) 2378 + fn call_arg( 2379 + &mut self, 2380 + arena: &'doc DocumentArena<'a, 'doc>, 2381 + cache: &'doc FormatterCache<'a, 'doc>, 2382 + argument: &'a CallArg<UntypedExpr>, 2383 + arity: usize, 2384 + ) -> Document<'a, 'doc> { 2385 + self.format_call_arg( 2386 + arena, 2387 + cache, 2388 + argument, 2389 + expr_call_arg_formatting, 2390 + |this, value| this.comma_separated_item(arena, cache, value, arity), 2391 + ) 1957 2392 } 1958 2393 1959 2394 fn format_call_arg<A, F, G>( 1960 2395 &mut self, 2396 + arena: &'doc DocumentArena<'a, 'doc>, 2397 + cache: &'doc FormatterCache<'a, 'doc>, 1961 2398 argument: &'a CallArg<A>, 1962 2399 figure_formatting: F, 1963 2400 format_value: G, 1964 - ) -> Document<'a> 2401 + ) -> Document<'a, 'doc> 1965 2402 where 1966 2403 F: Fn(&'a CallArg<A>) -> CallArgFormatting<'a, A>, 1967 - G: Fn(&mut Self, &'a A) -> Document<'a>, 2404 + G: Fn(&mut Self, &'a A) -> Document<'a, 'doc>, 1968 2405 { 1969 2406 match figure_formatting(argument) { 1970 2407 CallArgFormatting::Unlabelled(value) => format_value(self, value), 1971 2408 CallArgFormatting::ShorthandLabelled(label) => { 1972 2409 let comments = self.pop_comments(argument.location.start); 1973 - let label = label.as_ref().to_doc().append(":"); 1974 - commented(label, comments) 2410 + let label = label.as_ref().to_doc(&arena).append(&arena, ":"); 2411 + commented(arena, cache, label, comments) 1975 2412 } 1976 2413 CallArgFormatting::Labelled(label, value) => { 1977 2414 let comments = self.pop_comments(argument.location.start); 1978 - let label = label.as_ref().to_doc().append(": "); 2415 + let label = label.as_ref().to_doc(&arena).append(&arena, ": "); 1979 2416 let value = format_value(self, value); 1980 - commented(label, comments).append(value) 2417 + commented(arena, cache, label, comments).append(&arena, value) 1981 2418 } 1982 2419 } 1983 2420 } 1984 2421 1985 - fn record_update_arg(&mut self, argument: &'a UntypedRecordUpdateArg) -> Document<'a> { 2422 + fn record_update_arg( 2423 + &mut self, 2424 + arena: &'doc DocumentArena<'a, 'doc>, 2425 + cache: &'doc FormatterCache<'a, 'doc>, 2426 + argument: &'a UntypedRecordUpdateArg, 2427 + ) -> Document<'a, 'doc> { 1986 2428 let comments = self.pop_comments(argument.location.start); 1987 2429 match argument { 1988 2430 // Argument supplied with a label shorthand. 1989 - _ if argument.uses_label_shorthand() => { 1990 - commented(argument.label.as_str().to_doc().append(":"), comments) 1991 - } 2431 + _ if argument.uses_label_shorthand() => commented( 2432 + arena, 2433 + cache, 2434 + argument.label.as_str().to_doc(&arena).append(&arena, ":"), 2435 + comments, 2436 + ), 1992 2437 // Labelled argument. 1993 2438 _ => { 1994 2439 let doc = argument 1995 2440 .label 1996 2441 .as_str() 1997 - .to_doc() 1998 - .append(": ") 1999 - .append(self.expr(&argument.value)) 2000 - .group(); 2442 + .to_doc(&arena) 2443 + .append(&arena, ": ") 2444 + .append(&arena, self.expr(arena, cache, &argument.value)) 2445 + .group(&arena); 2001 2446 2002 2447 if argument.value.is_binop() || argument.value.is_pipeline() { 2003 - commented(doc, comments).nest(INDENT) 2448 + commented(arena, cache, doc, comments).nest(&arena, INDENT) 2004 2449 } else { 2005 - commented(doc, comments) 2450 + commented(arena, cache, doc, comments) 2006 2451 } 2007 2452 } 2008 2453 } 2009 2454 } 2010 2455 2011 - fn tuple_index(&mut self, tuple: &'a UntypedExpr, index: u64) -> Document<'a> { 2456 + fn tuple_index( 2457 + &mut self, 2458 + arena: &'doc DocumentArena<'a, 'doc>, 2459 + cache: &'doc FormatterCache<'a, 'doc>, 2460 + tuple: &'a UntypedExpr, 2461 + index: u64, 2462 + ) -> Document<'a, 'doc> { 2012 2463 // In case we have a block with a single variable tuple access we 2013 2464 // remove that redundant wrapper: 2014 2465 // ··· 2023 2474 // it: `#(1, #(2, 3)).1.0` is a syntax error at the moment. 2024 2475 if !inner.is_tuple() => 2025 2476 { 2026 - self.expr(tuple) 2477 + self.expr(arena,cache,tuple) 2027 2478 } 2028 - _ => self.expr(tuple), 2479 + _ => self.expr(arena,cache,tuple), 2029 2480 } 2030 2481 } else { 2031 - self.expr(tuple) 2482 + self.expr(arena, cache, tuple) 2032 2483 } 2033 - .append(".") 2034 - .append(index) 2484 + .append(&arena, ".") 2485 + .append(&arena, index) 2035 2486 } 2036 2487 2037 - fn case_clause_value(&mut self, expression: &'a UntypedExpr) -> Document<'a> { 2488 + fn case_clause_value( 2489 + &mut self, 2490 + arena: &'doc DocumentArena<'a, 'doc>, 2491 + cache: &'doc FormatterCache<'a, 'doc>, 2492 + expression: &'a UntypedExpr, 2493 + ) -> Document<'a, 'doc> { 2038 2494 match expression { 2039 2495 UntypedExpr::Fn { .. } 2040 2496 | UntypedExpr::List { .. } 2041 2497 | UntypedExpr::Tuple { .. } 2042 2498 | UntypedExpr::BitArray { .. } => { 2043 2499 let expression_comments = self.pop_comments(expression.location().start); 2044 - let expression_doc = self.expr(expression); 2045 - match printed_comments(expression_comments, true) { 2046 - Some(comments) => line().append(comments).append(expression_doc).nest(INDENT), 2047 - None => " ".to_doc().append(expression_doc), 2500 + let expression_doc = self.expr(arena, cache, expression); 2501 + match printed_comments(arena, cache, expression_comments, true) { 2502 + Some(comments) => arena 2503 + .line() 2504 + .append(&arena, comments) 2505 + .append(&arena, expression_doc) 2506 + .nest(&arena, INDENT), 2507 + None => cache.space.append(&arena, expression_doc), 2048 2508 } 2049 2509 } 2050 2510 2051 - UntypedExpr::Case { .. } => line().append(self.expr(expression)).nest(INDENT), 2511 + UntypedExpr::Case { .. } => arena 2512 + .line() 2513 + .append(&arena, self.expr(arena, cache, expression)) 2514 + .nest(&arena, INDENT), 2052 2515 2053 2516 UntypedExpr::Block { 2054 2517 statements, 2055 2518 location, 2056 2519 .. 2057 - } => " ".to_doc().append(self.block(location, statements, true)), 2520 + } => cache 2521 + .space 2522 + .append(&arena, self.block(arena, cache, location, statements, true)), 2058 2523 2059 2524 UntypedExpr::Int { .. } 2060 2525 | UntypedExpr::Float { .. } ··· 2070 2535 | UntypedExpr::Echo { .. } 2071 2536 | UntypedExpr::RecordUpdate { .. } 2072 2537 | UntypedExpr::NegateBool { .. } 2073 - | UntypedExpr::NegateInt { .. } => break_("", " ") 2074 - .append(self.expr(expression).group()) 2075 - .nest(INDENT), 2538 + | UntypedExpr::NegateInt { .. } => cache 2539 + .breakable_space 2540 + .append(&arena, self.expr(arena, cache, expression).group(&arena)) 2541 + .nest(&arena, INDENT), 2076 2542 } 2077 - .next_break_fits(NextBreakFitsMode::Disabled) 2078 - .group() 2543 + .next_break_fits(&arena, NextBreakFitsMode::Disabled) 2544 + .group(&arena) 2079 2545 } 2080 2546 2081 - fn assigned_value(&mut self, expression: &'a UntypedExpr) -> Document<'a> { 2547 + fn assigned_value( 2548 + &mut self, 2549 + arena: &'doc DocumentArena<'a, 'doc>, 2550 + cache: &'doc FormatterCache<'a, 'doc>, 2551 + expression: &'a UntypedExpr, 2552 + ) -> Document<'a, 'doc> { 2082 2553 match expression { 2083 - UntypedExpr::Case { .. } => " ".to_doc().append(self.expr(expression)).group(), 2554 + UntypedExpr::Case { .. } => cache 2555 + .space 2556 + .append(&arena, self.expr(arena, cache, expression)) 2557 + .group(&arena), 2084 2558 UntypedExpr::Int { .. } 2085 2559 | UntypedExpr::Float { .. } 2086 2560 | UntypedExpr::String { .. } ··· 2100 2574 | UntypedExpr::BitArray { .. } 2101 2575 | UntypedExpr::RecordUpdate { .. } 2102 2576 | UntypedExpr::NegateBool { .. } 2103 - | UntypedExpr::NegateInt { .. } => self.case_clause_value(expression), 2577 + | UntypedExpr::NegateInt { .. } => self.case_clause_value(arena, cache, expression), 2104 2578 } 2105 2579 } 2106 2580 2107 - fn clause(&mut self, clause: &'a UntypedClause, index: u32) -> Document<'a> { 2581 + fn clause( 2582 + &mut self, 2583 + arena: &'doc DocumentArena<'a, 'doc>, 2584 + cache: &'doc FormatterCache<'a, 'doc>, 2585 + clause: &'a UntypedClause, 2586 + index: u32, 2587 + ) -> Document<'a, 'doc> { 2108 2588 let space_before = self.pop_empty_lines(clause.location.start); 2109 2589 let comments = self.pop_comments(clause.location.start); 2110 2590 2111 2591 let clause_doc = match &clause.guard { 2112 - None => self.alternative_patterns(clause), 2592 + None => self.alternative_patterns(arena, cache, clause), 2113 2593 Some(guard) => self 2114 - .alternative_patterns(clause) 2115 - .append(break_("", " ").nest(INDENT)) 2116 - .append("if ") 2117 - .append(self.clause_guard(guard).group().nest(INDENT)), 2594 + .alternative_patterns(arena, cache, clause) 2595 + .append(&arena, cache.breakable_space.nest(&arena, INDENT)) 2596 + .append(&arena, "if ") 2597 + .append( 2598 + &arena, 2599 + self.clause_guard(arena, cache, guard) 2600 + .group(&arena) 2601 + .nest(&arena, INDENT), 2602 + ), 2118 2603 }; 2119 2604 2120 2605 // In case there's a guard or multiple subjects, if we decide to break ··· 2135 2620 let has_guard = clause.guard.is_some(); 2136 2621 let has_multiple_subjects = clause.pattern.len() > 1; 2137 2622 let arrow_break = if has_guard || has_multiple_subjects { 2138 - break_("", " ") 2623 + cache.breakable_space 2139 2624 } else { 2140 - " ".to_doc() 2625 + cache.space 2141 2626 }; 2142 2627 2143 - let clause_doc = docvec![clause_doc, arrow_break, "->"] 2144 - .group() 2145 - .append(self.case_clause_value(&clause.then)) 2146 - .group(); 2628 + let clause_doc = docvec_ref_arena![arena, clause_doc, arrow_break, "->"] 2629 + .group(&arena) 2630 + .append(&arena, self.case_clause_value(arena, cache, &clause.then)) 2631 + .group(&arena); 2147 2632 2148 - let clause_doc = match printed_comments(comments, false) { 2149 - Some(comments) => comments.append(line()).append(clause_doc), 2633 + let clause_doc = match printed_comments(arena, cache, comments, false) { 2634 + Some(comments) => comments 2635 + .append(&arena, cache.line) 2636 + .append(&arena, clause_doc), 2150 2637 None => clause_doc, 2151 2638 }; 2152 2639 2153 2640 if index == 0 { 2154 2641 clause_doc 2155 2642 } else if space_before { 2156 - lines(2).append(clause_doc) 2643 + cache.two_lines.append(&arena, clause_doc) 2157 2644 } else { 2158 - line().append(clause_doc) 2645 + cache.line.append(&arena, clause_doc) 2159 2646 } 2160 2647 } 2161 2648 2162 - fn alternative_patterns(&mut self, clause: &'a UntypedClause) -> Document<'a> { 2649 + fn alternative_patterns( 2650 + &mut self, 2651 + arena: &'doc DocumentArena<'a, 'doc>, 2652 + cache: &'doc FormatterCache<'a, 'doc>, 2653 + clause: &'a UntypedClause, 2654 + ) -> Document<'a, 'doc> { 2163 2655 let has_guard = clause.guard.is_some(); 2164 2656 let has_multiple_subjects = clause.pattern.len() > 1; 2165 2657 ··· 2178 2670 // } 2179 2671 // ``` 2180 2672 let alternatives_separator = if has_guard && !has_multiple_subjects { 2181 - break_("", " ").nest(INDENT).append("| ") 2673 + cache 2674 + .breakable_space 2675 + .nest(&arena, INDENT) 2676 + .append(&arena, "| ") 2182 2677 } else { 2183 - break_("", " ").append("| ") 2678 + cache.breakable_space.append(&arena, "| ") 2184 2679 }; 2185 2680 2186 2681 let alternative_patterns = std::iter::once(&clause.pattern) ··· 2207 2702 // of indentation: https://github.com/gleam-lang/gleam/issues/2940. 2208 2703 let is_first_pattern = pattern_index == 0; 2209 2704 let is_first_pattern_of_clause = is_first_pattern && is_first_alternative; 2210 - let pattern_doc = self.pattern(pattern); 2705 + let pattern_doc = self.pattern(arena, cache, pattern); 2211 2706 if is_first_pattern_of_clause { 2212 2707 pattern_doc 2213 2708 } else { 2214 - pattern_doc.nest(INDENT) 2709 + pattern_doc.nest(&arena, INDENT) 2215 2710 } 2216 2711 }); 2217 2712 // We join all subjects with a breakable comma (that's also 2218 2713 // going to be nested) and make the subjects into a group to 2219 2714 // make sure the formatter tries to keep them on a single line. 2220 - join(pattern_docs, break_(",", ", ").nest(INDENT)).group() 2715 + arena 2716 + .join(pattern_docs, arena.break_(",", ", ").nest(&arena, INDENT)) 2717 + .group(&arena) 2221 2718 }); 2222 - join(alternative_patterns, alternatives_separator) 2719 + arena.join(alternative_patterns, alternatives_separator) 2223 2720 } 2224 2721 2225 2722 fn list( 2226 2723 &mut self, 2724 + arena: &'doc DocumentArena<'a, 'doc>, 2725 + cache: &'doc FormatterCache<'a, 'doc>, 2227 2726 elements: &'a [UntypedExpr], 2228 2727 tail: Option<&'a UntypedExpr>, 2229 2728 location: &SrcSpan, 2230 - ) -> Document<'a> { 2729 + ) -> Document<'a, 'doc> { 2231 2730 if elements.is_empty() { 2232 2731 return match tail { 2233 - Some(tail) => self.expr(tail), 2732 + Some(tail) => self.expr(arena, cache, tail), 2234 2733 // We take all comments that come _before_ the end of the list, 2235 2734 // that is all comments that are inside "[" and "]", if there's 2236 2735 // any comment we want to put it inside the empty list! 2237 - None => match printed_comments(self.pop_comments(location.end), false) { 2238 - None => "[]".to_doc(), 2239 - Some(comments) => "[" 2240 - .to_doc() 2241 - .append(break_("", "").nest(INDENT)) 2242 - .append(comments) 2243 - .append(break_("", "")) 2244 - .append("]") 2245 - // vvv We want to make sure the comments are on a separate 2246 - // line from the opening and closing brackets so we 2247 - // force the breaks to be split on newlines. 2248 - .force_break(), 2249 - }, 2736 + None => { 2737 + let comments = self.pop_comments(location.end); 2738 + match printed_comments(arena, cache, comments, false) { 2739 + None => "[]".to_doc(&arena), 2740 + Some(comments) => "[" 2741 + .to_doc(&arena) 2742 + .append(&arena, cache.empty_break.nest(&arena, INDENT)) 2743 + .append(&arena, comments) 2744 + .append(&arena, cache.empty_break) 2745 + .append(&arena, "]") 2746 + // vvv We want to make sure the comments are on a separate 2747 + // line from the opening and closing brackets so we 2748 + // force the breaks to be split on newlines. 2749 + .force_break(&arena), 2750 + } 2751 + } 2250 2752 }; 2251 2753 } 2252 2754 ··· 2258 2760 ); 2259 2761 2260 2762 let comma = match list_packing { 2261 - ItemsPacking::FitMultiplePerLine => flex_break(",", ", "), 2262 - ItemsPacking::FitOnePerLine | ItemsPacking::BreakOnePerLine => break_(",", ", "), 2763 + ItemsPacking::FitMultiplePerLine => arena.flex_break(",", ", "), 2764 + ItemsPacking::FitOnePerLine | ItemsPacking::BreakOnePerLine => arena.break_(",", ", "), 2263 2765 }; 2264 2766 2265 2767 let list_size = elements.len() ··· 2268 2770 None => 0, 2269 2771 }; 2270 2772 2271 - let mut elements_doc = nil(); 2773 + let mut elements_doc = cache.nil; 2272 2774 for element in elements.iter() { 2273 2775 let empty_lines = self.pop_empty_lines(element.location().start); 2274 - let element_doc = self.comma_separated_item(element, list_size); 2776 + let element_doc = self.comma_separated_item(arena, cache, element, list_size); 2275 2777 2276 - elements_doc = if elements_doc.is_empty() { 2778 + elements_doc = if elements_doc.is_empty(&arena) { 2277 2779 element_doc 2278 2780 } else if empty_lines { 2279 2781 // If there's empty lines before the list item we want to add an 2280 2782 // empty line here. Notice how we're making sure no nesting is 2281 2783 // added after the comma, otherwise we would be adding needless 2282 2784 // whitespace in the empty line! 2283 - docvec![ 2785 + docvec_ref_arena![ 2786 + arena, 2284 2787 elements_doc, 2285 - comma.clone().set_nesting(0), 2286 - line(), 2788 + comma.clone().set_nesting(&arena, 0), 2789 + cache.line, 2287 2790 element_doc 2288 2791 ] 2289 2792 } else { 2290 - docvec![elements_doc, comma.clone(), element_doc] 2793 + docvec_ref_arena![arena, elements_doc, comma.clone(), element_doc] 2291 2794 }; 2292 2795 } 2293 - elements_doc = elements_doc.next_break_fits(NextBreakFitsMode::Disabled); 2796 + elements_doc = elements_doc.next_break_fits(&arena, NextBreakFitsMode::Disabled); 2294 2797 2295 - let doc = break_("[", "[").append(elements_doc); 2798 + let doc = arena.break_("[", "[").append(&arena, elements_doc); 2296 2799 // We need to keep the last break aside and do not add it immediately 2297 2800 // because in case there's a final comment before the closing square 2298 2801 // bracket we want to add indentation (to just that break). Otherwise, 2299 2802 // the final comment would be less indented than list's elements. 2300 2803 let (doc, last_break) = match tail { 2301 - None => (doc.nest(INDENT), break_(",", "")), 2804 + None => (doc.nest(&arena, INDENT), arena.break_(",", "")), 2302 2805 2303 2806 Some(tail) => { 2304 2807 let comments = self.pop_comments(tail.location().start); 2305 - let tail = commented(docvec!["..", self.expr(tail)], comments); 2808 + let tail = commented( 2809 + arena, 2810 + cache, 2811 + docvec_ref_arena![arena, "..", self.expr(arena, cache, tail)], 2812 + comments, 2813 + ); 2306 2814 ( 2307 - doc.append(break_(",", ", ")).append(tail).nest(INDENT), 2308 - break_("", ""), 2815 + doc.append(&arena, arena.break_(",", ", ")) 2816 + .append(&arena, tail) 2817 + .nest(&arena, INDENT), 2818 + cache.empty_break, 2309 2819 ) 2310 2820 } 2311 2821 }; ··· 2316 2826 // of moving those out of the list. 2317 2827 // Otherwise those would be moved out of the list. 2318 2828 let comments = self.pop_comments(location.end); 2319 - let doc = match printed_comments(comments, false) { 2320 - None => doc.append(last_break).append("]"), 2829 + let doc = match printed_comments(arena, cache, comments, false) { 2830 + None => doc.append(&arena, last_break).append(&arena, "]"), 2321 2831 Some(comment) => doc 2322 - .append(last_break.nest(INDENT)) 2832 + .append(&arena, last_break.nest(&arena, INDENT)) 2323 2833 // ^ See how here we're adding the missing indentation to the 2324 2834 // final break so that the final comment is as indented as the 2325 2835 // list's items. 2326 - .append(comment) 2327 - .append(line()) 2328 - .append("]") 2329 - .force_break(), 2836 + .append(&arena, comment) 2837 + .append(&arena, cache.line) 2838 + .append(&arena, "]") 2839 + .force_break(&arena), 2330 2840 }; 2331 2841 2332 2842 match list_packing { 2333 - ItemsPacking::FitOnePerLine | ItemsPacking::FitMultiplePerLine => doc.group(), 2334 - ItemsPacking::BreakOnePerLine => doc.force_break(), 2843 + ItemsPacking::FitOnePerLine | ItemsPacking::FitMultiplePerLine => doc.group(&arena), 2844 + ItemsPacking::BreakOnePerLine => doc.force_break(&arena), 2335 2845 } 2336 2846 } 2337 2847 ··· 2434 2944 /// example as a list item, a tuple item or as an argument of a function call. 2435 2945 fn comma_separated_item( 2436 2946 &mut self, 2947 + arena: &'doc DocumentArena<'a, 'doc>, 2948 + cache: &'doc FormatterCache<'a, 'doc>, 2437 2949 expression: &'a UntypedExpr, 2438 2950 siblings: usize, 2439 - ) -> Document<'a> { 2951 + ) -> Document<'a, 'doc> { 2440 2952 // If there's more than one item in the comma separated list and there's a 2441 2953 // pipeline or long binary chain, we want to indent those to make it 2442 2954 // easier to tell where one item ends and the other starts. ··· 2449 2961 .. 2450 2962 } if siblings > 1 => { 2451 2963 let comments = self.pop_comments(expression.start_byte_index()); 2452 - let doc = self.bin_op(operator, left, right, true).group(); 2453 - commented(doc, comments) 2964 + let doc = self 2965 + .bin_op(arena, cache, operator, left, right, true) 2966 + .group(&arena); 2967 + commented(arena, cache, doc, comments) 2454 2968 } 2455 2969 UntypedExpr::PipeLine { expressions } if siblings > 1 => { 2456 2970 let comments = self.pop_comments(expression.start_byte_index()); 2457 - let doc = self.pipeline(expressions, true).group(); 2458 - commented(doc, comments) 2971 + let doc = self.pipeline(arena, cache, expressions, true).group(&arena); 2972 + commented(arena, cache, doc, comments) 2459 2973 } 2460 2974 UntypedExpr::Int { .. } 2461 2975 | UntypedExpr::Float { .. } ··· 2477 2991 | UntypedExpr::BitArray { .. } 2478 2992 | UntypedExpr::RecordUpdate { .. } 2479 2993 | UntypedExpr::NegateBool { .. } 2480 - | UntypedExpr::NegateInt { .. } => self.expr(expression).group(), 2994 + | UntypedExpr::NegateInt { .. } => self.expr(arena, cache, expression).group(&arena), 2481 2995 } 2482 2996 } 2483 2997 2484 - fn pattern(&mut self, pattern: &'a UntypedPattern) -> Document<'a> { 2998 + fn pattern( 2999 + &mut self, 3000 + arena: &'doc DocumentArena<'a, 'doc>, 3001 + cache: &'doc FormatterCache<'a, 'doc>, 3002 + pattern: &'a UntypedPattern, 3003 + ) -> Document<'a, 'doc> { 2485 3004 let comments = self.pop_comments(pattern.location().start); 2486 3005 let doc = match pattern { 2487 - Pattern::Int { value, .. } => self.int(value), 3006 + Pattern::Int { value, .. } => self.int(arena, cache, value), 2488 3007 2489 - Pattern::Float { value, .. } => self.float(value), 3008 + Pattern::Float { value, .. } => self.float(arena, cache, value), 2490 3009 2491 - Pattern::String { value, .. } => self.string(value), 3010 + Pattern::String { value, .. } => self.string(arena, cache, value), 2492 3011 2493 - Pattern::Variable { name, .. } => name.to_doc(), 3012 + Pattern::Variable { name, .. } => name.to_doc(&arena), 2494 3013 2495 - Pattern::BitArraySize(size) => self.bit_array_size(size), 3014 + Pattern::BitArraySize(size) => self.bit_array_size(arena, cache, size), 2496 3015 2497 3016 Pattern::Assign { name, pattern, .. } => { 2498 3017 if pattern.is_discard() { 2499 - name.to_doc() 3018 + name.to_doc(&arena) 2500 3019 } else { 2501 - self.pattern(pattern).append(" as ").append(name.as_str()) 3020 + self.pattern(arena, cache, pattern) 3021 + .append(&arena, " as ") 3022 + .append(&arena, name.as_str()) 2502 3023 } 2503 3024 } 2504 3025 2505 - Pattern::Discard { name, .. } => name.to_doc(), 3026 + Pattern::Discard { name, .. } => name.to_doc(&arena), 2506 3027 2507 - Pattern::List { elements, tail, .. } => self.list_pattern(elements, tail), 3028 + Pattern::List { elements, tail, .. } => self.list_pattern(arena, cache, elements, tail), 2508 3029 2509 3030 Pattern::Constructor { 2510 3031 name, ··· 2513 3034 spread, 2514 3035 location, 2515 3036 .. 2516 - } => self.pattern_constructor(name, arguments, module, *spread, location), 3037 + } => self.pattern_constructor(arena, cache, name, arguments, module, *spread, location), 2517 3038 2518 3039 Pattern::Tuple { 2519 3040 elements, location, .. 2520 3041 } => { 2521 3042 let arguments = elements 2522 3043 .iter() 2523 - .map(|element| self.pattern(element)) 3044 + .map(|element| self.pattern(arena, cache, element)) 2524 3045 .collect_vec(); 2525 - "#".to_doc() 2526 - .append(self.wrap_arguments(arguments, location.end)) 2527 - .group() 3046 + "#".to_doc(&arena) 3047 + .append( 3048 + &arena, 3049 + self.wrap_arguments(arena, cache, arguments, location.end), 3050 + ) 3051 + .group(&arena) 2528 3052 } 2529 3053 2530 3054 Pattern::BitArray { ··· 2532 3056 } => { 2533 3057 let segment_docs = segments 2534 3058 .iter() 2535 - .map(|segment| bit_array_segment(segment, |pattern| self.pattern(pattern))) 3059 + .map(|segment| { 3060 + bit_array_segment(arena, cache, segment, |pattern| { 3061 + self.pattern(arena, cache, pattern) 3062 + }) 3063 + }) 2536 3064 .collect_vec(); 2537 3065 2538 - self.bit_array(segment_docs, ItemsPacking::FitOnePerLine, location) 3066 + self.bit_array( 3067 + arena, 3068 + cache, 3069 + segment_docs, 3070 + ItemsPacking::FitOnePerLine, 3071 + location, 3072 + ) 2539 3073 } 2540 3074 2541 3075 Pattern::StringPrefix { ··· 2544 3078 left_side_assignment: left_assign, 2545 3079 .. 2546 3080 } => { 2547 - let left = self.string(left); 3081 + let left = self.string(arena, cache, left); 2548 3082 let right = match right { 2549 - AssignName::Variable(name) => name.to_doc(), 2550 - AssignName::Discard(name) => name.to_doc(), 3083 + AssignName::Variable(name) => name.to_doc(&arena), 3084 + AssignName::Discard(name) => name.to_doc(&arena), 2551 3085 }; 2552 3086 match left_assign { 2553 - Some((name, _)) => docvec![left, " as ", name, " <> ", right], 2554 - None => docvec![left, " <> ", right], 3087 + Some((name, _)) => { 3088 + docvec_ref_arena![arena, left, " as ", name, " <> ", right] 3089 + } 3090 + None => docvec_ref_arena![arena, left, " <> ", right], 2555 3091 } 2556 3092 } 2557 3093 2558 3094 Pattern::Invalid { .. } => panic!("invalid patterns can not be in an untyped ast"), 2559 3095 }; 2560 - commented(doc, comments) 3096 + commented(arena, cache, doc, comments) 2561 3097 } 2562 3098 2563 - fn bit_array_size(&mut self, size: &'a BitArraySize<()>) -> Document<'a> { 3099 + fn bit_array_size( 3100 + &mut self, 3101 + arena: &'doc DocumentArena<'a, 'doc>, 3102 + cache: &'doc FormatterCache<'a, 'doc>, 3103 + size: &'a BitArraySize<()>, 3104 + ) -> Document<'a, 'doc> { 2564 3105 match size { 2565 - BitArraySize::Int { value, .. } => self.int(value), 2566 - BitArraySize::Variable { name, .. } => name.to_doc(), 3106 + BitArraySize::Int { value, .. } => self.int(arena, cache, value), 3107 + BitArraySize::Variable { name, .. } => name.to_doc(&arena), 2567 3108 BitArraySize::BinaryOperator { 2568 3109 left, 2569 3110 right, ··· 2578 3119 IntOperator::Remainder => " % ", 2579 3120 }; 2580 3121 2581 - docvec![ 2582 - self.bit_array_size(left), 3122 + docvec_ref_arena![ 3123 + arena, 3124 + self.bit_array_size(arena, cache, left), 2583 3125 operator, 2584 - self.bit_array_size(right) 3126 + self.bit_array_size(arena, cache, right) 2585 3127 ] 2586 3128 } 2587 - BitArraySize::Block { inner, .. } => self.bit_array_size(inner).surround("{ ", " }"), 3129 + BitArraySize::Block { inner, .. } => self 3130 + .bit_array_size(arena, cache, inner) 3131 + .surround(&arena, "{ ", " }"), 2588 3132 } 2589 3133 } 2590 3134 2591 3135 fn list_pattern( 2592 3136 &mut self, 3137 + arena: &'doc DocumentArena<'a, 'doc>, 3138 + cache: &'doc FormatterCache<'a, 'doc>, 2593 3139 elements: &'a [UntypedPattern], 2594 3140 tail: &'a Option<Box<UntypedTailPattern>>, 2595 - ) -> Document<'a> { 3141 + ) -> Document<'a, 'doc> { 2596 3142 if elements.is_empty() { 2597 3143 return match tail { 2598 - Some(tail) => self.pattern(&tail.pattern), 2599 - None => "[]".to_doc(), 3144 + Some(tail) => self.pattern(arena, cache, &tail.pattern), 3145 + None => "[]".to_doc(&arena), 2600 3146 }; 2601 3147 } 2602 - let elements = join( 2603 - elements.iter().map(|element| self.pattern(element)), 2604 - break_(",", ", "), 3148 + let elements = arena.join( 3149 + elements 3150 + .iter() 3151 + .map(|element| self.pattern(arena, cache, element)), 3152 + arena.break_(",", ", "), 2605 3153 ); 2606 - let doc = break_("[", "[").append(elements); 3154 + let doc = arena.break_("[", "[").append(&arena, elements); 2607 3155 match tail { 2608 - None => doc.nest(INDENT).append(break_(",", "")), 3156 + None => doc 3157 + .nest(&arena, INDENT) 3158 + .append(&arena, arena.break_(",", "")), 2609 3159 2610 3160 Some(tail) => { 2611 3161 let tail = &tail.pattern; 2612 3162 let comments = self.pop_comments(tail.location().start); 2613 3163 let tail = if tail.is_discard() { 2614 - "..".to_doc() 3164 + "..".to_doc(&arena) 2615 3165 } else { 2616 - docvec!["..", self.pattern(tail)] 3166 + docvec_ref_arena![arena, "..", self.pattern(arena, cache, tail)] 2617 3167 }; 2618 - let tail = commented(tail, comments); 2619 - doc.append(break_(",", ", ")) 2620 - .append(tail) 2621 - .nest(INDENT) 2622 - .append(break_("", "")) 3168 + let tail = commented(arena, cache, tail, comments); 3169 + doc.append(&arena, arena.break_(",", ", ")) 3170 + .append(&arena, tail) 3171 + .nest(&arena, INDENT) 3172 + .append(&arena, cache.empty_break) 2623 3173 } 2624 3174 } 2625 - .append("]") 2626 - .group() 3175 + .append(&arena, "]") 3176 + .group(&arena) 2627 3177 } 2628 3178 2629 - fn pattern_call_arg(&mut self, argument: &'a CallArg<UntypedPattern>) -> Document<'a> { 2630 - self.format_call_arg(argument, pattern_call_arg_formatting, |this, value| { 2631 - this.pattern(value) 2632 - }) 3179 + fn pattern_call_arg( 3180 + &mut self, 3181 + arena: &'doc DocumentArena<'a, 'doc>, 3182 + cache: &'doc FormatterCache<'a, 'doc>, 3183 + argument: &'a CallArg<UntypedPattern>, 3184 + ) -> Document<'a, 'doc> { 3185 + self.format_call_arg( 3186 + arena, 3187 + cache, 3188 + argument, 3189 + pattern_call_arg_formatting, 3190 + |this, value| this.pattern(arena, cache, value), 3191 + ) 2633 3192 } 2634 3193 2635 3194 pub fn clause_guard_bin_op( 2636 3195 &mut self, 3196 + arena: &'doc DocumentArena<'a, 'doc>, 3197 + cache: &'doc FormatterCache<'a, 'doc>, 2637 3198 name: &'a BinOp, 2638 3199 left: &'a UntypedClauseGuard, 2639 3200 right: &'a UntypedClauseGuard, 2640 - ) -> Document<'a> { 2641 - self.clause_guard_bin_op_side(name, left, left.precedence()) 2642 - .append(break_("", " ")) 2643 - .append(name.to_doc()) 2644 - .append(" ") 2645 - .append(self.clause_guard_bin_op_side(name, right, right.precedence() - 1)) 3201 + ) -> Document<'a, 'doc> { 3202 + self.clause_guard_bin_op_side(arena, cache, name, left, left.precedence()) 3203 + .append(&arena, cache.breakable_space) 3204 + .append(&arena, name.to_doc(&arena)) 3205 + .append(&arena, cache.space) 3206 + .append( 3207 + &arena, 3208 + self.clause_guard_bin_op_side(arena, cache, name, right, right.precedence() - 1), 3209 + ) 2646 3210 } 2647 3211 2648 3212 fn clause_guard_bin_op_side( 2649 3213 &mut self, 3214 + arena: &'doc DocumentArena<'a, 'doc>, 3215 + cache: &'doc FormatterCache<'a, 'doc>, 2650 3216 name: &BinOp, 2651 3217 side: &'a UntypedClauseGuard, 2652 3218 // As opposed to `bin_op_side`, here we take the side precedence as an ··· 2654 3220 // `clause_guard_bin_op` will reduce the precedence of any right side to 2655 3221 // make sure the formatter doesn't remove any needed curly bracket. 2656 3222 side_precedence: u8, 2657 - ) -> Document<'a> { 2658 - let side_doc = self.clause_guard(side); 3223 + ) -> Document<'a, 'doc> { 3224 + let side_doc = self.clause_guard(arena, cache, side); 2659 3225 match side.bin_op_name() { 2660 3226 // In case the other side is a binary operation as well and it can 2661 3227 // be grouped together with the current binary operation, the two ··· 2663 3229 // same group and the formatter will try to keep those on a single 2664 3230 // line. 2665 3231 Some(side_name) if side_name.can_be_grouped_with(name) => { 2666 - self.operator_side(side_doc, name.precedence(), side_precedence) 3232 + self.operator_side(arena, cache, side_doc, name.precedence(), side_precedence) 2667 3233 } 2668 3234 // In case the binary operations cannot be grouped together the 2669 3235 // other side is treated as a group on its own so that it can be 2670 3236 // broken independently of other pieces of the binary operations 2671 3237 // chain. 2672 - _ => self.operator_side(side_doc.group(), name.precedence(), side_precedence), 3238 + _ => self.operator_side( 3239 + arena, 3240 + cache, 3241 + side_doc.group(&arena), 3242 + name.precedence(), 3243 + side_precedence, 3244 + ), 2673 3245 } 2674 3246 } 2675 3247 2676 - fn clause_guard(&mut self, clause_guard: &'a UntypedClauseGuard) -> Document<'a> { 3248 + fn clause_guard( 3249 + &mut self, 3250 + arena: &'doc DocumentArena<'a, 'doc>, 3251 + cache: &'doc FormatterCache<'a, 'doc>, 3252 + clause_guard: &'a UntypedClauseGuard, 3253 + ) -> Document<'a, 'doc> { 2677 3254 match clause_guard { 2678 3255 ClauseGuard::Invalid { .. } => unreachable!("invalid guard made it to formatting"), 2679 3256 ··· 2682 3259 left, 2683 3260 right, 2684 3261 .. 2685 - } => self.clause_guard_bin_op(operator, left, right), 3262 + } => self.clause_guard_bin_op(arena, cache, operator, left, right), 2686 3263 2687 - ClauseGuard::Var { name, .. } => name.to_doc(), 3264 + ClauseGuard::Var { name, .. } => name.to_doc(&arena), 2688 3265 2689 - ClauseGuard::TupleIndex { tuple, index, .. } => { 2690 - self.clause_guard(tuple).append(".").append(*index).to_doc() 2691 - } 3266 + ClauseGuard::TupleIndex { tuple, index, .. } => self 3267 + .clause_guard(arena, cache, tuple) 3268 + .append(&arena, ".") 3269 + .append(&arena, *index) 3270 + .to_doc(&arena), 2692 3271 2693 3272 ClauseGuard::FieldAccess { 2694 3273 container, label, .. 2695 3274 } => self 2696 - .clause_guard(container) 2697 - .append(".") 2698 - .append(label) 2699 - .to_doc(), 3275 + .clause_guard(arena, cache, container) 3276 + .append(&arena, ".") 3277 + .append(&arena, label) 3278 + .to_doc(&arena), 2700 3279 2701 3280 ClauseGuard::ModuleSelect { 2702 3281 module_name, label, .. 2703 - } => module_name.to_doc().append(".").append(label).to_doc(), 3282 + } => module_name 3283 + .to_doc(&arena) 3284 + .append(&arena, ".") 3285 + .append(&arena, label) 3286 + .to_doc(&arena), 2704 3287 2705 - ClauseGuard::Constant(constant) => self.const_expr(constant), 3288 + ClauseGuard::Constant(constant) => self.const_expr(arena, cache, constant), 2706 3289 2707 - ClauseGuard::Not { expression, .. } => docvec!["!", self.clause_guard(expression)], 3290 + ClauseGuard::Not { expression, .. } => { 3291 + docvec_ref_arena![arena, "!", self.clause_guard(arena, cache, expression)] 3292 + } 2708 3293 2709 - ClauseGuard::Block { value, .. } => wrap_block(self.clause_guard(value)).group(), 3294 + ClauseGuard::Block { value, .. } => { 3295 + wrap_block(arena, cache, self.clause_guard(arena, cache, value)).group(&arena) 3296 + } 2710 3297 } 2711 3298 } 2712 3299 2713 - fn constant_call_arg<A>(&mut self, argument: &'a CallArg<Constant<A>>) -> Document<'a> { 2714 - self.format_call_arg(argument, constant_call_arg_formatting, |this, value| { 2715 - this.const_expr(value) 2716 - }) 3300 + fn constant_call_arg<A>( 3301 + &mut self, 3302 + arena: &'doc DocumentArena<'a, 'doc>, 3303 + cache: &'doc FormatterCache<'a, 'doc>, 3304 + argument: &'a CallArg<Constant<A>>, 3305 + ) -> Document<'a, 'doc> { 3306 + self.format_call_arg( 3307 + arena, 3308 + cache, 3309 + argument, 3310 + constant_call_arg_formatting, 3311 + |this, value| this.const_expr(arena, cache, value), 3312 + ) 2717 3313 } 2718 3314 2719 - fn negate_bool(&mut self, expression: &'a UntypedExpr) -> Document<'a> { 3315 + fn negate_bool( 3316 + &mut self, 3317 + arena: &'doc DocumentArena<'a, 'doc>, 3318 + cache: &'doc FormatterCache<'a, 'doc>, 3319 + expression: &'a UntypedExpr, 3320 + ) -> Document<'a, 'doc> { 2720 3321 match expression { 2721 - UntypedExpr::NegateBool { value, .. } => self.expr(value), 2722 - UntypedExpr::BinOp { .. } => "!".to_doc().append(wrap_block(self.expr(expression))), 3322 + UntypedExpr::NegateBool { value, .. } => self.expr(arena, cache, value), 3323 + UntypedExpr::BinOp { .. } => { 3324 + let doc = self.expr(arena, cache, expression); 3325 + "!".to_doc(&arena) 3326 + .append(&arena, wrap_block(arena, cache, doc)) 3327 + } 2723 3328 UntypedExpr::Int { .. } 2724 3329 | UntypedExpr::Float { .. } 2725 3330 | UntypedExpr::String { .. } ··· 2738 3343 | UntypedExpr::Echo { .. } 2739 3344 | UntypedExpr::BitArray { .. } 2740 3345 | UntypedExpr::RecordUpdate { .. } 2741 - | UntypedExpr::NegateInt { .. } => docvec!["!", self.expr(expression)], 3346 + | UntypedExpr::NegateInt { .. } => { 3347 + docvec_ref_arena![arena, "!", self.expr(arena, cache, expression)] 3348 + } 2742 3349 } 2743 3350 } 2744 3351 2745 - fn negate_int(&mut self, expression: &'a UntypedExpr) -> Document<'a> { 3352 + fn negate_int( 3353 + &mut self, 3354 + arena: &'doc DocumentArena<'a, 'doc>, 3355 + cache: &'doc FormatterCache<'a, 'doc>, 3356 + expression: &'a UntypedExpr, 3357 + ) -> Document<'a, 'doc> { 2746 3358 match expression { 2747 - UntypedExpr::NegateInt { value, .. } => self.expr(value), 2748 - UntypedExpr::Int { value, .. } if value.starts_with('-') => self.int(&value[1..]), 2749 - UntypedExpr::BinOp { .. } => "- ".to_doc().append(self.expr(expression)), 3359 + UntypedExpr::NegateInt { value, .. } => self.expr(arena, cache, value), 3360 + UntypedExpr::Int { value, .. } if value.starts_with('-') => { 3361 + self.int(arena, cache, &value[1..]) 3362 + } 3363 + UntypedExpr::BinOp { .. } => "- " 3364 + .to_doc(&arena) 3365 + .append(&arena, self.expr(arena, cache, expression)), 2750 3366 2751 3367 UntypedExpr::Int { .. } 2752 3368 | UntypedExpr::Float { .. } ··· 2766 3382 | UntypedExpr::Echo { .. } 2767 3383 | UntypedExpr::BitArray { .. } 2768 3384 | UntypedExpr::RecordUpdate { .. } 2769 - | UntypedExpr::NegateBool { .. } => docvec!["-", self.expr(expression)], 3385 + | UntypedExpr::NegateBool { .. } => { 3386 + docvec_ref_arena![arena, "-", self.expr(arena, cache, expression)] 3387 + } 2770 3388 } 2771 3389 } 2772 3390 2773 - fn use_(&mut self, use_: &'a UntypedUse) -> Document<'a> { 3391 + fn use_( 3392 + &mut self, 3393 + arena: &'doc DocumentArena<'a, 'doc>, 3394 + cache: &'doc FormatterCache<'a, 'doc>, 3395 + use_: &'a UntypedUse, 3396 + ) -> Document<'a, 'doc> { 2774 3397 let comments = self.pop_comments(use_.location.start); 2775 3398 2776 3399 let call = if use_.call.is_call() { 2777 - docvec![" ", self.expr(&use_.call)] 3400 + docvec_ref_arena![arena, cache.space, self.expr(arena, cache, &use_.call)] 2778 3401 } else { 2779 - docvec![break_("", " "), self.expr(&use_.call)].nest(INDENT) 3402 + docvec_ref_arena![ 3403 + arena, 3404 + cache.breakable_space, 3405 + self.expr(arena, cache, &use_.call) 3406 + ] 3407 + .nest(&arena, INDENT) 2780 3408 } 2781 - .group(); 3409 + .group(&arena); 2782 3410 2783 3411 let doc = if use_.assignments.is_empty() { 2784 - docvec!["use <-", call] 3412 + docvec_ref_arena![arena, "use <-", call] 2785 3413 } else { 2786 3414 let assignments = use_.assignments.iter().map(|use_assignment| { 2787 - let pattern = self.pattern(&use_assignment.pattern); 2788 - let annotation = use_assignment 2789 - .annotation 2790 - .as_ref() 2791 - .map(|annotation| ": ".to_doc().append(self.type_ast(annotation))); 3415 + let pattern = self.pattern(arena, cache, &use_assignment.pattern); 3416 + let annotation = use_assignment.annotation.as_ref().map(|annotation| { 3417 + ": ".to_doc(&arena) 3418 + .append(&arena, self.type_ast(arena, cache, annotation)) 3419 + }); 2792 3420 2793 - pattern.append(annotation).group() 3421 + pattern.append(&arena, annotation).group(&arena) 2794 3422 }); 2795 - let assignments = Itertools::intersperse(assignments, break_(",", ", ")); 2796 - let left = ["use".to_doc(), break_("", " ")] 3423 + let assignments = Itertools::intersperse(assignments, arena.break_(",", ", ")); 3424 + let left = ["use".to_doc(&arena), cache.breakable_space] 2797 3425 .into_iter() 2798 3426 .chain(assignments); 2799 - let left = concat(left).nest(INDENT).append(break_("", " ")).group(); 2800 - docvec![left, "<-", call].group() 3427 + let left = arena 3428 + .concat(left) 3429 + .nest(&arena, INDENT) 3430 + .append(&arena, cache.breakable_space) 3431 + .group(&arena); 3432 + docvec_ref_arena![arena, left, "<-", call].group(&arena) 2801 3433 }; 2802 3434 2803 - commented(doc, comments) 3435 + commented(arena, cache, doc, comments) 2804 3436 } 2805 3437 2806 - fn assert(&mut self, assert: &'a UntypedAssert) -> Document<'a> { 3438 + fn assert( 3439 + &mut self, 3440 + arena: &'doc DocumentArena<'a, 'doc>, 3441 + cache: &'doc FormatterCache<'a, 'doc>, 3442 + assert: &'a UntypedAssert, 3443 + ) -> Document<'a, 'doc> { 2807 3444 let comments = self.pop_comments(assert.location.start); 2808 3445 2809 3446 let expression = if assert.value.is_binop() || assert.value.is_pipeline() { 2810 - self.expr(&assert.value).nest(INDENT) 3447 + self.expr(arena, cache, &assert.value).nest(&arena, INDENT) 2811 3448 } else { 2812 - self.expr(&assert.value) 3449 + self.expr(arena, cache, &assert.value) 2813 3450 }; 2814 3451 2815 3452 let doc = self.append_as_message_expression( 3453 + arena, 3454 + cache, 2816 3455 expression, 2817 3456 PrecedingAs::Expression, 2818 3457 assert.message.as_ref(), 2819 3458 ); 2820 - commented(docvec!["assert ", doc], comments) 3459 + commented( 3460 + arena, 3461 + cache, 3462 + docvec_ref_arena![arena, "assert ", doc], 3463 + comments, 3464 + ) 2821 3465 } 2822 3466 2823 3467 fn bit_array( 2824 3468 &mut self, 2825 - segments: Vec<Document<'a>>, 3469 + arena: &'doc DocumentArena<'a, 'doc>, 3470 + cache: &'doc FormatterCache<'a, 'doc>, 3471 + segments: Vec<Document<'a, 'doc>>, 2826 3472 packing: ItemsPacking, 2827 3473 location: &SrcSpan, 2828 - ) -> Document<'a> { 3474 + ) -> Document<'a, 'doc> { 2829 3475 let comments = self.pop_comments(location.end); 2830 - let comments_doc = printed_comments(comments, false); 3476 + let comments_doc = printed_comments(arena, cache, comments, false); 2831 3477 2832 3478 // Avoid adding illegal comma in empty bit array by explicitly handling it 2833 3479 if segments.is_empty() { ··· 2836 3482 // any comment we want to put it inside the empty bit array! 2837 3483 // Refer to the `list` function for a similar procedure. 2838 3484 return match comments_doc { 2839 - None => "<<>>".to_doc(), 3485 + None => "<<>>".to_doc(&arena), 2840 3486 Some(comments) => "<<" 2841 - .to_doc() 2842 - .append(break_("", "").nest(INDENT)) 2843 - .append(comments) 2844 - .append(break_("", "")) 2845 - .append(">>") 3487 + .to_doc(&arena) 3488 + .append(&arena, cache.empty_break.nest(&arena, INDENT)) 3489 + .append(&arena, comments) 3490 + .append(&arena, cache.empty_break) 3491 + .append(&arena, ">>") 2846 3492 // vvv We want to make sure the comments are on a separate 2847 3493 // line from the opening and closing angle brackets so 2848 3494 // we force the breaks to be split on newlines. 2849 - .force_break(), 3495 + .force_break(&arena), 2850 3496 }; 2851 3497 } 2852 3498 2853 3499 let comma = match packing { 2854 - ItemsPacking::FitMultiplePerLine => flex_break(",", ", "), 2855 - ItemsPacking::FitOnePerLine | ItemsPacking::BreakOnePerLine => break_(",", ", "), 3500 + ItemsPacking::FitMultiplePerLine => arena.flex_break(",", ", "), 3501 + ItemsPacking::FitOnePerLine | ItemsPacking::BreakOnePerLine => arena.break_(",", ", "), 2856 3502 }; 2857 3503 2858 - let last_break = break_(",", ""); 2859 - let doc = break_("<<", "<<") 2860 - .append(join(segments, comma)) 2861 - .nest(INDENT); 3504 + let last_break = arena.break_(",", ""); 3505 + let doc = arena 3506 + .break_("<<", "<<") 3507 + .append(&arena, arena.join(segments, comma)) 3508 + .nest(&arena, INDENT); 2862 3509 2863 3510 let doc = match comments_doc { 2864 - None => doc.append(last_break).append(">>"), 3511 + None => doc.append(&arena, last_break).append(&arena, ">>"), 2865 3512 Some(comments) => doc 2866 - .append(last_break.nest(INDENT)) 3513 + .append(&arena, last_break.nest(&arena, INDENT)) 2867 3514 // ^ Notice how in this case we nest the final break before 2868 3515 // adding it: this way the comments are going to be as 2869 3516 // indented as the bit array items. 2870 - .append(comments.nest(INDENT)) 2871 - .append(line()) 2872 - .append(">>") 2873 - .force_break(), 3517 + .append(&arena, comments.nest(&arena, INDENT)) 3518 + .append(&arena, cache.line) 3519 + .append(&arena, ">>") 3520 + .force_break(&arena), 2874 3521 }; 2875 3522 2876 3523 match packing { 2877 - ItemsPacking::FitOnePerLine | ItemsPacking::FitMultiplePerLine => doc.group(), 2878 - ItemsPacking::BreakOnePerLine => doc.force_break(), 3524 + ItemsPacking::FitOnePerLine | ItemsPacking::FitMultiplePerLine => doc.group(&arena), 3525 + ItemsPacking::BreakOnePerLine => doc.force_break(&arena), 2879 3526 } 2880 3527 } 2881 3528 2882 - fn bit_array_segment_expr(&mut self, expression: &'a UntypedExpr) -> Document<'a> { 3529 + fn bit_array_segment_expr( 3530 + &mut self, 3531 + arena: &'doc DocumentArena<'a, 'doc>, 3532 + cache: &'doc FormatterCache<'a, 'doc>, 3533 + expression: &'a UntypedExpr, 3534 + ) -> Document<'a, 'doc> { 2883 3535 match expression { 2884 - UntypedExpr::BinOp { .. } => wrap_block(self.expr(expression)), 3536 + UntypedExpr::BinOp { .. } => { 3537 + wrap_block(arena, cache, self.expr(arena, cache, expression)) 3538 + } 2885 3539 2886 3540 UntypedExpr::Int { .. } 2887 3541 | UntypedExpr::Float { .. } ··· 2902 3556 | UntypedExpr::RecordUpdate { .. } 2903 3557 | UntypedExpr::NegateBool { .. } 2904 3558 | UntypedExpr::NegateInt { .. } 2905 - | UntypedExpr::Block { .. } => self.expr(expression), 3559 + | UntypedExpr::Block { .. } => self.expr(arena, cache, expression), 2906 3560 } 2907 3561 } 2908 3562 2909 - fn statement(&mut self, statement: &'a UntypedStatement) -> Document<'a> { 3563 + fn statement( 3564 + &mut self, 3565 + arena: &'doc DocumentArena<'a, 'doc>, 3566 + cache: &'doc FormatterCache<'a, 'doc>, 3567 + statement: &'a UntypedStatement, 3568 + ) -> Document<'a, 'doc> { 2910 3569 match statement { 2911 - Statement::Expression(expression) => self.expr(expression), 2912 - Statement::Assignment(assignment) => self.assignment(assignment), 2913 - Statement::Use(use_) => self.use_(use_), 2914 - Statement::Assert(assert) => self.assert(assert), 3570 + Statement::Expression(expression) => self.expr(arena, cache, expression), 3571 + Statement::Assignment(assignment) => self.assignment(arena, cache, assignment), 3572 + Statement::Use(use_) => self.use_(arena, cache, use_), 3573 + Statement::Assert(assert) => self.assert(arena, cache, assert), 2915 3574 } 2916 3575 } 2917 3576 2918 3577 fn block( 2919 3578 &mut self, 3579 + arena: &'doc DocumentArena<'a, 'doc>, 3580 + cache: &'doc FormatterCache<'a, 'doc>, 2920 3581 location: &SrcSpan, 2921 3582 statements: &'a Vec1<UntypedStatement>, 2922 3583 force_breaks: bool, 2923 - ) -> Document<'a> { 2924 - let statements_doc = 2925 - docvec![break_("", " "), self.statements(statements.as_vec())].nest(INDENT); 3584 + ) -> Document<'a, 'doc> { 3585 + let statements_doc = docvec_ref_arena![ 3586 + arena, 3587 + cache.breakable_space, 3588 + self.statements(arena, cache, statements.as_vec()) 3589 + ] 3590 + .nest(&arena, INDENT); 2926 3591 let trailing_comments = self.pop_comments(location.end); 2927 - let trailing_comments = printed_comments(trailing_comments, false); 3592 + let trailing_comments = printed_comments(arena, cache, trailing_comments, false); 2928 3593 let block_doc = match trailing_comments { 2929 - Some(trailing_comments_doc) => docvec![ 3594 + Some(trailing_comments_doc) => docvec_ref_arena![ 3595 + arena, 2930 3596 "{", 2931 3597 statements_doc, 2932 - line().nest(INDENT), 2933 - trailing_comments_doc.nest(INDENT), 2934 - line(), 3598 + cache.line.nest(&arena, INDENT), 3599 + trailing_comments_doc.nest(&arena, INDENT), 3600 + cache.line, 2935 3601 "}" 2936 3602 ] 2937 - .force_break(), 2938 - None => docvec!["{", statements_doc, break_("", " "), "}"], 3603 + .force_break(&arena), 3604 + None => docvec_ref_arena![arena, "{", statements_doc, cache.breakable_space, "}"], 2939 3605 }; 2940 3606 2941 3607 if force_breaks { 2942 - block_doc.force_break().group() 3608 + block_doc.force_break(&arena).group(&arena) 2943 3609 } else { 2944 - block_doc.group() 3610 + block_doc.group(&arena) 2945 3611 } 2946 3612 } 2947 3613 2948 3614 pub fn wrap_function_call_arguments<I>( 2949 3615 &mut self, 3616 + arena: &'doc DocumentArena<'a, 'doc>, 3617 + cache: &'doc FormatterCache<'a, 'doc>, 2950 3618 arguments: I, 2951 3619 location: &SrcSpan, 2952 - ) -> Document<'a> 3620 + ) -> Document<'a, 'doc> 2953 3621 where 2954 - I: IntoIterator<Item = Document<'a>>, 3622 + I: IntoIterator<Item = Document<'a, 'doc>>, 2955 3623 { 2956 3624 let mut arguments = arguments.into_iter().peekable(); 2957 3625 if arguments.peek().is_none() { 2958 - return "()".to_doc(); 3626 + return "()".to_doc(&arena); 2959 3627 } 2960 3628 2961 - let arguments_doc = break_("", "") 2962 - .append(join(arguments, break_(",", ", "))) 2963 - .nest_if_broken(INDENT); 3629 + let arguments_doc = arena 3630 + .break_("", "") 3631 + .append(&arena, arena.join(arguments, arena.break_(",", ", "))) 3632 + .nest_if_broken(&arena, INDENT); 2964 3633 2965 3634 // We get all remaining comments that come before the call's closing 2966 3635 // parenthesis. ··· 2968 3637 // of moving those out of the call. 2969 3638 // Otherwise those would be moved out of the call. 2970 3639 let comments = self.pop_comments(location.end); 2971 - let closing_parens = match printed_comments(comments, false) { 2972 - None => docvec![break_(",", ""), ")"], 2973 - Some(comment) => { 2974 - docvec![break_(",", "").nest(INDENT), comment, line(), ")"].force_break() 2975 - } 3640 + let closing_parens = match printed_comments(arena, cache, comments, false) { 3641 + None => docvec_ref_arena![arena, arena.break_(",", ""), ")"], 3642 + Some(comment) => docvec_ref_arena![ 3643 + arena, 3644 + arena.break_(",", "").nest(&arena, INDENT), 3645 + comment, 3646 + cache.line, 3647 + ")" 3648 + ] 3649 + .force_break(&arena), 2976 3650 }; 2977 3651 2978 - "(".to_doc() 2979 - .append(arguments_doc) 2980 - .append(closing_parens) 2981 - .group() 3652 + "(".to_doc(&arena) 3653 + .append(&arena, arguments_doc) 3654 + .append(&arena, closing_parens) 3655 + .group(&arena) 2982 3656 } 2983 3657 2984 - pub fn wrap_arguments<I>(&mut self, arguments: I, comments_limit: u32) -> Document<'a> 3658 + pub fn wrap_arguments<I>( 3659 + &mut self, 3660 + arena: &'doc DocumentArena<'a, 'doc>, 3661 + cache: &'doc FormatterCache<'a, 'doc>, 3662 + arguments: I, 3663 + comments_limit: u32, 3664 + ) -> Document<'a, 'doc> 2985 3665 where 2986 - I: IntoIterator<Item = Document<'a>>, 3666 + I: IntoIterator<Item = Document<'a, 'doc>>, 2987 3667 { 2988 3668 let mut arguments = arguments.into_iter().peekable(); 2989 3669 if arguments.peek().is_none() { 2990 3670 let comments = self.pop_comments(comments_limit); 2991 - return match printed_comments(comments, false) { 3671 + return match printed_comments(arena, cache, comments, false) { 2992 3672 Some(comments) => "(" 2993 - .to_doc() 2994 - .append(break_("", "")) 2995 - .append(comments) 2996 - .nest_if_broken(INDENT) 2997 - .force_break() 2998 - .append(break_("", "")) 2999 - .append(")"), 3000 - None => "()".to_doc(), 3673 + .to_doc(&arena) 3674 + .append(&arena, cache.empty_break) 3675 + .append(&arena, comments) 3676 + .nest_if_broken(&arena, INDENT) 3677 + .force_break(&arena) 3678 + .append(&arena, cache.empty_break) 3679 + .append(&arena, ")"), 3680 + None => "()".to_doc(&arena), 3001 3681 }; 3002 3682 } 3003 - let doc = break_("(", "(").append(join(arguments, break_(",", ", "))); 3683 + let doc = arena 3684 + .break_("(", "(") 3685 + .append(&arena, arena.join(arguments, arena.break_(",", ", "))); 3004 3686 3005 3687 // Include trailing comments if there are any 3006 3688 let comments = self.pop_comments(comments_limit); 3007 - match printed_comments(comments, false) { 3689 + match printed_comments(arena, cache, comments, false) { 3008 3690 Some(comments) => doc 3009 - .append(break_(",", "")) 3010 - .append(comments) 3011 - .nest_if_broken(INDENT) 3012 - .force_break() 3013 - .append(break_("", "")) 3014 - .append(")"), 3691 + .append(&arena, arena.break_(",", "")) 3692 + .append(&arena, comments) 3693 + .nest_if_broken(&arena, INDENT) 3694 + .force_break(&arena) 3695 + .append(&arena, cache.empty_break) 3696 + .append(&arena, ")"), 3015 3697 None => doc 3016 - .nest_if_broken(INDENT) 3017 - .append(break_(",", "")) 3018 - .append(")"), 3698 + .nest_if_broken(&arena, INDENT) 3699 + .append(&arena, arena.break_(",", "")) 3700 + .append(&arena, ")"), 3019 3701 } 3020 3702 } 3021 3703 3022 3704 pub fn wrap_arguments_with_spread<I>( 3023 3705 &mut self, 3706 + arena: &'doc DocumentArena<'a, 'doc>, 3707 + cache: &'doc FormatterCache<'a, 'doc>, 3024 3708 arguments: I, 3025 3709 comments_limit: u32, 3026 - ) -> Document<'a> 3710 + ) -> Document<'a, 'doc> 3027 3711 where 3028 - I: IntoIterator<Item = Document<'a>>, 3712 + I: IntoIterator<Item = Document<'a, 'doc>>, 3029 3713 { 3030 3714 let mut arguments = arguments.into_iter().peekable(); 3031 3715 if arguments.peek().is_none() { 3032 - return self.wrap_arguments(arguments, comments_limit); 3716 + return self.wrap_arguments(arena, cache, arguments, comments_limit); 3033 3717 } 3034 - let doc = break_("(", "(") 3035 - .append(join(arguments, break_(",", ", "))) 3036 - .append(break_(",", ", ")) 3037 - .append(".."); 3718 + let doc = arena 3719 + .break_("(", "(") 3720 + .append(&arena, arena.join(arguments, arena.break_(",", ", "))) 3721 + .append(&arena, arena.break_(",", ", ")) 3722 + .append(&arena, ".."); 3038 3723 3039 3724 // Include trailing comments if there are any 3040 3725 let comments = self.pop_comments(comments_limit); 3041 - match printed_comments(comments, false) { 3726 + match printed_comments(arena, cache, comments, false) { 3042 3727 Some(comments) => doc 3043 - .append(break_(",", "")) 3044 - .append(comments) 3045 - .nest_if_broken(INDENT) 3046 - .force_break() 3047 - .append(break_("", "")) 3048 - .append(")"), 3728 + .append(&arena, arena.break_(",", "")) 3729 + .append(&arena, comments) 3730 + .nest_if_broken(&arena, INDENT) 3731 + .force_break(&arena) 3732 + .append(&arena, cache.empty_break) 3733 + .append(&arena, ")"), 3049 3734 None => doc 3050 - .nest_if_broken(INDENT) 3051 - .append(break_(",", "")) 3052 - .append(")"), 3735 + .nest_if_broken(&arena, INDENT) 3736 + .append(&arena, arena.break_(",", "")) 3737 + .append(&arena, ")"), 3053 3738 } 3054 3739 } 3055 3740 ··· 3071 3756 /// 3072 3757 fn printed_documented_comments<'b>( 3073 3758 &mut self, 3759 + arena: &'doc DocumentArena<'a, 'doc>, 3760 + cache: &'doc FormatterCache<'a, 'doc>, 3074 3761 comments: impl IntoIterator<Item = (u32, Option<&'b str>)>, 3075 - ) -> Option<Document<'a>> { 3762 + ) -> Option<Document<'a, 'doc>> { 3076 3763 let mut comments = comments.into_iter().peekable(); 3077 3764 let _ = comments.peek()?; 3078 3765 ··· 3080 3767 while let Some(comment) = comments.next() { 3081 3768 let (is_doc_commented, comment) = match comment { 3082 3769 (comment_start, Some(c)) => { 3083 - let doc_comment = self.doc_comments(comment_start); 3084 - let is_doc_commented = !doc_comment.is_empty(); 3770 + let doc_comment = self.doc_comments(arena, cache, comment_start); 3771 + let is_doc_commented = !doc_comment.is_empty(&arena); 3085 3772 doc.push(doc_comment); 3086 3773 (is_doc_commented, c) 3087 3774 } 3088 3775 (_, None) => continue, 3089 3776 }; 3090 - doc.push("//".to_doc().append(EcoString::from(comment))); 3777 + doc.push("//".to_doc(&arena).append(&arena, EcoString::from(comment))); 3091 3778 match comments.peek() { 3092 3779 // Next line is a comment 3093 - Some((_, Some(_))) => doc.push(line()), 3780 + Some((_, Some(_))) => doc.push(cache.line), 3094 3781 // Next line is empty 3095 3782 Some((_, None)) => { 3096 3783 let _ = comments.next(); 3097 - doc.push(lines(2)); 3784 + doc.push(cache.two_lines); 3098 3785 } 3099 3786 // We've reached the end, there are no more lines 3100 3787 None => { 3101 3788 if is_doc_commented { 3102 - doc.push(lines(2)); 3789 + doc.push(cache.two_lines); 3103 3790 } else { 3104 - doc.push(line()); 3791 + doc.push(cache.line); 3105 3792 } 3106 3793 } 3107 3794 } 3108 3795 } 3109 - let doc = concat(doc); 3110 - Some(doc.force_break()) 3796 + let doc = arena.concat(doc); 3797 + Some(doc.force_break(&arena)) 3111 3798 } 3112 3799 3113 3800 fn append_as_message_expression( 3114 3801 &mut self, 3115 - doc: Document<'a>, 3802 + arena: &'doc DocumentArena<'a, 'doc>, 3803 + cache: &'doc FormatterCache<'a, 'doc>, 3804 + doc: Document<'a, 'doc>, 3116 3805 preceding_as: PrecedingAs, 3117 3806 message: Option<&'a UntypedExpr>, 3118 - ) -> Document<'a> { 3807 + ) -> Document<'a, 'doc> { 3119 3808 let Some(message) = message else { return doc }; 3120 3809 3121 3810 let comments = self.pop_comments(message.location().start); 3122 - let comments = printed_comments(comments, false); 3811 + let comments = printed_comments(arena, cache, comments, false); 3123 3812 3124 3813 let as_ = match preceding_as { 3125 - PrecedingAs::Keyword => " as".to_doc(), 3126 - PrecedingAs::Expression => docvec![break_("", " "), "as"].nest(INDENT), 3814 + PrecedingAs::Keyword => " as".to_doc(&arena), 3815 + PrecedingAs::Expression => { 3816 + docvec_ref_arena![arena, cache.breakable_space, "as"].nest(&arena, INDENT) 3817 + } 3127 3818 }; 3128 3819 3129 3820 let doc = match comments { ··· 3135 3826 // // comment! 3136 3827 // "wibble" 3137 3828 // ``` 3138 - Some(comments) => docvec![ 3139 - doc.group(), 3829 + Some(comments) => docvec_ref_arena![ 3830 + arena, 3831 + doc.group(&arena), 3140 3832 as_, 3141 - docvec![line(), comments, line(), self.expr(message).group()].nest(INDENT) 3833 + docvec_ref_arena![ 3834 + arena, 3835 + cache.line, 3836 + comments, 3837 + cache.line, 3838 + self.expr(arena, cache, message).group(&arena) 3839 + ] 3840 + .nest(&arena, INDENT) 3142 3841 ], 3143 3842 3144 3843 None => { ··· 3157 3856 // wibble wobble 3158 3857 // } 3159 3858 // ``` 3160 - (PrecedingAs::Keyword, UntypedExpr::Block { .. }) => self.expr(message).group(), 3161 - _ => self.expr(message).group().nest(INDENT), 3859 + (PrecedingAs::Keyword, UntypedExpr::Block { .. }) => { 3860 + self.expr(arena, cache, message).group(&arena) 3861 + } 3862 + _ => self 3863 + .expr(arena, cache, message) 3864 + .group(&arena) 3865 + .nest(&arena, INDENT), 3162 3866 }; 3163 - docvec![doc.group(), as_, " ", message] 3867 + docvec_ref_arena![arena, doc.group(&arena), as_, cache.space, message] 3164 3868 } 3165 3869 }; 3166 3870 3167 - doc.group() 3871 + doc.group(&arena) 3168 3872 } 3169 3873 3170 3874 fn append_as_message_constant<A>( 3171 3875 &mut self, 3172 - doc: Document<'a>, 3876 + arena: &'doc DocumentArena<'a, 'doc>, 3877 + cache: &'doc FormatterCache<'a, 'doc>, 3878 + doc: Document<'a, 'doc>, 3173 3879 message: Option<&'a Constant<A>>, 3174 - ) -> Document<'a> { 3880 + ) -> Document<'a, 'doc> { 3175 3881 let Some(message) = message else { return doc }; 3176 3882 3177 3883 let comments = self.pop_comments(message.location().start); 3178 - let comments = printed_comments(comments, false); 3884 + let comments = printed_comments(arena, cache, comments, false); 3179 3885 3180 3886 let doc = match comments { 3181 3887 // If there's comments between the document and the message we want ··· 3186 3892 // // comment! 3187 3893 // "wibble" 3188 3894 // ``` 3189 - Some(comments) => docvec![ 3190 - doc.group(), 3895 + Some(comments) => docvec_ref_arena![ 3896 + arena, 3897 + doc.group(&arena), 3191 3898 " as", 3192 - docvec![line(), comments, line(), self.const_expr(message).group()].nest(INDENT) 3899 + docvec_ref_arena![ 3900 + arena, 3901 + cache.line, 3902 + comments, 3903 + cache.line, 3904 + self.const_expr(arena, cache, message).group(&arena) 3905 + ] 3906 + .nest(&arena, INDENT) 3193 3907 ], 3194 3908 3195 3909 None => { 3196 - let message = self.const_expr(message).group().nest(INDENT); 3197 - docvec![doc.group(), " as ", message] 3910 + let message = self 3911 + .const_expr(arena, cache, message) 3912 + .group(&arena) 3913 + .nest(&arena, INDENT); 3914 + docvec_ref_arena![arena, doc.group(&arena), " as ", message] 3198 3915 } 3199 3916 }; 3200 3917 3201 - doc.group() 3918 + doc.group(&arena) 3202 3919 } 3203 3920 3204 3921 fn echo( 3205 3922 &mut self, 3923 + arena: &'doc DocumentArena<'a, 'doc>, 3924 + cache: &'doc FormatterCache<'a, 'doc>, 3206 3925 expression: &'a Option<Box<UntypedExpr>>, 3207 3926 message: &'a Option<Box<UntypedExpr>>, 3208 - ) -> Document<'a> { 3927 + ) -> Document<'a, 'doc> { 3209 3928 let Some(expression) = expression else { 3210 3929 return self.append_as_message_expression( 3211 - "echo".to_doc(), 3930 + arena, 3931 + cache, 3932 + "echo".to_doc(&arena), 3212 3933 PrecedingAs::Keyword, 3213 3934 message.as_deref(), 3214 3935 ); ··· 3233 3954 // |> wibble 3234 3955 // ``` 3235 3956 // 3236 - let doc = self.expr(expression); 3957 + let doc = self.expr(arena, cache, expression); 3237 3958 if expression.is_binop() || expression.is_pipeline() { 3238 3959 let doc = self.append_as_message_expression( 3239 - doc.nest(INDENT), 3960 + arena, 3961 + cache, 3962 + doc.nest(&arena, INDENT), 3240 3963 PrecedingAs::Expression, 3241 3964 message.as_deref(), 3242 3965 ); 3243 - docvec!["echo ", doc] 3966 + docvec_ref_arena![arena, "echo ", doc] 3244 3967 } else { 3245 - docvec![ 3968 + docvec_ref_arena![ 3969 + arena, 3246 3970 "echo ", 3247 - self.append_as_message_expression(doc, PrecedingAs::Expression, message.as_deref()) 3971 + self.append_as_message_expression( 3972 + arena, 3973 + cache, 3974 + doc, 3975 + PrecedingAs::Expression, 3976 + message.as_deref() 3977 + ) 3248 3978 ] 3249 3979 } 3250 3980 } ··· 3287 4017 } 3288 4018 } 3289 4019 3290 - impl<'a> Documentable<'a> for &'a ArgNames { 3291 - fn to_doc(self) -> Document<'a> { 4020 + impl<'a, 'doc> Documentable<'a, 'doc> for &'a ArgNames { 4021 + fn to_doc(self, arena: &'doc DocumentArena<'a, 'doc>) -> Document<'a, 'doc> { 3292 4022 match self { 3293 - ArgNames::Named { name, .. } | ArgNames::Discard { name, .. } => name.to_doc(), 4023 + ArgNames::Named { name, .. } | ArgNames::Discard { name, .. } => name.to_doc(arena), 3294 4024 ArgNames::LabelledDiscard { label, name, .. } 3295 4025 | ArgNames::NamedLabelled { label, name, .. } => { 3296 - docvec![label, " ", name] 4026 + docvec_ref_arena![arena, label, " ", name] 3297 4027 } 3298 4028 } 3299 4029 } 3300 4030 } 3301 4031 3302 - fn pub_(publicity: Publicity) -> Document<'static> { 3303 - match publicity { 3304 - Publicity::Public | Publicity::Internal { .. } => "pub ".to_doc(), 3305 - Publicity::Private => nil(), 4032 + impl<'a, 'doc> Documentable<'a, 'doc> for &'a UnqualifiedImport { 4033 + fn to_doc(self, arena: &'doc DocumentArena<'a, 'doc>) -> Document<'a, 'doc> { 4034 + self.name.as_str().to_doc(arena).append( 4035 + arena, 4036 + match &self.as_name { 4037 + None => arena.nil(), 4038 + Some(s) => " as ".to_doc(arena).append(arena, s.as_str()), 4039 + }, 4040 + ) 3306 4041 } 3307 4042 } 3308 4043 3309 - impl<'a> Documentable<'a> for &'a UnqualifiedImport { 3310 - fn to_doc(self) -> Document<'a> { 3311 - self.name.as_str().to_doc().append(match &self.as_name { 3312 - None => nil(), 3313 - Some(s) => " as ".to_doc().append(s.as_str()), 3314 - }) 3315 - } 3316 - } 3317 - 3318 - impl<'a> Documentable<'a> for &'a BinOp { 3319 - fn to_doc(self) -> Document<'a> { 4044 + impl<'a, 'doc> Documentable<'a, 'doc> for &'a BinOp { 4045 + fn to_doc(self, arena: &'doc DocumentArena<'a, 'doc>) -> Document<'a, 'doc> { 3320 4046 match self { 3321 4047 BinOp::And => "&&", 3322 4048 BinOp::Or => "||", ··· 3341 4067 BinOp::RemainderInt => "%", 3342 4068 BinOp::Concatenate => "<>", 3343 4069 } 3344 - .to_doc() 4070 + .to_doc(arena) 3345 4071 } 3346 4072 } 3347 4073 ··· 3397 4123 BreakOnePerLine, 3398 4124 } 3399 4125 3400 - pub fn break_block(doc: Document<'_>) -> Document<'_> { 3401 - "{".to_doc() 3402 - .append(line().append(doc).nest(INDENT)) 3403 - .append(line()) 3404 - .append("}") 3405 - .force_break() 3406 - } 3407 - 3408 - pub fn wrap_block(doc: Document<'_>) -> Document<'_> { 3409 - break_("{", "{ ") 3410 - .append(doc) 3411 - .nest(INDENT) 3412 - .append(break_("", " ")) 3413 - .append("}") 3414 - } 3415 - 3416 - fn printed_comments<'a, 'comments>( 3417 - comments: impl IntoIterator<Item = Option<&'comments str>>, 3418 - trailing_newline: bool, 3419 - ) -> Option<Document<'a>> { 3420 - let mut comments = comments.into_iter().peekable(); 3421 - let _ = comments.peek()?; 3422 - 3423 - let mut doc = Vec::new(); 3424 - while let Some(c) = comments.next() { 3425 - let c = match c { 3426 - Some(c) => c, 3427 - None => continue, 3428 - }; 3429 - doc.push("//".to_doc().append(EcoString::from(c))); 3430 - match comments.peek() { 3431 - // Next line is a comment 3432 - Some(Some(_)) => doc.push(line()), 3433 - // Next line is empty 3434 - Some(None) => { 3435 - let _ = comments.next(); 3436 - match comments.peek() { 3437 - Some(_) => doc.push(lines(2)), 3438 - None => { 3439 - if trailing_newline { 3440 - doc.push(lines(2)); 3441 - } 3442 - } 3443 - } 3444 - } 3445 - // We've reached the end, there are no more lines 3446 - None => { 3447 - if trailing_newline { 3448 - doc.push(line()); 3449 - } 3450 - } 3451 - } 3452 - } 3453 - let doc = concat(doc); 3454 - if trailing_newline { 3455 - Some(doc.force_break()) 3456 - } else { 3457 - Some(doc) 3458 - } 3459 - } 3460 - 3461 - fn commented<'a, 'comments>( 3462 - doc: Document<'a>, 3463 - comments: impl IntoIterator<Item = Option<&'comments str>>, 3464 - ) -> Document<'a> { 3465 - match printed_comments(comments, true) { 3466 - Some(comments) => comments.append(doc.group()), 3467 - None => doc, 3468 - } 3469 - } 3470 - 3471 - fn bit_array_segment<'a, Value, Type, ToDoc>( 3472 - segment: &'a BitArraySegment<Value, Type>, 3473 - mut to_doc: ToDoc, 3474 - ) -> Document<'a> 3475 - where 3476 - ToDoc: FnMut(&'a Value) -> Document<'a>, 3477 - { 3478 - match segment { 3479 - BitArraySegment { value, options, .. } if options.is_empty() => to_doc(value), 3480 - 3481 - BitArraySegment { value, options, .. } => to_doc(value).append(":").append(join( 3482 - options 3483 - .iter() 3484 - .map(|option| segment_option(option, &mut to_doc)), 3485 - "-".to_doc(), 3486 - )), 3487 - } 3488 - } 3489 - 3490 - fn segment_option<'a, ToDoc, Value>( 3491 - option: &'a BitArrayOption<Value>, 3492 - mut to_doc: ToDoc, 3493 - ) -> Document<'a> 3494 - where 3495 - ToDoc: FnMut(&'a Value) -> Document<'a>, 3496 - { 3497 - match option { 3498 - BitArrayOption::Bytes { .. } => "bytes".to_doc(), 3499 - BitArrayOption::Bits { .. } => "bits".to_doc(), 3500 - BitArrayOption::Int { .. } => "int".to_doc(), 3501 - BitArrayOption::Float { .. } => "float".to_doc(), 3502 - BitArrayOption::Utf8 { .. } => "utf8".to_doc(), 3503 - BitArrayOption::Utf16 { .. } => "utf16".to_doc(), 3504 - BitArrayOption::Utf32 { .. } => "utf32".to_doc(), 3505 - BitArrayOption::Utf8Codepoint { .. } => "utf8_codepoint".to_doc(), 3506 - BitArrayOption::Utf16Codepoint { .. } => "utf16_codepoint".to_doc(), 3507 - BitArrayOption::Utf32Codepoint { .. } => "utf32_codepoint".to_doc(), 3508 - BitArrayOption::Signed { .. } => "signed".to_doc(), 3509 - BitArrayOption::Unsigned { .. } => "unsigned".to_doc(), 3510 - BitArrayOption::Big { .. } => "big".to_doc(), 3511 - BitArrayOption::Little { .. } => "little".to_doc(), 3512 - BitArrayOption::Native { .. } => "native".to_doc(), 3513 - 3514 - BitArrayOption::Size { 3515 - value, 3516 - short_form: false, 3517 - .. 3518 - } => "size" 3519 - .to_doc() 3520 - .append("(") 3521 - .append(to_doc(value)) 3522 - .append(")"), 3523 - 3524 - BitArrayOption::Size { 3525 - value, 3526 - short_form: true, 3527 - .. 3528 - } => to_doc(value), 3529 - 3530 - BitArrayOption::Unit { value, .. } => "unit" 3531 - .to_doc() 3532 - .append("(") 3533 - .append(eco_format!("{value}")) 3534 - .append(")"), 3535 - } 3536 - } 3537 - 3538 4126 pub fn comments_before<'a>( 3539 4127 comments: &'a [Comment<'a>], 3540 4128 empty_lines: &'a [u32], ··· 3725 4313 } 3726 4314 } 3727 4315 3728 - impl<'a> Documentable<'a> for AttributesPrinter<'a> { 3729 - fn to_doc(self) -> Document<'a> { 4316 + impl<'a, 'doc> Documentable<'a, 'doc> for AttributesPrinter<'a> { 4317 + fn to_doc(self, arena: &'doc DocumentArena<'a, 'doc>) -> Document<'a, 'doc> { 3730 4318 let mut attributes = vec![]; 3731 4319 3732 4320 // @deprecated attribute 3733 4321 if let Deprecation::Deprecated { message } = self.deprecation { 3734 - attributes.push(docvec!["@deprecated(\"", message, "\")"]) 4322 + attributes.push(docvec_ref_arena![arena, "@deprecated(\"", message, "\")"]) 3735 4323 }; 3736 4324 3737 4325 // @external attributes 3738 4326 if let Some((m, f, _)) = self.external_erlang { 3739 - attributes.push(docvec!["@external(erlang, \"", m, "\", \"", f, "\")"]) 4327 + attributes.push(docvec_ref_arena![ 4328 + arena, 4329 + "@external(erlang, \"", 4330 + m, 4331 + "\", \"", 4332 + f, 4333 + "\")" 4334 + ]) 3740 4335 }; 3741 4336 3742 4337 if let Some((m, f, _)) = self.external_javascript { 3743 - attributes.push(docvec!["@external(javascript, \"", m, "\", \"", f, "\")"]) 4338 + attributes.push(docvec_ref_arena![ 4339 + arena, 4340 + "@external(javascript, \"", 4341 + m, 4342 + "\", \"", 4343 + f, 4344 + "\")" 4345 + ]) 3744 4346 }; 3745 4347 3746 4348 // @internal attribute 3747 4349 if self.internal { 3748 - attributes.push("@internal".to_doc()); 4350 + attributes.push("@internal".to_doc(arena)); 3749 4351 }; 3750 4352 3751 4353 if attributes.is_empty() { 3752 - nil() 4354 + arena.nil() 3753 4355 } else { 3754 - join(attributes, line()).append(line()) 4356 + arena 4357 + .join(attributes, arena.line()) 4358 + .append(arena, arena.line()) 3755 4359 } 3756 4360 } 3757 4361 } 4362 + 4363 + pub fn break_block<'a, 'doc>( 4364 + arena: &'doc DocumentArena<'a, 'doc>, 4365 + cache: &'doc FormatterCache<'a, 'doc>, 4366 + doc: Document<'a, 'doc>, 4367 + ) -> Document<'a, 'doc> { 4368 + "{".to_doc(&arena) 4369 + .append(&arena, cache.line.append(&arena, doc).nest(&arena, INDENT)) 4370 + .append(&arena, cache.line) 4371 + .append(&arena, "}") 4372 + .force_break(&arena) 4373 + } 4374 + 4375 + pub fn wrap_block<'a, 'doc>( 4376 + arena: &'doc DocumentArena<'a, 'doc>, 4377 + cache: &'doc FormatterCache<'a, 'doc>, 4378 + doc: Document<'a, 'doc>, 4379 + ) -> Document<'a, 'doc> { 4380 + arena 4381 + .break_("{", "{ ") 4382 + .append(&arena, doc) 4383 + .nest(&arena, INDENT) 4384 + .append(&arena, cache.breakable_space) 4385 + .append(&arena, "}") 4386 + } 4387 + 4388 + fn printed_comments<'a, 'doc>( 4389 + arena: &'doc DocumentArena<'a, 'doc>, 4390 + cache: &'doc FormatterCache<'a, 'doc>, 4391 + comments: impl IntoIterator<Item = Option<&'a str>>, 4392 + trailing_newline: bool, 4393 + ) -> Option<Document<'a, 'doc>> { 4394 + let mut comments = comments.into_iter().peekable(); 4395 + let _ = comments.peek()?; 4396 + 4397 + let mut doc = Vec::new(); 4398 + while let Some(c) = comments.next() { 4399 + let c = match c { 4400 + Some(c) => c, 4401 + None => continue, 4402 + }; 4403 + doc.push("//".to_doc(&arena).append(&arena, EcoString::from(c))); 4404 + match comments.peek() { 4405 + // Next line is a comment 4406 + Some(Some(_)) => doc.push(cache.line), 4407 + // Next line is empty 4408 + Some(None) => { 4409 + let _ = comments.next(); 4410 + match comments.peek() { 4411 + Some(_) => doc.push(cache.two_lines), 4412 + None => { 4413 + if trailing_newline { 4414 + doc.push(cache.two_lines); 4415 + } 4416 + } 4417 + } 4418 + } 4419 + // We've reached the end, there are no more lines 4420 + None => { 4421 + if trailing_newline { 4422 + doc.push(cache.line); 4423 + } 4424 + } 4425 + } 4426 + } 4427 + let doc = arena.concat(doc); 4428 + if trailing_newline { 4429 + Some(doc.force_break(&arena)) 4430 + } else { 4431 + Some(doc) 4432 + } 4433 + } 4434 + 4435 + fn commented<'a, 'doc>( 4436 + arena: &'doc DocumentArena<'a, 'doc>, 4437 + cache: &'doc FormatterCache<'a, 'doc>, 4438 + doc: Document<'a, 'doc>, 4439 + comments: impl IntoIterator<Item = Option<&'a str>>, 4440 + ) -> Document<'a, 'doc> { 4441 + match printed_comments(arena, cache, comments, true) { 4442 + Some(comments) => comments.append(&arena, doc.group(&arena)), 4443 + None => doc, 4444 + } 4445 + } 4446 + 4447 + fn bit_array_segment<'a, 'doc, Value, Type, ToDoc>( 4448 + arena: &'doc DocumentArena<'a, 'doc>, 4449 + cache: &'doc FormatterCache<'a, 'doc>, 4450 + segment: &'a BitArraySegment<Value, Type>, 4451 + mut to_doc: ToDoc, 4452 + ) -> Document<'a, 'doc> 4453 + where 4454 + ToDoc: FnMut(&'a Value) -> Document<'a, 'doc>, 4455 + { 4456 + match segment { 4457 + BitArraySegment { value, options, .. } if options.is_empty() => to_doc(value), 4458 + 4459 + BitArraySegment { value, options, .. } => to_doc(value).append(&arena, ":").append( 4460 + &arena, 4461 + arena.join( 4462 + options 4463 + .iter() 4464 + .map(|option| segment_option(arena, cache, option, &mut to_doc)), 4465 + "-".to_doc(&arena), 4466 + ), 4467 + ), 4468 + } 4469 + } 4470 + 4471 + fn segment_option<'a, 'doc, ToDoc, Value>( 4472 + arena: &'doc DocumentArena<'a, 'doc>, 4473 + _cache: &'doc FormatterCache<'a, 'doc>, 4474 + option: &'a BitArrayOption<Value>, 4475 + mut to_doc: ToDoc, 4476 + ) -> Document<'a, 'doc> 4477 + where 4478 + ToDoc: FnMut(&'a Value) -> Document<'a, 'doc>, 4479 + { 4480 + match option { 4481 + BitArrayOption::Bytes { .. } => "bytes".to_doc(&arena), 4482 + BitArrayOption::Bits { .. } => "bits".to_doc(&arena), 4483 + BitArrayOption::Int { .. } => "int".to_doc(&arena), 4484 + BitArrayOption::Float { .. } => "float".to_doc(&arena), 4485 + BitArrayOption::Utf8 { .. } => "utf8".to_doc(&arena), 4486 + BitArrayOption::Utf16 { .. } => "utf16".to_doc(&arena), 4487 + BitArrayOption::Utf32 { .. } => "utf32".to_doc(&arena), 4488 + BitArrayOption::Utf8Codepoint { .. } => "utf8_codepoint".to_doc(&arena), 4489 + BitArrayOption::Utf16Codepoint { .. } => "utf16_codepoint".to_doc(&arena), 4490 + BitArrayOption::Utf32Codepoint { .. } => "utf32_codepoint".to_doc(&arena), 4491 + BitArrayOption::Signed { .. } => "signed".to_doc(&arena), 4492 + BitArrayOption::Unsigned { .. } => "unsigned".to_doc(&arena), 4493 + BitArrayOption::Big { .. } => "big".to_doc(&arena), 4494 + BitArrayOption::Little { .. } => "little".to_doc(&arena), 4495 + BitArrayOption::Native { .. } => "native".to_doc(&arena), 4496 + 4497 + BitArrayOption::Size { 4498 + value, 4499 + short_form: false, 4500 + .. 4501 + } => "size" 4502 + .to_doc(&arena) 4503 + .append(&arena, "(") 4504 + .append(&arena, to_doc(value)) 4505 + .append(&arena, ")"), 4506 + 4507 + BitArrayOption::Size { 4508 + value, 4509 + short_form: true, 4510 + .. 4511 + } => to_doc(value), 4512 + 4513 + BitArrayOption::Unit { value, .. } => "unit" 4514 + .to_doc(&arena) 4515 + .append(&arena, "(") 4516 + .append(&arena, eco_format!("{value}")) 4517 + .append(&arena, ")"), 4518 + } 4519 + } 4520 + 4521 + fn pub_<'a, 'doc>( 4522 + arena: &'doc DocumentArena<'a, 'doc>, 4523 + cache: &'doc FormatterCache<'a, 'doc>, 4524 + publicity: Publicity, 4525 + ) -> Document<'a, 'doc> { 4526 + match publicity { 4527 + Publicity::Public | Publicity::Internal { .. } => "pub ".to_doc(&arena), 4528 + Publicity::Private => cache.nil, 4529 + } 4530 + }
+8 -1
compiler-core/src/javascript/decision.rs
··· 13 13 FallbackCheck, MatchTest, Offset, ReadAction, ReadSize, ReadType, RuntimeCheck, 14 14 SizeOperator, SizeTest, Variable, VariableUsage, 15 15 }, 16 - format::break_block, 17 16 javascript::{ 18 17 TypeVariant, 19 18 expression::{eco_string_int, string}, ··· 2068 2067 fn utf16_no_escape_len(str: &EcoString) -> usize { 2069 2068 length_utf16(&convert_string_escape_chars(str)) 2070 2069 } 2070 + 2071 + pub fn break_block(doc: Document<'_>) -> Document<'_> { 2072 + "{".to_doc() 2073 + .append(line().append(doc).nest(INDENT)) 2074 + .append(line()) 2075 + .append("}") 2076 + .force_break() 2077 + }
+1
compiler-core/src/lib.rs
··· 86 86 pub mod parse; 87 87 pub mod paths; 88 88 pub mod pretty; 89 + pub mod pretty_ref_arena; 89 90 pub mod requirement; 90 91 pub mod strings; 91 92 pub mod type_;
+1034
compiler-core/src/pretty_ref_arena.rs
··· 1 + //! This module implements the functionality described in 2 + //! ["Strictly Pretty" (2000) by Christian Lindig][0], with a few 3 + //! extensions. 4 + //! 5 + //! This module is heavily influenced by Elixir's Inspect.Algebra and 6 + //! JavaScript's Prettier. 7 + //! 8 + //! [0]: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.34.2200 9 + //! 10 + //! ## Extensions 11 + //! 12 + //! - `ForcedBreak` from Elixir. 13 + //! - `FlexBreak` from Elixir. 14 + //! 15 + //! The way this module works is fairly simple conceptually, however the actual 16 + //! behaviour in practice can be hard to wrap one's head around. 17 + //! 18 + //! The basic premise is the `Document` type, which is a tree structure, 19 + //! containing some text as well as information on how it can be formatted. 20 + //! Once the document is constructed, it can be printed using the 21 + //! `to_pretty_string` function. 22 + //! 23 + //! It will then traverse the tree, and construct 24 + //! a string, attempting to wrap lines to that they do not exceed the line length 25 + //! limit specified. Where and when it wraps lines is determined by the structure 26 + //! of the `Document` itself. 27 + //! 28 + #![allow(clippy::wrong_self_convention)] 29 + 30 + use std::{cell::RefCell, rc::Rc}; 31 + 32 + use ecow::{EcoString, eco_format}; 33 + use num_bigint::BigInt; 34 + use typed_arena::Arena; 35 + use unicode_segmentation::UnicodeSegmentation; 36 + 37 + use crate::{Result, io::Utf8Writer}; 38 + 39 + /// Join multiple documents together in a vector. This macro calls the `to_doc` 40 + /// method on each element, providing a concise way to write a document sequence. 41 + /// For example: 42 + /// 43 + /// ```rust:norun 44 + /// docvec!["Hello", line(), "world!"] 45 + /// ``` 46 + /// 47 + /// Note: each document in a docvec is not separated in any way: the formatter 48 + /// will never break a line unless a `Document::Break` or `Document::Line` 49 + /// is used. Therefore, `docvec!["a", "b", "c"]` is equivalent to 50 + /// `"abc".to_doc()`. 51 + /// 52 + #[macro_export] 53 + macro_rules! docvec_ref_arena { 54 + () => { 55 + Document::Vec(Vec::new()) 56 + }; 57 + 58 + // A docvec![] with multiple elements. 59 + ($arena:expr, $first:expr, $($rest:expr),+ $(,)?) => { 60 + // A document that looks like this: `Vec[Vec[..rest], ..other_rest]` 61 + // is exactly the same as a flat: `Vec[..rest, ..other_rest]`. 62 + // So in case a `docvec!` starts with a `Vec` we flatten it out to avoid 63 + // having deeply nested documents. 64 + { 65 + $first.to_doc(&$arena) 66 + $(.append(&$arena, $rest))+ 67 + } 68 + }; 69 + } 70 + 71 + /// A trait that allows for objects to observe the cursor position as it is being formatted. 72 + /// This is useful for any operations that need to track the exact position a document is 73 + /// being written to in a buffer such as for source mapping. 74 + pub trait CursorPositionObserver: std::fmt::Debug { 75 + fn observe_cursor_position(&mut self, line: isize, width: isize); 76 + } 77 + 78 + // To the outside world a document is an opaque data structure. It just wraps an 79 + // id for an arena used to allocate all the `PrintableDocument`s. 80 + #[derive(Debug, Clone, Copy)] 81 + pub struct Document<'string, 'doc>(&'doc PrintableDocument<'string, 'doc>); 82 + 83 + /// Coerce a value into a Document. 84 + /// Note we do not implement this for String as a slight pressure to favour str 85 + /// over String. 86 + pub trait Documentable<'string, 'doc> { 87 + fn to_doc(self, arena: &'doc DocumentArena<'string, 'doc>) -> Document<'string, 'doc>; 88 + } 89 + 90 + impl<'string, 'doc> Documentable<'string, 'doc> for char { 91 + fn to_doc(self, arena: &'doc DocumentArena<'string, 'doc>) -> Document<'string, 'doc> { 92 + eco_format!("{self}").to_doc(arena) 93 + } 94 + } 95 + 96 + impl<'string, 'doc> Documentable<'string, 'doc> for &'string str { 97 + fn to_doc(self, arena: &'doc DocumentArena<'string, 'doc>) -> Document<'string, 'doc> { 98 + Document(arena.documents.alloc(PrintableDocument::str(self))) 99 + } 100 + } 101 + 102 + impl<'string, 'doc> Documentable<'string, 'doc> for EcoString { 103 + fn to_doc(self, arena: &'doc DocumentArena<'string, 'doc>) -> Document<'string, 'doc> { 104 + Document(arena.documents.alloc(PrintableDocument::EcoString { 105 + graphemes: self.graphemes(true).count() as isize, 106 + string: self, 107 + })) 108 + } 109 + } 110 + 111 + impl<'string, 'doc> Documentable<'string, 'doc> for &EcoString { 112 + fn to_doc(self, arena: &'doc DocumentArena<'string, 'doc>) -> Document<'string, 'doc> { 113 + self.clone().to_doc(arena) 114 + } 115 + } 116 + 117 + impl<'string, 'doc> Documentable<'string, 'doc> for isize { 118 + fn to_doc(self, arena: &'doc DocumentArena<'string, 'doc>) -> Document<'string, 'doc> { 119 + eco_format!("{self}").to_doc(arena) 120 + } 121 + } 122 + 123 + impl<'string, 'doc> Documentable<'string, 'doc> for i64 { 124 + fn to_doc(self, arena: &'doc DocumentArena<'string, 'doc>) -> Document<'string, 'doc> { 125 + eco_format!("{self}").to_doc(arena) 126 + } 127 + } 128 + 129 + impl<'string, 'doc> Documentable<'string, 'doc> for usize { 130 + fn to_doc(self, arena: &'doc DocumentArena<'string, 'doc>) -> Document<'string, 'doc> { 131 + eco_format!("{self}").to_doc(arena) 132 + } 133 + } 134 + 135 + impl<'string, 'doc> Documentable<'string, 'doc> for f64 { 136 + fn to_doc(self, arena: &'doc DocumentArena<'string, 'doc>) -> Document<'string, 'doc> { 137 + eco_format!("{self:?}").to_doc(arena) 138 + } 139 + } 140 + 141 + impl<'string, 'doc> Documentable<'string, 'doc> for u64 { 142 + fn to_doc(self, arena: &'doc DocumentArena<'string, 'doc>) -> Document<'string, 'doc> { 143 + eco_format!("{self:?}").to_doc(arena) 144 + } 145 + } 146 + 147 + impl<'string, 'doc> Documentable<'string, 'doc> for u32 { 148 + fn to_doc(self, arena: &'doc DocumentArena<'string, 'doc>) -> Document<'string, 'doc> { 149 + eco_format!("{self}").to_doc(arena) 150 + } 151 + } 152 + 153 + impl<'string, 'doc> Documentable<'string, 'doc> for u16 { 154 + fn to_doc(self, arena: &'doc DocumentArena<'string, 'doc>) -> Document<'string, 'doc> { 155 + eco_format!("{self}").to_doc(arena) 156 + } 157 + } 158 + 159 + impl<'string, 'doc> Documentable<'string, 'doc> for u8 { 160 + fn to_doc(self, arena: &'doc DocumentArena<'string, 'doc>) -> Document<'string, 'doc> { 161 + eco_format!("{self}").to_doc(arena) 162 + } 163 + } 164 + 165 + impl<'string, 'doc> Documentable<'string, 'doc> for BigInt { 166 + fn to_doc(self, arena: &'doc DocumentArena<'string, 'doc>) -> Document<'string, 'doc> { 167 + eco_format!("{self}").to_doc(arena) 168 + } 169 + } 170 + 171 + impl<'string, 'doc> Documentable<'string, 'doc> for Document<'string, 'doc> { 172 + fn to_doc(self, _arena: &'doc DocumentArena<'string, 'doc>) -> Document<'string, 'doc> { 173 + self 174 + } 175 + } 176 + 177 + impl<'string, 'doc> Documentable<'string, 'doc> for Vec<Document<'string, 'doc>> { 178 + fn to_doc(self, arena: &'doc DocumentArena<'string, 'doc>) -> Document<'string, 'doc> { 179 + arena.concat(self) 180 + } 181 + } 182 + 183 + impl<'string, 'doc, D: Documentable<'string, 'doc>> Documentable<'string, 'doc> for Option<D> { 184 + fn to_doc(self, arena: &'doc DocumentArena<'string, 'doc>) -> Document<'string, 'doc> { 185 + self.map(|documentable| documentable.to_doc(arena)) 186 + .unwrap_or(arena.nil()) 187 + } 188 + } 189 + 190 + impl<'string, 'doc> Document<'string, 'doc> { 191 + /// Groups a document. When pretty printing a group, the formatter will 192 + /// first attempt to fit the entire group on one line. If it fails, all 193 + /// `break_` documents in the group will render broken. 194 + /// 195 + /// Nested groups are handled separately to their parents, so if the 196 + /// outermost group is broken, any sub-groups might be rendered broken 197 + /// or unbroken, depending on whether they fit on a single line. 198 + pub fn group(self, arena: &'doc DocumentArena<'string, 'doc>) -> Document<'string, 'doc> { 199 + match self.0 { 200 + // Grouping a group doesn't change how it will be formatted so we 201 + // can avoid boxing it. 202 + PrintableDocument::Group(_) 203 + // Same for an empty document. 204 + | PrintableDocument::Empty 205 + // Grouping a literal string will never change how it's formatted, 206 + // we can avoid boxing it. 207 + | PrintableDocument::Str { .. } 208 + | PrintableDocument::EcoString { .. } 209 + | PrintableDocument::ZeroWidthString { .. } 210 + | PrintableDocument::CursorPositionObserver { .. } => self, 211 + 212 + PrintableDocument::Line(_) 213 + | PrintableDocument::ForceBroken(_) 214 + | PrintableDocument::NextBreakFits(..) 215 + | PrintableDocument::Break { .. } 216 + | PrintableDocument::Join(..) 217 + | PrintableDocument::Nest(..) => Document(arena.documents.alloc(PrintableDocument::Group(self))), 218 + } 219 + } 220 + 221 + /// Sets the indentation level of a document. 222 + pub fn set_nesting( 223 + self, 224 + arena: &'doc DocumentArena<'string, 'doc>, 225 + indent: isize, 226 + ) -> Document<'string, 'doc> { 227 + Document(arena.documents.alloc(PrintableDocument::Nest( 228 + indent, 229 + NestMode::Set, 230 + NestCondition::Always, 231 + self, 232 + ))) 233 + } 234 + 235 + /// Nests a document by a certain indentation. When rending linebreaks, the 236 + /// formatter will print a new line followed by the current indentation. 237 + pub fn nest( 238 + self, 239 + arena: &'doc DocumentArena<'string, 'doc>, 240 + indent: isize, 241 + ) -> Document<'string, 'doc> { 242 + Document(arena.documents.alloc(PrintableDocument::Nest( 243 + indent, 244 + NestMode::Increase, 245 + NestCondition::Always, 246 + self, 247 + ))) 248 + } 249 + 250 + /// Nests a document by a certain indentation, but only if the current 251 + /// group is broken. 252 + pub fn nest_if_broken( 253 + self, 254 + arena: &'doc DocumentArena<'string, 'doc>, 255 + indent: isize, 256 + ) -> Document<'string, 'doc> { 257 + Document(arena.documents.alloc(PrintableDocument::Nest( 258 + indent, 259 + NestMode::Increase, 260 + NestCondition::IfBroken, 261 + self, 262 + ))) 263 + } 264 + 265 + /// Forces all `break_` and `flex_break` documents in the current group 266 + /// to render broken. 267 + pub fn force_break(self, arena: &'doc DocumentArena<'string, 'doc>) -> Document<'string, 'doc> { 268 + Document(arena.documents.alloc(PrintableDocument::ForceBroken(self))) 269 + } 270 + 271 + /// Force the next `Break` to render unbroken, regardless of whether it 272 + /// fits on the line or not. 273 + pub fn next_break_fits( 274 + self, 275 + arena: &'doc DocumentArena<'string, 'doc>, 276 + mode: NextBreakFitsMode, 277 + ) -> Document<'string, 'doc> { 278 + Document( 279 + arena 280 + .documents 281 + .alloc(PrintableDocument::NextBreakFits(self, mode)), 282 + ) 283 + } 284 + 285 + /// Appends one document to another. Equivalent to `docvec![self, second]`, 286 + /// except that it `self` is already a `Document::Vec`, it will append 287 + /// directly to it instead of allocating a new vector. 288 + /// 289 + /// Useful when chaining multiple documents together in a fashion where 290 + /// they cannot be put all into one `docvec!` macro. 291 + pub fn append( 292 + self, 293 + arena: &'doc DocumentArena<'string, 'doc>, 294 + second: impl Documentable<'string, 'doc>, 295 + ) -> Document<'string, 'doc> { 296 + match self.0 { 297 + PrintableDocument::Empty => second.to_doc(arena), 298 + PrintableDocument::Line(..) 299 + | PrintableDocument::Join(..) 300 + | PrintableDocument::ForceBroken(..) 301 + | PrintableDocument::NextBreakFits(..) 302 + | PrintableDocument::Break { .. } 303 + | PrintableDocument::Nest(..) 304 + | PrintableDocument::Group(..) 305 + | PrintableDocument::Str { .. } 306 + | PrintableDocument::EcoString { .. } 307 + | PrintableDocument::ZeroWidthString { .. } 308 + | PrintableDocument::CursorPositionObserver { .. } => { 309 + let second = second.to_doc(arena); 310 + if let PrintableDocument::Empty = second.0 { 311 + self 312 + } else { 313 + Document(arena.documents.alloc(PrintableDocument::Join(self, second))) 314 + } 315 + } 316 + } 317 + } 318 + 319 + /// Surrounds a document in two delimiters. Equivalent to 320 + /// `docvec![option, self, closed]`. 321 + pub fn surround( 322 + self, 323 + arena: &'doc DocumentArena<'string, 'doc>, 324 + open: impl Documentable<'string, 'doc>, 325 + closed: impl Documentable<'string, 'doc>, 326 + ) -> Document<'string, 'doc> { 327 + open.to_doc(arena) 328 + .append(arena, self) 329 + .append(arena, closed.to_doc(arena)) 330 + } 331 + 332 + /// Returns true when the document contains no printable characters 333 + /// (whitespace and newlines are considered printable characters). 334 + pub fn is_empty(&self, arena: &DocumentArena<'string, 'doc>) -> bool { 335 + match self.0 { 336 + PrintableDocument::Empty => true, 337 + PrintableDocument::Line(n) => *n == 0, 338 + PrintableDocument::EcoString { string, .. } => string.is_empty(), 339 + PrintableDocument::Str { string, .. } => string.is_empty(), 340 + // assuming `broken` and `unbroken` are equivalent 341 + PrintableDocument::Break { broken, .. } => broken.is_empty(), 342 + PrintableDocument::ForceBroken(document) 343 + | PrintableDocument::Nest(_, _, _, document) 344 + | PrintableDocument::Group(document) 345 + | PrintableDocument::NextBreakFits(document, _) => document.is_empty(arena), 346 + PrintableDocument::Join(first, second) => { 347 + first.is_empty(arena) && second.is_empty(arena) 348 + } 349 + // Zero-width strings don't count towards line length, but they are 350 + // still printed and so are not empty. (Unless their string contents 351 + // is also empty) 352 + PrintableDocument::ZeroWidthString { string } => string.is_empty(), 353 + PrintableDocument::CursorPositionObserver { .. } => true, 354 + } 355 + } 356 + 357 + /// Prints a document into a `String`, attempting to limit lines to `limit` 358 + /// characters in length. 359 + pub fn to_pretty_string(self, limit: isize) -> String { 360 + let mut buffer = String::new(); 361 + self.pretty_print(limit, &mut buffer) 362 + .expect("Writing to string buffer failed"); 363 + buffer 364 + } 365 + 366 + /// Prints a document into `writer`, attempting to limit lines to `limit` 367 + /// characters in length. 368 + pub fn pretty_print(self, limit: isize, writer: &mut impl Utf8Writer) -> Result<()> { 369 + let docs = im::vector![(0, Mode::Unbroken, self)]; 370 + format(writer, limit, docs)?; 371 + Ok(()) 372 + } 373 + } 374 + 375 + /// A pretty printable document. A tree structure, made up of text and other 376 + /// elements which determine how it can be formatted. 377 + /// 378 + /// The variants of this enum should probably not be constructed directly, 379 + /// rather use the helper functions of the same names to construct them. 380 + /// For example, use `line()` instead of `Document::Line(1)`. 381 + /// 382 + #[derive(Debug, Clone)] 383 + enum PrintableDocument<'string, 'doc> { 384 + /// A mandatory linebreak. This is always printed as a string of newlines, 385 + /// equal in length to the number specified. 386 + Line(usize), 387 + 388 + /// Forces the breaks of the wrapped document to be considered as not 389 + /// fitting on a single line. Used in combination with a `Group` it can be 390 + /// used to force its `Break`s to always break. 391 + ForceBroken(Document<'string, 'doc>), 392 + 393 + /// Ignore the next break, forcing it to render as unbroken. 394 + NextBreakFits(Document<'string, 'doc>, NextBreakFitsMode), 395 + 396 + /// A document after which the formatter can insert a newline. This determines 397 + /// where line breaks can occur, outside of hardcoded `Line`s. 398 + /// See `break_` and `flex_break` for usage. 399 + Break { 400 + broken: &'string str, 401 + unbroken: &'string str, 402 + kind: BreakKind, 403 + }, 404 + 405 + /// Join multiple documents together. The documents are not separated in any 406 + /// way: the formatter will only print newlines if `Document::Break` or 407 + /// `Document::Line` is used. 408 + // maybe experiment with Slice(&'doc [Document<'string, 'doc>]), 409 + Join(Document<'string, 'doc>, Document<'string, 'doc>), 410 + 411 + /// Nests the given document by the given indent, depending on the specified 412 + /// condition. See `Document::nest`, `Document::set_nesting` and 413 + /// `Document::nest_if_broken` for usages. 414 + Nest(isize, NestMode, NestCondition, Document<'string, 'doc>), 415 + 416 + /// Groups a document. When pretty printing a group, the formatter will 417 + /// first attempt to fit the entire group on one line. If it fails, all 418 + /// `break_` documents in the group will render broken. 419 + /// 420 + /// Nested groups are handled separately to their parents, so if the 421 + /// outermost group is broken, any sub-groups might be rendered broken 422 + /// or unbroken, depending on whether they fit on a single line. 423 + Group(Document<'string, 'doc>), 424 + 425 + /// Renders a string slice. This will always render the string verbatim, 426 + /// without any line breaks or other modifications to it. 427 + Str { 428 + string: &'string str, 429 + /// The number of extended grapheme clusters in the string. 430 + /// This is what the pretty printer uses as the width of the string as it 431 + /// is closes to what a human would consider the "length" of a string. 432 + /// 433 + /// Since computing the number of grapheme clusters requires walking over 434 + /// the string we precompute it to avoid iterating through a string over 435 + /// and over again in the pretty printing algorithm. 436 + /// 437 + graphemes: isize, 438 + }, 439 + 440 + /// Renders an `EcoString`. This will always render the string verbatim, 441 + /// without any line breaks or other modifications to it. 442 + EcoString { 443 + string: EcoString, 444 + /// The number of extended grapheme clusters in the string. 445 + /// This is what the pretty printer uses as the width of the string as it 446 + /// is closes to what a human would consider the "length" of a string. 447 + /// 448 + /// Since computing the number of grapheme clusters requires walking over 449 + /// the string we precompute it to avoid iterating through a string over 450 + /// and over again in the pretty printing algorithm. 451 + /// 452 + graphemes: isize, 453 + }, 454 + 455 + /// A string that is not taken into account when determining line length. 456 + /// This is useful for additional formatting text which won't be rendered 457 + /// in the final output, such as ANSI codes or HTML elements. 458 + ZeroWidthString { 459 + string: EcoString, 460 + }, 461 + 462 + /// A node that gets notified of the cursor position as it is being formatted. 463 + /// This allows for processes outside of the final output to be notified of 464 + /// the cursor position and perform actions based on it, such as recording 465 + /// the span of the node in the generated source code for a source mapping. 466 + CursorPositionObserver { 467 + observer: Rc<RefCell<dyn CursorPositionObserver>>, 468 + }, 469 + Empty, 470 + } 471 + 472 + impl<'a, 'doc> PrintableDocument<'a, 'doc> { 473 + fn str(str: &'a str) -> Self { 474 + PrintableDocument::Str { 475 + graphemes: str.graphemes(true).count() as isize, 476 + string: str, 477 + } 478 + } 479 + } 480 + 481 + /// The kind of line break this `Document::Break` is. 482 + #[derive(Debug, Clone, Copy, PartialEq, Eq)] 483 + pub enum BreakKind { 484 + /// A `flex_break`. 485 + Flex, 486 + /// A `break_`. 487 + Strict, 488 + } 489 + 490 + #[derive(Debug, Clone, Copy, PartialEq, Eq)] 491 + enum Mode { 492 + /// The mode used when a group doesn't fit on a single line: when `Broken` 493 + /// the `Break`s inside it will be rendered as newlines, splitting the 494 + /// group. 495 + Broken, 496 + 497 + /// The default mode used when a group can fit on a single line: all its 498 + /// `Break`s will be rendered as their unbroken string and kept on a single 499 + /// line. 500 + Unbroken, 501 + 502 + /// This mode is used by the `NextBreakFit` document to force a break to be 503 + /// considered as broken. 504 + ForcedBroken, 505 + 506 + /// This mode is used to disable a `NextBreakFit` document. 507 + ForcedUnbroken, 508 + } 509 + 510 + /// A flag that can be used to enable or disable a `NextBreakFit` document. 511 + #[derive(Debug, Clone, Copy, PartialEq, Eq)] 512 + pub enum NextBreakFitsMode { 513 + Enabled, 514 + Disabled, 515 + } 516 + 517 + /// A flag that can be used to conditionally disable a `Nest` document. 518 + #[derive(Debug, Clone, Copy, PartialEq, Eq)] 519 + pub enum NestCondition { 520 + /// This always applies the nesting. This is a sensible default that will 521 + /// work for most of the cases. 522 + Always, 523 + /// Only applies the nesting if the wrapping `Group` couldn't fit on a 524 + /// single line and has been broken. 525 + IfBroken, 526 + } 527 + 528 + /// Used to change the way nesting of documents work. 529 + #[derive(Debug, Clone, Copy, PartialEq, Eq)] 530 + pub enum NestMode { 531 + /// If the nesting mode is `Increase`, the current indentation will be 532 + /// increased by the specified value. 533 + Increase, 534 + /// If the nesting mode is `Set`, the current indentation is going to be set 535 + /// to exactly the specified value. 536 + /// 537 + /// `doc.nest(2).set_nesting(0)` 538 + /// "wibble 539 + /// wobble <- no indentation is added! 540 + /// wubble" 541 + Set, 542 + } 543 + 544 + /// A structure used to allocate and manipulate documents. 545 + /// All documents are allocated on an arena. 546 + /// Some documents are only allocated once and can be used accessed as fields 547 + /// of this structure to avoid wasting loads of allocations on them. 548 + pub struct DocumentArena<'string, 'doc> { 549 + documents: Arena<PrintableDocument<'string, 'doc>>, 550 + } 551 + 552 + impl<'string, 'doc> std::fmt::Debug for DocumentArena<'string, 'doc> { 553 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 554 + f.debug_struct("DocumentArena").finish() 555 + } 556 + } 557 + 558 + impl<'string, 'doc> DocumentArena<'string, 'doc> { 559 + pub fn new() -> Self { 560 + let documents = Arena::new(); 561 + Self { documents } 562 + } 563 + 564 + pub fn nil(&'doc self) -> Document<'string, 'doc> { 565 + Document(self.documents.alloc(PrintableDocument::Empty)) 566 + } 567 + 568 + pub fn line(&'doc self) -> Document<'string, 'doc> { 569 + self.lines(1) 570 + } 571 + 572 + pub fn position_observer( 573 + &'doc self, 574 + observer: Rc<RefCell<dyn CursorPositionObserver>>, 575 + ) -> Document<'string, 'doc> { 576 + Document( 577 + self.documents 578 + .alloc(PrintableDocument::CursorPositionObserver { observer }), 579 + ) 580 + } 581 + 582 + /// Renders a string of newlines, equal in length to the number provided. 583 + #[inline] 584 + pub fn lines(&'doc self, i: usize) -> Document<'string, 'doc> { 585 + Document(self.documents.alloc(PrintableDocument::Line(i))) 586 + } 587 + 588 + /// A document after which the formatter can insert a newline. This determines 589 + /// where line breaks can occur, outside of hardcoded `Line`s. 590 + /// 591 + /// If the formatter determines that a group cannot fit on a single line, 592 + /// all breaks in the group will be rendered as broken. Otherwise, they 593 + /// will be rendered as unbroken. 594 + /// 595 + /// A broken `Break` renders the `broken` string, followed by a newline. 596 + /// An unbroken `Break` renders the `unbroken` string by itself. 597 + /// 598 + /// For example: 599 + /// ```rust:norun 600 + /// let document = docvec!["Hello", break_("", ", "), "world!"]; 601 + /// assert_eq!(document.to_pretty_string(20), "Hello, world!"); 602 + /// assert_eq!(document.to_pretty_string(10), "Hello\nworld!"); 603 + /// ``` 604 + /// 605 + pub fn break_( 606 + &'doc self, 607 + broken: &'string str, 608 + unbroken: &'string str, 609 + ) -> Document<'string, 'doc> { 610 + Document(self.documents.alloc(PrintableDocument::Break { 611 + broken, 612 + unbroken, 613 + kind: BreakKind::Strict, 614 + })) 615 + } 616 + 617 + /// A document after which the formatter can insert a newline, similar to 618 + /// `break_()`. The difference is that when a group is rendered broken, all 619 + /// breaks are rendered broken. However, `flex_break` decides whether to 620 + /// break or not for every individual `flex_break`. 621 + /// 622 + /// For example: 623 + /// ```rust:norun 624 + /// let with_breaks = docvec!["Hello", break_("", ", "), "pretty", break_("", ", "), "printed!"]; 625 + /// assert_eq!(with_breaks.to_pretty_string(20), "Hello\npretty\nprinted!"); 626 + /// 627 + /// let with_flex_breaks = docvec!["Hello", flex_break("", ", "), "pretty", flex_break("", ", "), "printed!"]; 628 + /// assert_eq!(with_flex_breaks.to_pretty_string(20), "Hello, pretty\nprinted!"); 629 + /// ``` 630 + /// 631 + pub fn flex_break( 632 + &'doc self, 633 + broken: &'string str, 634 + unbroken: &'string str, 635 + ) -> Document<'string, 'doc> { 636 + Document(self.documents.alloc(PrintableDocument::Break { 637 + broken, 638 + unbroken, 639 + kind: BreakKind::Flex, 640 + })) 641 + } 642 + 643 + /// A string that is not taken into account when determining line length. 644 + /// This is useful for additional formatting text which won't be rendered 645 + /// in the final output, such as ANSI codes or HTML elements. 646 + /// 647 + /// For example: 648 + /// ```rust:norun 649 + /// let document = docvec!["Hello", zero_width_string("This is a very long string"), break_("", ""), "world"]; 650 + /// assert_eq!(document.to_pretty_string(20), "HelloThis is a very long stringworld"); 651 + /// ``` 652 + /// 653 + pub fn zero_width_string(&'doc self, string: EcoString) -> Document<'string, 'doc> { 654 + Document( 655 + self.documents 656 + .alloc(PrintableDocument::ZeroWidthString { string }), 657 + ) 658 + } 659 + 660 + pub fn concat( 661 + &'doc self, 662 + documents: impl IntoIterator<Item = Document<'string, 'doc>>, 663 + ) -> Document<'string, 'doc> { 664 + let mut documents = documents.into_iter().peekable(); 665 + let Some(mut previous) = documents.next() else { 666 + return self.nil(); 667 + }; 668 + 669 + while let Some(next) = documents.next() { 670 + previous = Document( 671 + self.documents 672 + .alloc(PrintableDocument::Join(previous, next)), 673 + ); 674 + } 675 + 676 + previous 677 + } 678 + 679 + /// Joins an iterator into a single document, interspersing each element with 680 + /// another document. This is useful for example in argument lists, where a 681 + /// list of arguments must all be separated with a comma. 682 + pub fn join( 683 + &'doc self, 684 + elements: impl IntoIterator<Item = Document<'string, 'doc>>, 685 + separator: Document<'string, 'doc>, 686 + ) -> Document<'string, 'doc> { 687 + let mut elements = elements.into_iter().peekable(); 688 + let Some(mut previous) = elements.next() else { 689 + return self.nil(); 690 + }; 691 + 692 + while let Some(next) = elements.next() { 693 + let doc = Document( 694 + self.documents 695 + .alloc(PrintableDocument::Join(previous, separator)), 696 + ); 697 + previous = Document(self.documents.alloc(PrintableDocument::Join(doc, next))); 698 + } 699 + 700 + previous 701 + } 702 + } 703 + 704 + fn format<'string, 'doc>( 705 + writer: &mut impl Utf8Writer, 706 + limit: isize, 707 + mut docs: im::Vector<(isize, Mode, Document<'string, 'doc>)>, 708 + ) -> Result<()> { 709 + let mut line: isize = 0; 710 + let mut width: isize = 0; 711 + // As long as there are documents to print we'll take each one by one and 712 + // output the corresponding string to the given writer. 713 + // 714 + // Each document in the `docs` queue also has an accompanying indentation 715 + // and mode: 716 + // - the indentation is used to keep track of the current indentation, 717 + // you might notice in [ref:format-nest] that it adds documents to the 718 + // queue increasing their current indentation. 719 + // - the mode is used to keep track of the state of the documents inside a 720 + // group. For example, if a group doesn't fit on a single line its 721 + // documents will be split into multiple lines and the mode set to 722 + // `Broken` to keep track of this. 723 + while let Some((indent, mode, document)) = docs.pop_front() { 724 + match document.0 { 725 + // When we run into a line we print the given number of newlines and 726 + // add the indentation required by the given document. 727 + PrintableDocument::Line(count) => { 728 + for _ in 0..*count { 729 + writer.str_write("\n")?; 730 + } 731 + line += *count as isize; 732 + for _ in 0..indent { 733 + writer.str_write(" ")?; 734 + } 735 + width = indent; 736 + } 737 + 738 + // Flex breaks are NOT conditional to the mode: if the mode is 739 + // already `Unbroken`, then the break is left unbroken (like strict 740 + // breaks); any other mode is ignored. 741 + // A flexible break will only be split if the following documents 742 + // can't fit on the same line; otherwise, it is just displayed as an 743 + // unbroken `Break`. 744 + PrintableDocument::Break { 745 + broken, 746 + unbroken, 747 + kind: BreakKind::Flex, 748 + } => { 749 + let unbroken_width = width + unbroken.len() as isize; 750 + // Every time we need to check again if the remaining piece can 751 + // fit. If it does, the flexible break is not broken. 752 + if mode == Mode::Unbroken || fits(limit, unbroken_width, docs.clone()) { 753 + writer.str_write(unbroken)?; 754 + width = unbroken_width; 755 + } else { 756 + writer.str_write(broken)?; 757 + writer.str_write("\n")?; 758 + line += 1; 759 + for _ in 0..indent { 760 + writer.str_write(" ")?; 761 + } 762 + width = indent; 763 + } 764 + } 765 + 766 + // Strict breaks are conditional to the mode. They differ from 767 + // flexible break because, if a group gets split - that is the mode 768 + // is `Broken` or `ForceBroken` - ALL of the breaks in that group 769 + // will be split. You can notice the difference with flexible breaks 770 + // because here we only check the mode and then take action; before 771 + // we would try and see if the remaining documents fit on a single 772 + // line before deciding if the (flexible) break can be split or not. 773 + PrintableDocument::Break { 774 + broken, 775 + unbroken, 776 + kind: BreakKind::Strict, 777 + } => match mode { 778 + // If the mode requires the break to be broken, then its broken 779 + // string is printed, then we start a newline and indent it 780 + // according to the current indentation level. 781 + Mode::Broken | Mode::ForcedBroken => { 782 + writer.str_write(broken)?; 783 + writer.str_write("\n")?; 784 + line += 1; 785 + for _ in 0..indent { 786 + writer.str_write(" ")?; 787 + } 788 + width = indent; 789 + } 790 + // If the mode doesn't require the break to be broken, then its 791 + // unbroken string is printed as if it were a normal string; 792 + // also updating the width of the current line. 793 + Mode::Unbroken | Mode::ForcedUnbroken => { 794 + writer.str_write(unbroken)?; 795 + width += unbroken.len() as isize 796 + } 797 + }, 798 + 799 + // Strings are printed as they are and the current width is 800 + // increased accordingly. 801 + PrintableDocument::EcoString { string, graphemes } => { 802 + width += graphemes; 803 + writer.str_write(&string)?; 804 + } 805 + 806 + PrintableDocument::Str { string, graphemes } => { 807 + width += graphemes; 808 + writer.str_write(string)?; 809 + } 810 + 811 + PrintableDocument::ZeroWidthString { string } => { 812 + // We write the string, but do not increment the length 813 + writer.str_write(&string)?; 814 + } 815 + 816 + // If multiple documents need to be printed, then they are all 817 + // pushed to the front of the queue and will be printed one by one. 818 + PrintableDocument::Join(first, second) => { 819 + // Just like `fits`, the elements will be pushed _on the front_ 820 + // of the queue. In order to keep their original order they need 821 + // to be pushed in reverse order. 822 + docs.push_front((indent, mode, *second)); 823 + docs.push_front((indent, mode, *first)); 824 + } 825 + 826 + // A `Nest` document doesn't result in anything being printed, its 827 + // only effect is to increase the current nesting level for the 828 + // wrapped document [tag:format-nest]. 829 + PrintableDocument::Nest(i, nest_mode, condition, doc) => match (condition, mode) { 830 + // The nesting is only applied under two conditions: 831 + // - either the nesting condition is `Always`. 832 + // - or the condition is `IfBroken` and the group was actually 833 + // broken (that is, the current mode is `Broken`). 834 + (NestCondition::Always, _) | (NestCondition::IfBroken, Mode::Broken) => { 835 + let new_indent = match nest_mode { 836 + NestMode::Increase => indent + i, 837 + NestMode::Set => *i, 838 + }; 839 + docs.push_front((new_indent, mode, *doc)) 840 + } 841 + // If none of the above conditions is met, then the nesting is 842 + // not applied. 843 + _ => docs.push_front((indent, mode, *doc)), 844 + }, 845 + 846 + PrintableDocument::Group(doc) => { 847 + // When we see a group we first try and see if it can fit on a 848 + // single line without breaking any break; that is why we use 849 + // the `Unbroken` mode here: we want to try to fit everything on 850 + // a single line. 851 + let group_docs = im::vector![(indent, Mode::Unbroken, *doc)]; 852 + if fits(limit, width, group_docs) { 853 + // If everything can stay on a single line we print the 854 + // wrapped document with the `Unbroken` mode, leaving all 855 + // the group's break as unbroken. 856 + docs.push_front((indent, Mode::Unbroken, *doc)); 857 + } else { 858 + // Otherwise, we need to break the group. We print the 859 + // wrapped document changing its mode to `Broken` so that 860 + // all its breaks will be split on newlines. 861 + docs.push_front((indent, Mode::Broken, *doc)); 862 + } 863 + } 864 + 865 + // `ForceBroken` and `NextBreakFits` only change the way the `fit` 866 + // function works but do not actually change the formatting of a 867 + // document by themselves. That's why when we run into those we 868 + // just go on printing the wrapped document without altering the 869 + // current mode. 870 + PrintableDocument::ForceBroken(document) 871 + | PrintableDocument::NextBreakFits(document, _) => { 872 + docs.push_front((indent, mode, *document)); 873 + } 874 + 875 + PrintableDocument::CursorPositionObserver { observer } => { 876 + // Notify the observer of the current cursor position 877 + observer.borrow_mut().observe_cursor_position(line, width); 878 + } 879 + 880 + PrintableDocument::Empty => (), 881 + } 882 + } 883 + Ok(()) 884 + } 885 + 886 + fn fits<'string, 'doc>( 887 + limit: isize, 888 + mut current_width: isize, 889 + mut docs: im::Vector<(isize, Mode, Document<'string, 'doc>)>, 890 + ) -> bool { 891 + // The `fits` function is going to take each document from the `docs` queue 892 + // and check if those can fit on a single line. In order to do so documents 893 + // are going to be pushed in front of this queue and have to be accompanied 894 + // by additional information: 895 + // - the document indentation, that can be increased by the `Nest` block. 896 + // - the current mode; this is needed to know if a group is being broken or 897 + // not and treat `Break`s differently as a consequence. You can see how 898 + // the behaviour changes in [ref:break-fit]. 899 + // 900 + // The loop might be broken earlier without checking all documents under one 901 + // of two conditions: 902 + // - the documents exceed the line `limit` and surely won't fit 903 + // [ref:document-unfit]. 904 + // - the documents are sure to fit the line - for example, if we meet a 905 + // broken `Break` [ref:break-fit] or a newline [ref:newline-fit]. 906 + loop { 907 + // [tag:document-unfit] If we've exceeded the maximum width allowed for 908 + // a line, it means that the document won't fit on a single line, we can 909 + // break the loop. 910 + if current_width > limit { 911 + return false; 912 + }; 913 + 914 + // We start by checking the first document of the queue. If there's no 915 + // documents then we can safely say that it fits (if reached this point 916 + // it means that the limit wasn't exceeded). 917 + let (indent, mode, document) = match docs.pop_front() { 918 + Some(x) => x, 919 + None => return true, 920 + }; 921 + 922 + match document.0 { 923 + // If a document is marked as `ForceBroken` we can immediately say 924 + // that it doesn't fit, so that every break is going to be 925 + // forcefully broken. 926 + PrintableDocument::ForceBroken(doc) => match mode { 927 + // If the mode is `ForcedBroken` it means that we have to ignore 928 + // this break [ref:forced-broken], so we go check the inner 929 + // document ignoring the effects of this one. 930 + Mode::ForcedBroken => docs.push_front((indent, mode, *doc)), 931 + Mode::Broken | Mode::Unbroken | Mode::ForcedUnbroken => return false, 932 + }, 933 + 934 + // [tag:newline-fit] When we run into a line we know that the 935 + // document has a bit that fits in the current line; if it didn't 936 + // fit (that is, it exceeded the maximum allowed width) the loop 937 + // would have been broken by one of the earlier checks. 938 + PrintableDocument::Line(_) => return true, 939 + 940 + // If the nesting level is increased we go on checking the wrapped 941 + // document and increase its indentation level based on the nesting 942 + // condition. 943 + PrintableDocument::Nest(i, nest_mode, condition, doc) => match condition { 944 + NestCondition::IfBroken => docs.push_front((indent, mode, *doc)), 945 + NestCondition::Always => { 946 + let new_indent = match nest_mode { 947 + NestMode::Increase => indent + i, 948 + NestMode::Set => *i, 949 + }; 950 + docs.push_front((new_indent, mode, *doc)) 951 + } 952 + }, 953 + 954 + // As a general rule, a group fits if it can stay on a single line 955 + // without its breaks being broken down. 956 + PrintableDocument::Group(doc) => match mode { 957 + // If an outer group was broken, we still try to fit the inner 958 + // group on a single line, that's why for the inner document 959 + // we change the mode back to `Unbroken`. 960 + Mode::Broken => docs.push_front((indent, Mode::Unbroken, *doc)), 961 + // Any other mode is preserved as-is: if the mode is forced it 962 + // has to be left unchanged, and if the mode is already unbroken 963 + // there's no need to change it. 964 + Mode::Unbroken | Mode::ForcedBroken | Mode::ForcedUnbroken => { 965 + docs.push_front((indent, mode, *doc)) 966 + } 967 + }, 968 + 969 + // When we run into a string we increase the current_width; looping 970 + // back we will check if we've exceeded the maximum allowed width. 971 + PrintableDocument::Str { graphemes, .. } 972 + | PrintableDocument::EcoString { graphemes, .. } => current_width += graphemes, 973 + 974 + // Zero width strings do nothing: they do not contribute to line length 975 + PrintableDocument::ZeroWidthString { .. } 976 + | PrintableDocument::CursorPositionObserver { .. } => {} 977 + 978 + // If we get to a break we need to first see if it has to be 979 + // rendered as its unbroken or broken string, depending on the mode. 980 + PrintableDocument::Break { unbroken, .. } => match mode { 981 + // [tag:break-fit] If the break has to be broken we're done! 982 + // We haven't exceeded the maximum length (otherwise the loop 983 + // iteration would have stopped with one of the earlier checks), 984 + // and - since it needs to be broken - we'll have to go on a new 985 + // line anyway. 986 + // This means that the document inspected so far will fit on a 987 + // single line, thus we return true. 988 + Mode::Broken | Mode::ForcedBroken => return true, 989 + // If the break is not broken then it will be rendered inline as 990 + // its unbroken string, so we treat it exactly as if it were a 991 + // normal string. 992 + Mode::Unbroken | Mode::ForcedUnbroken => current_width += unbroken.len() as isize, 993 + }, 994 + 995 + // The `NextBreakFits` can alter the current mode to `ForcedBroken` 996 + // or `ForcedUnbroken` based on its enabled flag. 997 + PrintableDocument::NextBreakFits(doc, enabled) => match enabled { 998 + // [tag:disable-next-break] If it is disabled then we check the 999 + // wrapped document changing the mode to `ForcedUnbroken`. 1000 + NextBreakFitsMode::Disabled => { 1001 + docs.push_front((indent, Mode::ForcedUnbroken, *doc)) 1002 + } 1003 + NextBreakFitsMode::Enabled => match mode { 1004 + // If we're in `ForcedUnbroken` mode it means that the check 1005 + // was disabled by a document wrapping this one 1006 + // [ref:disable-next-break]; that's why we do nothing and 1007 + // check the wrapped document as if it were a normal one. 1008 + Mode::ForcedUnbroken => docs.push_front((indent, mode, *doc)), 1009 + // [tag:forced-broken] Any other mode is turned into 1010 + // `ForcedBroken` so that when we run into a break, the 1011 + // response to the question "Does the document fit?" will be 1012 + // yes [ref:break-fit]. 1013 + // This is why this is called `NextBreakFit` I think. 1014 + Mode::Broken | Mode::Unbroken | Mode::ForcedBroken => { 1015 + docs.push_front((indent, Mode::ForcedBroken, *doc)) 1016 + } 1017 + }, 1018 + }, 1019 + 1020 + // If there's a sequence of documents we will check each one, one 1021 + // after the other to see if - as a whole - they can fit on a single 1022 + // line. 1023 + PrintableDocument::Join(first, second) => { 1024 + // The array needs to be reversed to preserve the order of the 1025 + // documents since each one is pushed _to the front_ of the 1026 + // queue of documents to check. 1027 + docs.push_front((indent, mode, *second)); 1028 + docs.push_front((indent, mode, *first)); 1029 + } 1030 + 1031 + PrintableDocument::Empty => (), 1032 + } 1033 + } 1034 + }