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

Configure Feed

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

use the arena for printing documentation

author
Giacomo Cavalieri
committer
Louis Pilfold
date (Jun 25, 2026, 1:38 PM +0100) commit 05402983 parent a1f346e1 change-id ptsvtsty
+320 -155
+276 -155
compiler-core/src/docs/printer.rs
··· 7 7 }; 8 8 9 9 use ecow::{EcoString, eco_format}; 10 - use itertools::Itertools; 11 10 12 11 use crate::{ 13 12 ast::{ 14 13 ArgNames, CustomType, Function, Publicity, RecordConstructorArg, SrcSpan, TypeAlias, 15 14 TypedArg, TypedDefinitions, TypedModuleConstant, TypedRecordConstructor, 16 15 }, 17 - docvec, 18 - pretty::{Document, Documentable, break_, join, line, nil, zero_width_string}, 19 16 type_::{ 20 17 Deprecation, PRELUDE_MODULE_NAME, PRELUDE_PACKAGE_NAME, Type, TypeVar, 21 18 printer::{Names, PrintMode}, 22 19 }, 23 20 }; 21 + use pretty_arena::*; 24 22 25 23 use super::{ 26 24 Dependency, DependencyKind, DocsValues, TypeConstructor, TypeConstructorArg, TypeDefinition, ··· 62 60 dependencies: &'a HashMap<EcoString, Dependency>, 63 61 } 64 62 65 - impl Printer<'_> { 66 - pub fn new<'a>( 63 + impl<'a, 'doc> Printer<'a> { 64 + pub fn new( 67 65 package: EcoString, 68 66 module: EcoString, 69 67 names: &'a Names, ··· 88 86 self.options = options; 89 87 } 90 88 91 - pub fn type_definitions<'a>( 89 + pub fn type_definitions( 92 90 &mut self, 93 91 source_links: &SourceLinker, 94 92 definitions: &'a TypedDefinitions, 95 93 ) -> Vec<TypeDefinition<'a>> { 96 94 let mut type_definitions = vec![]; 95 + let arena = DocumentArena::new(); 97 96 98 97 for CustomType { 99 98 location, ··· 113 112 114 113 type_definitions.push(TypeDefinition { 115 114 name, 116 - definition: print(self.custom_type(name, parameters, constructors, *opaque)), 115 + definition: print(self.custom_type( 116 + &arena, 117 + name, 118 + parameters, 119 + constructors, 120 + *opaque, 121 + )), 117 122 raw_definition: self 118 - .raw(|this| this.custom_type(name, parameters, constructors, *opaque)), 123 + .raw(|this| this.custom_type(&arena, name, parameters, constructors, *opaque)), 119 124 documentation: markdown_documentation(documentation), 120 125 text_documentation: text_documentation(documentation), 121 126 deprecation_message: match deprecation { ··· 128 133 constructors 129 134 .iter() 130 135 .map(|constructor| TypeConstructor { 131 - definition: print(self.record_constructor(constructor)), 132 - raw_definition: self.raw(|this| this.record_constructor(constructor)), 136 + definition: print(self.record_constructor(&arena, constructor)), 137 + raw_definition: self 138 + .raw(|this| this.record_constructor(&arena, constructor)), 133 139 documentation: markdown_documentation(&constructor.documentation), 134 140 text_documentation: text_documentation(&constructor.documentation), 135 141 arguments: constructor ··· 167 173 } 168 174 type_definitions.push(TypeDefinition { 169 175 name, 170 - definition: print(self.type_alias(name, type_, parameters).group()), 171 - raw_definition: self.raw(|this| this.type_alias(name, type_, parameters).group()), 176 + definition: print( 177 + self.type_alias(&arena, name, type_, parameters) 178 + .group(&arena), 179 + ), 180 + raw_definition: self.raw(|this| { 181 + this.type_alias(&arena, name, type_, parameters) 182 + .group(&arena) 183 + }), 172 184 documentation: markdown_documentation(documentation), 173 185 text_documentation: text_documentation(documentation), 174 186 constructors: vec![], ··· 186 198 } 187 199 188 200 /// Print a definition without HTML highlighting, such as for search data 189 - fn raw<'a, F>(&mut self, definition: F) -> String 190 - where 191 - F: FnOnce(&mut Self) -> Document<'a>, 192 - { 201 + fn raw<'doc1, 'a1: 'doc1>( 202 + &mut self, 203 + definition: impl FnOnce(&mut Self) -> Document<'a1, 'doc1>, 204 + ) -> String { 193 205 let options = self.options; 194 206 // Turn off highlighting for this definition 195 207 self.options = PrintOptions { ··· 202 214 format!("```\n{result}\n```") 203 215 } 204 216 205 - pub fn value_definitions<'a>( 217 + pub fn value_definitions( 206 218 &mut self, 207 219 source_links: &SourceLinker, 208 220 definitions: &'a TypedDefinitions, 209 221 ) -> Vec<DocsValues<'a>> { 210 222 let mut value_definitions = vec![]; 223 + let arena = DocumentArena::new(); 211 224 212 225 for Function { 213 226 location, ··· 233 246 234 247 value_definitions.push(DocsValues { 235 248 name, 236 - definition: print(self.function_signature(name, arguments, return_type)), 249 + definition: print(self.function_signature(&arena, name, arguments, return_type)), 237 250 raw_definition: self 238 - .raw(|this| this.function_signature(name, arguments, return_type)), 251 + .raw(|this| this.function_signature(&arena, name, arguments, return_type)), 239 252 documentation: markdown_documentation(documentation), 240 253 text_documentation: text_documentation(documentation), 241 254 source_url: source_links.url(*location), ··· 262 275 263 276 value_definitions.push(DocsValues { 264 277 name, 265 - definition: print(self.constant(name, type_)), 266 - raw_definition: self.raw(|this| this.constant(name, type_)), 278 + definition: print(self.constant(&arena, name, type_)), 279 + raw_definition: self.raw(|this| this.constant(&arena, name, type_)), 267 280 documentation: markdown_documentation(documentation), 268 281 text_documentation: text_documentation(documentation), 269 282 source_url: source_links.url(*location), ··· 278 291 value_definitions 279 292 } 280 293 281 - fn custom_type<'a>( 294 + fn custom_type( 282 295 &mut self, 296 + arena: &'doc DocumentArena<'a, 'doc>, 283 297 name: &'a str, 284 298 parameters: &'a [(SrcSpan, EcoString)], 285 299 constructors: &'a [TypedRecordConstructor], 286 300 opaque: bool, 287 - ) -> Document<'a> { 301 + ) -> Document<'a, 'doc> { 288 302 let arguments = if parameters.is_empty() { 289 - nil() 303 + EMPTY_DOCUMENT 290 304 } else { 291 305 Self::wrap_arguments( 306 + arena, 292 307 parameters 293 308 .iter() 294 - .map(|(_, parameter)| self.variable(parameter)), 309 + .map(|(_, parameter)| self.variable(arena, parameter)), 295 310 ) 296 311 }; 297 312 298 - let keywords = if opaque { 299 - "pub opaque type " 300 - } else { 301 - "pub type " 302 - }; 303 - 304 - let type_head = docvec![self.keyword(keywords), self.title(name), arguments]; 313 + let type_head = docvec![ 314 + arena, 315 + self.keyword( 316 + arena, 317 + if opaque { 318 + PUB_OPAQUE_TYPE_SPACE_DOCUMENT 319 + } else { 320 + PUB_TYPE_SPACE_DOCUMENT 321 + } 322 + ), 323 + self.title(arena, name), 324 + arguments 325 + ]; 305 326 306 327 if constructors.is_empty() || opaque { 307 328 return type_head; 308 329 } 309 330 310 - let constructors = constructors 311 - .iter() 312 - .map(|constructor| { 313 - line() 314 - .append(self.record_constructor(constructor)) 315 - .nest(INDENT) 316 - }) 317 - .collect_vec(); 331 + let constructors = arena.concat(constructors.iter().map(|constructor| { 332 + LINE_DOCUMENT 333 + .append(arena, self.record_constructor(arena, constructor)) 334 + .nest(arena, INDENT) 335 + })); 318 336 319 - docvec![type_head, " {", constructors, line(), "}"] 337 + docvec![ 338 + arena, 339 + type_head, 340 + SPACE_OPEN_CURLY_DOCUMENT, 341 + constructors, 342 + LINE_DOCUMENT, 343 + CLOSE_CURLY_DOCUMENT 344 + ] 320 345 } 321 346 322 - pub fn record_constructor<'a>( 347 + pub fn record_constructor( 323 348 &mut self, 349 + arena: &'doc DocumentArena<'a, 'doc>, 324 350 constructor: &'a TypedRecordConstructor, 325 - ) -> Document<'a> { 351 + ) -> Document<'a, 'doc> { 326 352 if constructor.arguments.is_empty() { 327 - return self.title(&constructor.name); 353 + return self.title(arena, &constructor.name); 328 354 } 329 355 330 356 let arguments = constructor.arguments.iter().map( 331 357 |RecordConstructorArg { label, type_, .. }| match label { 332 358 Some((_, label)) => self 333 - .variable(label) 334 - .append(": ") 335 - .append(self.type_(type_, PrintMode::Normal)), 336 - None => self.type_(type_, PrintMode::Normal), 359 + .variable(arena, label) 360 + .append(arena, COLON_SPACE_DOCUMENT) 361 + .append(arena, self.type_(arena, type_, PrintMode::Normal)), 362 + None => self.type_(arena, type_, PrintMode::Normal), 337 363 }, 338 364 ); 339 365 340 - let arguments = Self::wrap_arguments(arguments); 366 + let arguments = Self::wrap_arguments(arena, arguments); 341 367 342 - docvec![self.title(&constructor.name), arguments].group() 368 + docvec![arena, self.title(arena, &constructor.name), arguments].group(arena) 343 369 } 344 370 345 - fn type_alias<'a>( 371 + fn type_alias( 346 372 &mut self, 373 + arena: &'doc DocumentArena<'a, 'doc>, 347 374 name: &'a str, 348 375 type_: &Type, 349 376 parameters: &[(SrcSpan, EcoString)], 350 - ) -> Document<'a> { 377 + ) -> Document<'a, 'doc> { 351 378 let parameters = if parameters.is_empty() { 352 - nil() 379 + EMPTY_DOCUMENT 353 380 } else { 354 381 let arguments = parameters 355 382 .iter() 356 - .map(|(_, parameter)| self.variable(parameter)); 357 - Self::wrap_arguments(arguments) 383 + .map(|(_, parameter)| self.variable(arena, parameter)); 384 + Self::wrap_arguments(arena, arguments) 358 385 }; 359 386 360 387 docvec![ 361 - self.keyword("pub type "), 362 - self.title(name), 388 + arena, 389 + self.keyword(arena, PUB_TYPE_SPACE_DOCUMENT), 390 + self.title(arena, name), 363 391 parameters, 364 - " =", 365 - line() 366 - .append(self.type_(type_, PrintMode::ExpandAliases)) 367 - .nest(INDENT) 392 + SPACE_EQUAL_DOCUMENT, 393 + LINE_DOCUMENT 394 + .append(arena, self.type_(arena, type_, PrintMode::ExpandAliases)) 395 + .nest(arena, INDENT) 368 396 ] 369 397 } 370 398 371 - fn constant<'a>(&mut self, name: &'a str, type_: &Type) -> Document<'a> { 399 + fn constant( 400 + &mut self, 401 + arena: &'doc DocumentArena<'a, 'doc>, 402 + name: &'a str, 403 + type_: &Type, 404 + ) -> Document<'a, 'doc> { 372 405 self.register_local_type_variable_names(type_); 373 406 374 407 docvec![ 375 - self.keyword("pub const "), 376 - self.title(name), 377 - ": ", 378 - self.type_(type_, PrintMode::Normal) 408 + arena, 409 + self.keyword(arena, PUB_CONST_SPACE_DOCUMENT), 410 + self.title(arena, name), 411 + COLON_SPACE_DOCUMENT, 412 + self.type_(arena, type_, PrintMode::Normal) 379 413 ] 380 414 } 381 415 382 - fn function_signature<'a>( 416 + fn function_signature( 383 417 &mut self, 418 + arena: &'doc DocumentArena<'a, 'doc>, 384 419 name: &'a str, 385 420 arguments: &'a [TypedArg], 386 421 return_type: &Type, 387 - ) -> Document<'a> { 422 + ) -> Document<'a, 'doc> { 388 423 for argument in arguments { 389 424 self.register_local_type_variable_names(&argument.type_); 390 425 } 391 426 self.register_local_type_variable_names(return_type); 392 427 393 428 let arguments = if arguments.is_empty() { 394 - "()".to_doc() 429 + OPEN_CLOSE_PAREN_DOCUMENT 395 430 } else { 396 - Self::wrap_arguments(arguments.iter().map(|argument| { 397 - let name = self.variable(self.argument_name(argument)); 398 - docvec![name, ": ", self.type_(&argument.type_, PrintMode::Normal)].group() 399 - })) 431 + Self::wrap_arguments( 432 + arena, 433 + arguments.iter().map(|argument| { 434 + let name = self.variable(arena, self.argument_name(arena, argument)); 435 + docvec![ 436 + arena, 437 + name, 438 + COLON_SPACE_DOCUMENT, 439 + self.type_(arena, &argument.type_, PrintMode::Normal) 440 + ] 441 + .group(arena) 442 + }), 443 + ) 400 444 }; 401 445 402 446 docvec![ 403 - self.keyword("pub fn "), 404 - self.title(name), 447 + arena, 448 + self.keyword(arena, PUB_FN_SPACE_DOCUMENT), 449 + self.title(arena, name), 405 450 arguments, 406 - " -> ", 407 - self.type_(return_type, PrintMode::Normal) 451 + SPACE_RIGHT_ARROW_SPACE_DOCUMENT, 452 + self.type_(arena, return_type, PrintMode::Normal) 408 453 ] 409 - .group() 454 + .group(arena) 410 455 } 411 456 412 - fn argument_name<'a>(&self, arg: &'a TypedArg) -> Document<'a> { 457 + fn argument_name( 458 + &self, 459 + arena: &'doc DocumentArena<'a, 'doc>, 460 + arg: &'a TypedArg, 461 + ) -> Document<'a, 'doc> { 413 462 match &arg.names { 414 - ArgNames::Named { name, .. } => name.to_doc(), 415 - ArgNames::NamedLabelled { label, name, .. } => docvec![label, " ", name], 463 + ArgNames::Named { name, .. } => name.to_doc(arena), 464 + ArgNames::NamedLabelled { label, name, .. } => { 465 + docvec![arena, label, SPACE_DOCUMENT, name] 466 + } 416 467 // We remove the underscore from discarded function arguments since we don't want to 417 468 // expose this kind of detail: https://github.com/gleam-lang/gleam/issues/2561 418 469 ArgNames::Discard { name, .. } => match name.strip_prefix('_').unwrap_or(name) { 419 - "" => "arg".to_doc(), 420 - name => name.to_doc(), 470 + "" => LOWERCASE_ARG_DOCUMENT, 471 + name => name.to_doc(arena), 421 472 }, 422 473 ArgNames::LabelledDiscard { label, name, .. } => { 423 - docvec![label, " ", name.strip_prefix('_').unwrap_or(name).to_doc()] 474 + docvec![ 475 + arena, 476 + label, 477 + SPACE_DOCUMENT, 478 + name.strip_prefix('_').unwrap_or(name).to_doc(arena) 479 + ] 424 480 } 425 481 } 426 482 } 427 483 428 - fn wrap_arguments<'a>(arguments: impl IntoIterator<Item = Document<'a>>) -> Document<'a> { 429 - break_("(", "(") 430 - .append(join(arguments, break_(",", ", "))) 431 - .nest_if_broken(INDENT) 432 - .append(break_(",", "")) 433 - .append(")") 484 + fn wrap_arguments( 485 + arena: &'doc DocumentArena<'a, 'doc>, 486 + arguments: impl IntoIterator<Item = Document<'a, 'doc>>, 487 + ) -> Document<'a, 'doc> { 488 + OPEN_PAREN_BREAK_DOCUMENT 489 + .append(arena, arena.join(arguments, COMMA_BREAK_DOCUMENT)) 490 + .nest_if_broken(arena, INDENT) 491 + .append(arena, TRAILING_COMMA_BREAK_DOCUMENT) 492 + .append(arena, CLOSE_PAREN_DOCUMENT) 434 493 } 435 494 436 - fn type_arguments<'a>(arguments: impl IntoIterator<Item = Document<'a>>) -> Document<'a> { 437 - break_("", "") 438 - .append(join(arguments, break_(",", ", "))) 439 - .nest_if_broken(INDENT) 440 - .append(break_(",", "")) 441 - .group() 442 - .surround("(", ")") 495 + fn type_arguments( 496 + arena: &'doc DocumentArena<'a, 'doc>, 497 + arguments: impl IntoIterator<Item = Document<'a, 'doc>>, 498 + ) -> Document<'a, 'doc> { 499 + EMPTY_BREAK_DOCUMENT 500 + .append(arena, arena.join(arguments, COMMA_BREAK_DOCUMENT)) 501 + .nest_if_broken(arena, INDENT) 502 + .append(arena, TRAILING_COMMA_BREAK_DOCUMENT) 503 + .group(arena) 504 + .surround(arena, OPEN_PAREN_DOCUMENT, CLOSE_PAREN_DOCUMENT) 443 505 } 444 506 445 - fn type_(&mut self, type_: &Type, print_mode: PrintMode) -> Document<'static> { 507 + fn type_( 508 + &mut self, 509 + arena: &'doc DocumentArena<'a, 'doc>, 510 + type_: &Type, 511 + print_mode: PrintMode, 512 + ) -> Document<'a, 'doc> { 446 513 match type_ { 447 514 Type::Named { 448 515 package, ··· 458 525 // is aliasing that internal type, rather than showing it as 459 526 // aliasing itself. 460 527 PrintMode::ExpandAliases if *package == self.package => { 461 - self.named_type_name(publicity, package, module, name) 528 + self.named_type_name(arena, publicity, package, module, name) 462 529 } 463 530 // If we are printing a type alias which aliases an internal 464 531 // type from a different package, we still want to print the ··· 471 538 if let Some((module, alias)) = 472 539 self.names.reexport_alias(module.clone(), name.clone()) 473 540 { 474 - self.named_type_name(&Publicity::Public, package, module, alias) 541 + self.named_type_name(arena, &Publicity::Public, package, module, alias) 475 542 } else { 476 - self.named_type_name(publicity, package, module, name) 543 + self.named_type_name(arena, publicity, package, module, name) 477 544 } 478 545 } 479 546 }; ··· 481 548 if arguments.is_empty() { 482 549 name 483 550 } else { 484 - name.append(Self::type_arguments( 485 - arguments 486 - .iter() 487 - .map(|argument| self.type_(argument, PrintMode::Normal)), 488 - )) 551 + name.append( 552 + arena, 553 + Self::type_arguments( 554 + arena, 555 + arguments 556 + .iter() 557 + .map(|argument| self.type_(arena, argument, PrintMode::Normal)), 558 + ), 559 + ) 489 560 } 490 561 } 491 562 Type::Fn { arguments, return_ } => docvec![ 492 - self.keyword("fn"), 563 + arena, 564 + self.keyword(arena, FN_DOCUMENT), 493 565 Self::type_arguments( 566 + arena, 494 567 arguments 495 568 .iter() 496 - .map(|argument| self.type_(argument, PrintMode::Normal)) 569 + .map(|argument| self.type_(arena, argument, PrintMode::Normal)) 497 570 ), 498 - " -> ", 499 - self.type_(return_, PrintMode::Normal) 571 + SPACE_RIGHT_ARROW_SPACE_DOCUMENT, 572 + self.type_(arena, return_, PrintMode::Normal) 500 573 ], 501 574 Type::Tuple { elements } => docvec![ 502 - "#", 575 + arena, 576 + HASHTAG_DOCUMENT, 503 577 Self::type_arguments( 578 + arena, 504 579 elements 505 580 .iter() 506 - .map(|element| self.type_(element, PrintMode::Normal)) 581 + .map(|element| self.type_(arena, element, PrintMode::Normal)) 507 582 ), 508 583 ], 509 584 Type::Var { type_ } => match type_.as_ref().borrow().deref() { 510 - TypeVar::Link { type_ } => self.type_(type_, PrintMode::Normal), 585 + TypeVar::Link { type_ } => self.type_(arena, type_, PrintMode::Normal), 511 586 512 587 TypeVar::Unbound { id } | TypeVar::Generic { id } => { 513 588 let name = self.type_variable(*id); 514 - self.variable(name) 589 + self.variable(arena, name) 515 590 } 516 591 }, 517 592 } ··· 561 636 562 637 fn named_type_name( 563 638 &self, 639 + arena: &'doc DocumentArena<'a, 'doc>, 564 640 publicity: &Publicity, 565 641 package: &str, 566 642 module: &str, 567 643 name: &EcoString, 568 - ) -> Document<'static> { 644 + ) -> Document<'a, 'doc> { 569 645 // There's no documentation page for the prelude 570 646 if package == PRELUDE_PACKAGE_NAME && module == PRELUDE_MODULE_NAME { 571 - return self.title(name); 647 + return self.title(arena, name); 572 648 } 573 649 574 650 // Internal types don't get linked 575 651 if !publicity.is_public() { 576 - return docvec![self.comment("@internal ".to_doc()), self.title(name)]; 652 + return docvec![ 653 + arena, 654 + self.comment(arena, INTERNAL_ATTRIBUTE_SPACE_DOCUMENT), 655 + self.title(arena, name) 656 + ]; 577 657 } 578 658 579 659 // Linking to a type within the same page 580 660 if package == self.package && module == self.module { 581 - return self.link(eco_format!("#{name}"), self.title(name), None); 661 + return self.link(arena, eco_format!("#{name}"), self.title(arena, name), None); 582 662 } 583 663 584 664 // Linking to a module within the package ··· 622 702 path.push(module_name); 623 703 624 704 let qualified_name = docvec![ 625 - self.variable(EcoString::from(module_name)), 626 - ".", 627 - self.title(name) 705 + arena, 706 + self.variable(arena, EcoString::from(module_name)), 707 + DOT_DOCUMENT, 708 + self.title(arena, name) 628 709 ]; 629 710 630 711 let title = eco_format!("{module}.{{type {name}}}"); 631 712 632 713 return self.link( 714 + arena, 633 715 eco_format!("{path}.html#{name}", path = path.join("/")), 634 716 qualified_name, 635 717 Some(title), ··· 638 720 639 721 let module_name = module.split('/').next_back().unwrap_or(module); 640 722 let qualified_name = docvec![ 641 - self.variable(EcoString::from(module_name)), 642 - ".", 643 - self.title(name) 723 + arena, 724 + self.variable(arena, EcoString::from(module_name)), 725 + DOT_DOCUMENT, 726 + self.title(arena, name) 644 727 ]; 645 728 let title = eco_format!("{module}.{{type {name}}}"); 646 729 ··· 651 734 kind: DependencyKind::Hex, 652 735 version, 653 736 }) => self.link( 737 + arena, 654 738 eco_format!( 655 739 "https://{package}.hexdocs.pm/{version}/{module}.html#{name}", 656 740 package = package.replace('_', "-") ··· 658 742 qualified_name, 659 743 Some(title), 660 744 ), 661 - Some(_) | None => self.span_with_title(qualified_name, title), 745 + Some(_) | None => self.span_with_title(arena, qualified_name, title), 662 746 } 663 747 } 664 748 ··· 705 789 } 706 790 } 707 791 708 - fn keyword<'a>(&self, keyword: impl Documentable<'a>) -> Document<'a> { 709 - self.colour_span(keyword, "keyword") 792 + fn keyword( 793 + &self, 794 + arena: &'doc DocumentArena<'a, 'doc>, 795 + keyword: Document<'static, 'static>, 796 + ) -> Document<'a, 'doc> { 797 + self.colour_span(arena, keyword, ColourClass::Keyword) 710 798 } 711 799 712 - fn comment<'a>(&self, name: impl Documentable<'a>) -> Document<'a> { 713 - self.colour_span(name, "comment") 800 + fn comment( 801 + &self, 802 + arena: &'doc DocumentArena<'a, 'doc>, 803 + name: impl Documentable<'a, 'doc>, 804 + ) -> Document<'a, 'doc> { 805 + self.colour_span(arena, name, ColourClass::Comment) 714 806 } 715 807 716 - fn title<'a>(&self, name: impl Documentable<'a>) -> Document<'a> { 717 - self.colour_span(name, "title") 808 + fn title( 809 + &self, 810 + arena: &'doc DocumentArena<'a, 'doc>, 811 + name: impl Documentable<'a, 'doc>, 812 + ) -> Document<'a, 'doc> { 813 + self.colour_span(arena, name, ColourClass::Title) 718 814 } 719 815 720 - fn variable<'a>(&self, name: impl Documentable<'a>) -> Document<'a> { 721 - self.colour_span(name, "variable") 816 + fn variable( 817 + &self, 818 + arena: &'doc DocumentArena<'a, 'doc>, 819 + name: impl Documentable<'a, 'doc>, 820 + ) -> Document<'a, 'doc> { 821 + self.colour_span(arena, name, ColourClass::Variable) 722 822 } 723 823 724 - fn colour_span<'a>( 824 + fn colour_span( 725 825 &self, 726 - name: impl Documentable<'a>, 727 - colour_class: &'static str, 728 - ) -> Document<'a> { 826 + arena: &'doc DocumentArena<'a, 'doc>, 827 + name: impl Documentable<'a, 'doc>, 828 + colour_class: ColourClass, 829 + ) -> Document<'a, 'doc> { 729 830 if !self.options.print_highlighting { 730 - return name.to_doc(); 831 + return name.to_doc(arena); 731 832 } 732 833 733 - name.to_doc().surround( 734 - zero_width_string(eco_format!(r#"<span class="hljs-{colour_class}">"#)), 735 - zero_width_string("</span>".into()), 736 - ) 834 + let open_tag = match colour_class { 835 + ColourClass::Variable => ZERO_WIDTH_OPEN_VARIABLE_COLOUR_SPAN, 836 + ColourClass::Title => ZERO_WIDTH_OPEN_TITLE_COLOUR_SPAN, 837 + ColourClass::Keyword => ZERO_WIDTH_OPEN_KEYWORD_COLOUR_SPAN, 838 + ColourClass::Comment => ZERO_WIDTH_OPEN_COMMENT_COLOUR_SPAN, 839 + }; 840 + 841 + name.to_doc(arena) 842 + .surround(arena, open_tag, ZERO_WIDTH_CLOSED_SPAN_TAG_DOCUMENT) 737 843 } 738 844 739 - fn link<'a>( 845 + fn link( 740 846 &self, 847 + arena: &'doc DocumentArena<'a, 'doc>, 741 848 href: EcoString, 742 - name: impl Documentable<'a>, 849 + name: impl Documentable<'a, 'doc>, 743 850 title: Option<EcoString>, 744 - ) -> Document<'a> { 851 + ) -> Document<'a, 'doc> { 745 852 if !self.options.print_html { 746 - return name.to_doc(); 853 + return name.to_doc(arena); 747 854 } 748 855 749 856 let opening_tag = if let Some(title) = title { ··· 752 859 eco_format!(r#"<a href="{href}">"#) 753 860 }; 754 861 755 - name.to_doc().surround( 756 - zero_width_string(opening_tag), 757 - zero_width_string("</a>".into()), 862 + name.to_doc(arena).surround( 863 + arena, 864 + arena.zero_width_string(opening_tag), 865 + ZERO_WIDTH_CLOSED_A_TAG_DOCUMENT, 758 866 ) 759 867 } 760 868 761 - fn span_with_title<'a>(&self, name: impl Documentable<'a>, title: EcoString) -> Document<'a> { 869 + fn span_with_title( 870 + &self, 871 + arena: &'doc DocumentArena<'a, 'doc>, 872 + name: impl Documentable<'a, 'doc>, 873 + title: EcoString, 874 + ) -> Document<'a, 'doc> { 762 875 if !self.options.print_html { 763 - return name.to_doc(); 876 + return name.to_doc(arena); 764 877 } 765 878 766 - name.to_doc().surround( 767 - zero_width_string(eco_format!(r#"<span title="{title}">"#)), 768 - zero_width_string("</span>".into()), 879 + name.to_doc(arena).surround( 880 + arena, 881 + arena.zero_width_string(eco_format!(r#"<span title="{title}">"#)), 882 + ZERO_WIDTH_CLOSED_SPAN_TAG_DOCUMENT, 769 883 ) 770 884 } 771 885 } 772 886 887 + enum ColourClass { 888 + Variable, 889 + Title, 890 + Keyword, 891 + Comment, 892 + } 893 + 773 894 const MAX_COLUMNS: isize = 65; 774 895 const INDENT: isize = 2; 775 896 776 - fn print(doc: Document<'_>) -> String { 897 + fn print<'a, 'doc>(doc: Document<'a, 'doc>) -> String { 777 898 doc.to_pretty_string(MAX_COLUMNS) 778 899 }
+44
pretty-arena/src/lib.rs
··· 201 201 | PrintableDocument::Str { .. } 202 202 | PrintableDocument::EcoString { .. } 203 203 | PrintableDocument::ZeroWidthString { .. } 204 + | PrintableDocument::ZeroWidthStr { .. } 204 205 | PrintableDocument::CursorPositionObserver { .. } => self, 205 206 206 207 PrintableDocument::Line(_) ··· 316 317 | PrintableDocument::Str { .. } 317 318 | PrintableDocument::EcoString { .. } 318 319 | PrintableDocument::ZeroWidthString { .. } 320 + | PrintableDocument::ZeroWidthStr { .. } 319 321 | PrintableDocument::CursorPositionObserver { .. } => { 320 322 Document(arena.documents.alloc(PrintableDocument::Join(self, new))) 321 323 } ··· 360 362 // still printed and so are not empty. (Unless their string contents 361 363 // is also empty) 362 364 PrintableDocument::ZeroWidthString { string } => string.is_empty(), 365 + PrintableDocument::ZeroWidthStr { string } => string.is_empty(), 363 366 PrintableDocument::CursorPositionObserver { .. } => true, 364 367 } 365 368 } ··· 477 480 /// in the final output, such as ANSI codes or HTML elements. 478 481 ZeroWidthString { 479 482 string: EcoString, 483 + }, 484 + ZeroWidthStr { 485 + string: &'string str, 480 486 }, 481 487 482 488 /// A node that gets notified of the cursor position as it is being formatted. ··· 551 557 /// wubble" 552 558 Set, 553 559 } 560 + 554 561 macro_rules! const_str { 555 562 ($name:ident, $string:expr, $graphemes:expr) => { 556 563 pub const $name: $crate::Document<'static, 'static> = { ··· 562 569 }; 563 570 } 564 571 572 + macro_rules! const_zero_width_str { 573 + ($name:ident, $string:expr) => { 574 + pub const $name: $crate::Document<'static, 'static> = 575 + { $crate::Document(&$crate::PrintableDocument::ZeroWidthStr { string: $string }) }; 576 + }; 577 + } 578 + 565 579 macro_rules! const_break { 566 580 ($name:ident, $broken:expr, $unbroken:expr) => { 567 581 pub const $name: $crate::Document<'static, 'static> = { ··· 700 714 ); 701 715 const_str!(QUOTE_COMMA_SPACE_QUOTE_DOCUMENT, "\", \"", 4); 702 716 const_str!(INTERNAL_ATTRIBUTE_DOCUMENT, "@internal", 9); 717 + const_str!(INTERNAL_ATTRIBUTE_SPACE_DOCUMENT, "@internal ", 9); 703 718 const_str!(TRUE_LOWERCASE_DOCUMENT, "true", 4); 704 719 const_str!(FALSE_LOWERCASE_DOCUMENT, "false", 5); 705 720 const_str!(OPEN_CLOSE_CURLY_DOCUMENT, "{}", 2); ··· 914 929 const_str!(SPACE_GT_EQ_INT_SPACE_DOCUMENT, " >= ", 4); 915 930 const_str!(CAMEL_CASE_REMAINDER_INT_DOCUMENT, "remainderInt", 12); 916 931 const_str!(PURE_JAVASCRIPT_COMMENT_DOCUMENT, "/* @__PURE__ */ ", 16); 932 + const_str!(LOWERCASE_ARG_DOCUMENT, "arg", 3); 933 + const_str!(PUB_TYPE_SPACE_DOCUMENT, "pub type ", 9); 934 + const_str!(PUB_FN_SPACE_DOCUMENT, "pub fn ", 7); 935 + const_str!(PUB_CONST_SPACE_DOCUMENT, "pub const ", 10); 936 + const_str!(PUB_OPAQUE_TYPE_SPACE_DOCUMENT, "pub opaque type ", 16); 937 + 938 + const_zero_width_str!(ZERO_WIDTH_CLOSED_SPAN_TAG_DOCUMENT, "</span>"); 939 + const_zero_width_str!(ZERO_WIDTH_CLOSED_A_TAG_DOCUMENT, "</a>"); 940 + const_zero_width_str!( 941 + ZERO_WIDTH_OPEN_VARIABLE_COLOUR_SPAN, 942 + r#"<span class="hljs-variable">"# 943 + ); 944 + const_zero_width_str!( 945 + ZERO_WIDTH_OPEN_TITLE_COLOUR_SPAN, 946 + r#"<span class="hljs-title">"# 947 + ); 948 + const_zero_width_str!( 949 + ZERO_WIDTH_OPEN_KEYWORD_COLOUR_SPAN, 950 + r#"<span class="hljs-keyword">"# 951 + ); 952 + const_zero_width_str!( 953 + ZERO_WIDTH_OPEN_COMMENT_COLOUR_SPAN, 954 + r#"<span class="hljs-comment">"# 955 + ); 917 956 918 957 const_break!(EMPTY_BREAK_DOCUMENT, "", ""); 919 958 const_break!(BREAKABLE_SPACE_DOCUMENT, "", " "); ··· 1239 1278 // We write the string, but do not increment the length 1240 1279 writer.write_str(string)?; 1241 1280 } 1281 + PrintableDocument::ZeroWidthStr { string } => { 1282 + // We write the string, but do not increment the length 1283 + writer.write_str(string)?; 1284 + } 1242 1285 1243 1286 // If multiple documents need to be printed, then they are all 1244 1287 // pushed to the front of the queue and will be printed one by one. ··· 1411 1454 1412 1455 // Zero width strings do nothing: they do not contribute to line length 1413 1456 PrintableDocument::ZeroWidthString { .. } 1457 + | PrintableDocument::ZeroWidthStr { .. } 1414 1458 | PrintableDocument::CursorPositionObserver { .. } => {} 1415 1459 1416 1460 // If we get to a break we need to first see if it has to be