diff --git a/src/formatting/formatter.rs b/src/formatting/formatter.rs index 2aa1228..93bdc58 100644 --- a/src/formatting/formatter.rs +++ b/src/formatting/formatter.rs @@ -25,7 +25,22 @@ fn to_superscript(num: i8) -> String { .collect() } -pub fn format_with_renderer(technique: &Document, width: u8) -> Vec<(Syntax, Cow<'static, str>)> { +// Pre-allocated common indent strings to avoid repeated allocations +const INDENT_CACHE: &[&str] = &[ + "", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", +]; + +pub fn format_with_renderer<'i>(technique: &'i Document, width: u8) -> Vec<(Syntax, Cow<'i, str>)> { let mut output = Formatter::new(width); if let Some(metadata) = &technique.header { @@ -49,7 +64,7 @@ pub fn format_with_renderer(technique: &Document, width: u8) -> Vec<(Syntax, Cow .last() { if !last_content.ends_with('\n') { - output.add_static(Syntax::Description, "\n"); + output.add_fragment_reference(Syntax::Description, "\n"); } } } @@ -60,71 +75,71 @@ pub fn format_with_renderer(technique: &Document, width: u8) -> Vec<(Syntax, Cow /// Utility functions for rendering individual AST types for Present trait implementations /// These functions create a sub-formatter, render the specific type, and return a styled string -pub fn render_signature(signature: &Signature, renderer: &dyn Render) -> String { +pub fn render_signature<'i>(signature: &'i Signature, renderer: &dyn Render) -> String { let mut sub = Formatter::new(78); sub.append_signature(signature); render_fragments(&sub.fragments, renderer) } -pub fn render_genus(genus: &Genus, renderer: &dyn Render) -> String { +pub fn render_genus<'i>(genus: &'i Genus, renderer: &dyn Render) -> String { let mut sub = Formatter::new(78); sub.append_genus(genus); render_fragments(&sub.fragments, renderer) } -pub fn render_forma(forma: &Forma, renderer: &dyn Render) -> String { +pub fn render_forma<'i>(forma: &'i Forma, renderer: &dyn Render) -> String { let mut sub = Formatter::new(78); sub.append_forma(forma); render_fragments(&sub.fragments, renderer) } -pub fn render_identifier(identifier: &Identifier, renderer: &dyn Render) -> String { +pub fn render_identifier<'i>(identifier: &'i Identifier, renderer: &dyn Render) -> String { renderer.style(Syntax::Declaration, identifier.0) } -pub fn render_response(response: &Response, renderer: &dyn Render) -> String { +pub fn render_response<'i>(response: &'i Response, renderer: &dyn Render) -> String { let mut sub = Formatter::new(78); - sub.add_static(Syntax::Quote, "'"); - sub.add_fragment(Syntax::Response, response.value); - sub.add_static(Syntax::Quote, "'"); + sub.add_fragment_reference(Syntax::Quote, "'"); + sub.add_fragment_reference(Syntax::Response, response.value); + sub.add_fragment_reference(Syntax::Quote, "'"); if let Some(condition) = response.condition { - sub.add_static(Syntax::Neutral, " "); - sub.add_fragment(Syntax::Description, condition); + sub.add_fragment_reference(Syntax::Neutral, " "); + sub.add_fragment_reference(Syntax::Description, condition); } render_fragments(&sub.fragments, renderer) } -pub fn render_numeric(numeric: &Numeric, renderer: &dyn Render) -> String { +pub fn render_numeric<'i>(numeric: &'i Numeric, renderer: &dyn Render) -> String { let mut sub = Formatter::new(78); sub.append_numeric(numeric); render_fragments(&sub.fragments, renderer) } -pub fn render_quantity(quantity: &Quantity, renderer: &dyn Render) -> String { +pub fn render_quantity<'i>(quantity: &'i Quantity, renderer: &dyn Render) -> String { let mut sub = Formatter::new(78); sub.append_quantity(quantity); render_fragments(&sub.fragments, renderer) } -pub fn render_invocation(invocation: &Invocation, renderer: &dyn Render) -> String { +pub fn render_invocation<'i>(invocation: &'i Invocation, renderer: &dyn Render) -> String { let mut sub = Formatter::new(78); sub.append_application(invocation); render_fragments(&sub.fragments, renderer) } -pub fn render_function(function: &Function, renderer: &dyn Render) -> String { +pub fn render_function<'i>(function: &'i Function, renderer: &dyn Render) -> String { let mut sub = Formatter::new(78); sub.append_function(function); render_fragments(&sub.fragments, renderer) } -pub fn render_expression(expression: &Expression, renderer: &dyn Render) -> String { +pub fn render_expression<'i>(expression: &'i Expression, renderer: &dyn Render) -> String { let mut sub = Formatter::new(78); sub.append_expression(expression); render_fragments(&sub.fragments, renderer) } -pub fn render_procedure_declaration(procedure: &Procedure, renderer: &dyn Render) -> String { +pub fn render_procedure_declaration<'i>(procedure: &'i Procedure, renderer: &dyn Render) -> String { let mut sub = Formatter::new(78); sub.append( Syntax::Declaration, @@ -135,17 +150,17 @@ pub fn render_procedure_declaration(procedure: &Procedure, renderer: &dyn Render if let Some(parameters) = &procedure.parameters { sub.append_parameters(parameters); } - sub.add_static(Syntax::Neutral, " "); - sub.add_static(Syntax::Structure, ":"); + sub.add_fragment_reference(Syntax::Neutral, " "); + sub.add_fragment_reference(Syntax::Structure, ":"); if let Some(signature) = &procedure.signature { - sub.add_static(Syntax::Neutral, " "); + sub.add_fragment_reference(Syntax::Neutral, " "); sub.append_signature(signature); } render_fragments(&sub.fragments, renderer) } /// Helper function to convert fragments to a styled string using a renderer -fn render_fragments(fragments: &[(Syntax, Cow<'static, str>)], renderer: &dyn Render) -> String { +fn render_fragments<'i>(fragments: &[(Syntax, Cow<'i, str>)], renderer: &dyn Render) -> String { let mut result = String::new(); for (syntax, content) in fragments { result.push_str(&renderer.style(*syntax, content)); @@ -153,16 +168,16 @@ fn render_fragments(fragments: &[(Syntax, Cow<'static, str>)], renderer: &dyn Re result } -struct Formatter { - fragments: Vec<(Syntax, Cow<'static, str>)>, +struct Formatter<'i> { + fragments: Vec<(Syntax, Cow<'i, str>)>, nesting: u8, width: u8, current: Syntax, buffer: String, } -impl Formatter { - fn new(width: u8) -> Formatter { +impl<'i> Formatter<'i> { + fn new(width: u8) -> Formatter<'i> { Formatter { fragments: Vec::new(), nesting: 0, @@ -172,11 +187,11 @@ impl Formatter { } } - /// Add a fragment based on the supplied static string. This is + /// Add a fragment from a reference to the input string. This is /// extensively used for adding whitespace and structural characters so we /// avoid repeatedly allocating entire Strings just to hold a single known /// character. - pub fn add_static(&mut self, syntax: Syntax, content: &'static str) { + pub fn add_fragment_reference(&mut self, syntax: Syntax, content: &'i str) { self.fragments .push((syntax, Cow::Borrowed(content))); } @@ -184,40 +199,32 @@ impl Formatter { /// Add a fragment, taking ownership of the supplied String. This is /// useful when we have just allocated a temporary string immediately /// prior to passing it in. - pub fn add_string(&mut self, syntax: Syntax, content: String) { + pub fn add_fragment_string(&mut self, syntax: Syntax, content: String) { self.fragments .push((syntax, Cow::Owned(content))); } - /// Add a fragment from a reference to the input string. This is typically - /// used when copying in user-supplied content. - pub fn add_fragment(&mut self, syntax: Syntax, content: &str) { - self.fragments - .push((syntax, Cow::Owned(content.to_string()))); + /// Add a fragment, already wrapped as a Copy-on-write value. + pub fn add_fragment(&mut self, syntax: Syntax, content: Cow<'i, str>) { + match content { + Cow::Borrowed(s) => self.add_fragment_reference(syntax, s), + Cow::Owned(s) => self.add_fragment_string(syntax, s), + } } /// Append content with a specific syntax tagging - fn append(&mut self, syntax: Syntax, content: &str) { + fn append(&mut self, syntax: Syntax, content: &'i str) { // Flush any pending buffer content first to maintain order self.flush_current(); - self.add_fragment(syntax, content); + self.add_fragment_reference(syntax, content); } fn switch_syntax(&mut self, new_syntax: Syntax) { - if !self - .buffer - .is_empty() - { - self.fragments - .push(( - self.current, - Cow::Owned( - self.buffer - .clone(), - ), - )); - self.buffer - .clear(); + if !self.buffer.is_empty() { + self.fragments.push(( + self.current, + Cow::Owned(std::mem::take(&mut self.buffer)), + )); } self.current = new_syntax; } @@ -227,20 +234,11 @@ impl Formatter { } pub fn flush_current(&mut self) { - if !self - .buffer - .is_empty() - { - self.fragments - .push(( - self.current, - Cow::Owned( - self.buffer - .clone(), - ), - )); - self.buffer - .clear(); + if !self.buffer.is_empty() { + self.fragments.push(( + self.current, + Cow::Owned(std::mem::take(&mut self.buffer)), + )); } } @@ -265,8 +263,14 @@ impl Formatter { if self.nesting > 0 { // Flush any existing buffer before adding indentation self.flush_current(); - let spaces = " ".repeat(self.nesting as usize); - self.add_string(Syntax::Indent, spaces); + let indent_level = (self.nesting as usize) / 4; + if indent_level < INDENT_CACHE.len() { + self.add_fragment_reference(Syntax::Indent, INDENT_CACHE[indent_level]); + } else { + // Fallback for deep nesting + let spaces = " ".repeat(self.nesting as usize); + self.add_fragment_string(Syntax::Indent, spaces); + } } } @@ -276,19 +280,19 @@ impl Formatter { } } - fn append_breakable(&mut self, syntax: Syntax, text: &str) { + fn append_breakable(&mut self, syntax: Syntax, text: &'i str) { for (i, word) in text .split_ascii_whitespace() .enumerate() { if i > 0 { - self.add_static(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Neutral, " "); } - self.add_fragment(syntax, word); + self.add_fragment_reference(syntax, word); } } - fn subformatter(&self) -> Formatter { + fn subformatter(&self) -> Formatter<'i> { Formatter { fragments: Vec::new(), nesting: self.nesting, @@ -298,11 +302,11 @@ impl Formatter { } } - fn builder(&mut self) -> Line { + fn builder(&mut self) -> Line<'_, 'i> { Line::new(self) } - fn render_inline_code(&self, expr: &Expression) -> Vec<(Syntax, Cow<'static, str>)> { + fn render_inline_code(&self, expr: &'i Expression) -> Vec<(Syntax, Cow<'i, str>)> { match expr { Expression::Tablet(_) | Expression::Multiline(_, _) => { // These are not inline, caller should handle specially @@ -310,18 +314,18 @@ impl Formatter { } _ => { let mut sub = self.subformatter(); - sub.add_static(Syntax::Structure, "{"); - sub.add_static(Syntax::Neutral, " "); + sub.add_fragment_reference(Syntax::Structure, "{"); + sub.add_fragment_reference(Syntax::Neutral, " "); sub.append_expression(expr); - sub.add_static(Syntax::Neutral, " "); - sub.add_static(Syntax::Structure, "}"); + sub.add_fragment_reference(Syntax::Neutral, " "); + sub.add_fragment_reference(Syntax::Structure, "}"); sub.flush_current(); sub.fragments } } } - fn render_application(&self, invocation: &Invocation) -> Vec<(Syntax, Cow<'static, str>)> { + fn render_application(&self, invocation: &'i Invocation) -> Vec<(Syntax, Cow<'i, str>)> { let mut sub = self.subformatter(); sub.append_application(invocation); sub.flush_current(); @@ -330,19 +334,19 @@ impl Formatter { fn render_binding( &self, - inner_descriptive: &Descriptive, - variables: &Vec, - ) -> Vec<(Syntax, Cow<'static, str>)> { + inner_descriptive: &'i Descriptive, + variables: &'i Vec, + ) -> Vec<(Syntax, Cow<'i, str>)> { let mut sub = self.subformatter(); match inner_descriptive { Descriptive::Text(text) => sub.append_breakable(Syntax::Description, text), Descriptive::CodeInline(expr) => { - sub.add_static(Syntax::Structure, "{"); - sub.add_static(Syntax::Neutral, " "); + sub.add_fragment_reference(Syntax::Structure, "{"); + sub.add_fragment_reference(Syntax::Neutral, " "); sub.append_expression(expr); - sub.add_static(Syntax::Neutral, " "); - sub.add_static(Syntax::Structure, "}"); + sub.add_fragment_reference(Syntax::Neutral, " "); + sub.add_fragment_reference(Syntax::Structure, "}"); } Descriptive::Application(invocation) => { sub.append_application(invocation); @@ -352,9 +356,9 @@ impl Formatter { } } - sub.add_static(Syntax::Neutral, " "); - sub.add_static(Syntax::Structure, "~"); - sub.add_static(Syntax::Neutral, " "); + sub.add_fragment_reference(Syntax::Neutral, " "); + sub.add_fragment_reference(Syntax::Structure, "~"); + sub.add_fragment_reference(Syntax::Neutral, " "); sub.append_variables(variables); sub.flush_current(); sub.fragments @@ -364,7 +368,7 @@ impl Formatter { if c == '\n' { // Flush any existing buffer before adding newline self.flush_current(); - self.add_static(Syntax::Newline, "\n"); + self.add_fragment_reference(Syntax::Newline, "\n"); } else { self.buffer .push(c); @@ -379,7 +383,7 @@ impl Formatter { .is_empty() } - fn format_header(&mut self, metadata: &Metadata) { + fn format_header(&mut self, metadata: &'i Metadata) { self.switch_syntax(Syntax::Header); self.append_str("% technique v1\n"); @@ -403,7 +407,7 @@ impl Formatter { self.reset_syntax(); } - fn format_technique(&mut self, technique: &Technique) { + fn format_technique(&mut self, technique: &'i Technique) { match technique { Technique::Steps(steps) => { self.append_steps(steps); @@ -422,7 +426,7 @@ impl Formatter { } } - fn format_procedure(&mut self, procedure: &Procedure) { + fn format_procedure(&mut self, procedure: &'i Procedure) { // if a header or another procedure has already been added, // separate the upcoming one with a blank line. if !self.is_empty() { @@ -432,7 +436,7 @@ impl Formatter { // declaration let name = &procedure.name; - self.add_fragment(Syntax::Declaration, name.0); + self.add_fragment_reference(Syntax::Declaration, name.0); if let Some(parameters) = &procedure.parameters { // note that append_arguments() is for general expression @@ -443,11 +447,11 @@ impl Formatter { self.append_parameters(parameters); } - self.add_static(Syntax::Neutral, " "); - self.add_static(Syntax::Structure, ":"); + self.add_fragment_reference(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Structure, ":"); if let Some(signature) = &procedure.signature { - self.add_static(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Neutral, " "); self.append_signature(signature); } @@ -460,64 +464,64 @@ impl Formatter { } } - fn append_element(&mut self, element: &Element) { + fn append_element(&mut self, element: &'i Element) { match element { Element::Title(title) => { - self.add_static(Syntax::Newline, "\n"); - self.add_static(Syntax::Header, "# "); - self.add_fragment(Syntax::Title, title); - self.add_static(Syntax::Newline, "\n"); + self.add_fragment_reference(Syntax::Newline, "\n"); + self.add_fragment_reference(Syntax::Header, "# "); + self.add_fragment_reference(Syntax::Title, title); + self.add_fragment_reference(Syntax::Newline, "\n"); } Element::Description(paragraphs) => { - self.add_static(Syntax::Newline, "\n"); + self.add_fragment_reference(Syntax::Newline, "\n"); self.append_paragraphs(paragraphs); } Element::Steps(steps) => { - self.add_static(Syntax::Newline, "\n"); + self.add_fragment_reference(Syntax::Newline, "\n"); self.append_steps(steps); } Element::CodeBlock(expression) => { - self.add_static(Syntax::Structure, "{"); - self.add_static(Syntax::Newline, "\n"); + self.add_fragment_reference(Syntax::Structure, "{"); + self.add_fragment_reference(Syntax::Newline, "\n"); self.increase(4); self.indent(); self.append_expression(expression); - self.add_static(Syntax::Newline, "\n"); + self.add_fragment_reference(Syntax::Newline, "\n"); self.decrease(4); - self.add_static(Syntax::Structure, "}"); + self.add_fragment_reference(Syntax::Structure, "}"); } } } - pub fn append_signature(&mut self, signature: &Signature) { + pub fn append_signature(&mut self, signature: &'i Signature) { self.append_genus(&signature.domain); - self.add_static(Syntax::Neutral, " "); - self.add_static(Syntax::Structure, "->"); - self.add_static(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Structure, "->"); + self.add_fragment_reference(Syntax::Neutral, " "); self.append_genus(&signature.range); } - pub fn append_genus(&mut self, genus: &Genus) { + pub fn append_genus(&mut self, genus: &'i Genus) { match genus { Genus::Unit => { - self.add_static(Syntax::Forma, "()"); + self.add_fragment_reference(Syntax::Forma, "()"); } Genus::Single(forma) => self.append_forma(forma), Genus::Tuple(formas) => { - self.add_static(Syntax::Structure, "("); + self.add_fragment_reference(Syntax::Structure, "("); for (i, forma) in formas .iter() .enumerate() { if i > 0 { - self.add_static(Syntax::Structure, ","); - self.add_static(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Structure, ","); + self.add_fragment_reference(Syntax::Neutral, " "); } self.append_forma(forma); } - self.add_static(Syntax::Structure, ")"); + self.add_fragment_reference(Syntax::Structure, ")"); } Genus::Naked(formas) => { for (i, forma) in formas @@ -525,41 +529,41 @@ impl Formatter { .enumerate() { if i > 0 { - self.add_static(Syntax::Structure, ","); - self.add_static(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Structure, ","); + self.add_fragment_reference(Syntax::Neutral, " "); } self.append_forma(forma); } } Genus::List(forma) => { - self.add_static(Syntax::Structure, "["); + self.add_fragment_reference(Syntax::Structure, "["); self.append_forma(forma); - self.add_static(Syntax::Structure, "]"); + self.add_fragment_reference(Syntax::Structure, "]"); } } } // Output names surrounded by parenthesis - pub fn append_parameters(&mut self, variables: &Vec) { - self.add_static(Syntax::Structure, "("); + pub fn append_parameters(&mut self, variables: &'i Vec) { + self.add_fragment_reference(Syntax::Structure, "("); for (i, variable) in variables .iter() .enumerate() { if i > 0 { - self.add_static(Syntax::Structure, ","); - self.add_static(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Structure, ","); + self.add_fragment_reference(Syntax::Neutral, " "); } - self.add_fragment(Syntax::Variable, variable.0); + self.add_fragment_reference(Syntax::Variable, variable.0); } - self.add_static(Syntax::Structure, ")"); + self.add_fragment_reference(Syntax::Structure, ")"); } - pub fn append_forma(&mut self, forma: &Forma) { - self.add_fragment(Syntax::Forma, forma.0) + pub fn append_forma(&mut self, forma: &'i Forma) { + self.add_fragment_reference(Syntax::Forma, forma.0) } - fn append_paragraphs(&mut self, paragraphs: &Vec) { + fn append_paragraphs(&mut self, paragraphs: &'i Vec) { for (i, paragraph) in paragraphs .iter() .enumerate() @@ -573,7 +577,7 @@ impl Formatter { } } - fn append_descriptives(&mut self, descriptives: &Vec) { + fn append_descriptives(&mut self, descriptives: &'i Vec) { let syntax = self.current; let mut line = self.builder(); @@ -585,7 +589,7 @@ impl Formatter { Descriptive::CodeInline(expr) => match expr { Expression::Tablet(_) => { line.flush(); - self.add_static(Syntax::Structure, "{"); + self.add_fragment_reference(Syntax::Structure, "{"); self.append_char('\n'); self.increase(4); self.indent(); @@ -597,7 +601,7 @@ impl Formatter { } Expression::Multiline(_, _) => { line.flush(); - self.add_static(Syntax::Structure, "{"); + self.add_fragment_reference(Syntax::Structure, "{"); self.increase(4); self.append_expression(expr); self.decrease(4); @@ -614,11 +618,11 @@ impl Formatter { .any(|p| matches!(p, Expression::Multiline(_, _))) => { line.flush(); - self.add_fragment(Syntax::Neutral, " "); - self.add_static(Syntax::Structure, "{"); - self.add_fragment(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Structure, "{"); + self.add_fragment_reference(Syntax::Neutral, " "); self.append_expression(expr); - self.add_fragment(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Neutral, " "); line = self.builder(); line.add_word(Syntax::Structure, "}"); } @@ -660,13 +664,13 @@ impl Formatter { line.flush(); } - fn append_steps(&mut self, steps: &Vec) { + fn append_steps(&mut self, steps: &'i Vec) { self.increase(4); self.append_scopes(steps); self.decrease(4); } - fn append_step(&mut self, step: &Scope) { + fn append_step(&mut self, step: &'i Scope) { match step { Scope::DependentBlock { ordinal, @@ -674,10 +678,10 @@ impl Formatter { subscopes: scopes, } => { self.indent(); - self.add_fragment(Syntax::StepItem, &format!("{}.", ordinal)); - self.add_static(Syntax::Neutral, " "); + self.add_fragment_string(Syntax::StepItem, format!("{}.", ordinal)); + self.add_fragment_reference(Syntax::Neutral, " "); if ordinal.len() == 1 { - self.add_static(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Neutral, " "); } self.increase(4); @@ -698,8 +702,8 @@ impl Formatter { subscopes, } => { self.indent(); - self.add_fragment(Syntax::StepItem, &bullet.to_string()); - self.add_static(Syntax::Neutral, " "); + self.add_fragment_string(Syntax::StepItem, bullet.to_string()); + self.add_fragment_reference(Syntax::Neutral, " "); self.increase(4); @@ -717,29 +721,29 @@ impl Formatter { } } - fn append_responses(&mut self, responses: &Vec) { + fn append_responses(&mut self, responses: &'i Vec) { for (i, response) in responses .iter() .enumerate() { if i > 0 { - self.add_static(Syntax::Neutral, " "); - self.add_static(Syntax::Structure, "|"); - self.add_static(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Structure, "|"); + self.add_fragment_reference(Syntax::Neutral, " "); } - self.add_static(Syntax::Quote, "'"); - self.add_fragment(Syntax::Response, response.value); - self.add_static(Syntax::Quote, "'"); + self.add_fragment_reference(Syntax::Quote, "'"); + self.add_fragment_reference(Syntax::Response, response.value); + self.add_fragment_reference(Syntax::Quote, "'"); if let Some(text) = response.condition { - self.add_static(Syntax::Neutral, " "); - self.add_fragment(Syntax::Description, text); + self.add_fragment_reference(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Description, text); } } self.append_char('\n'); } - fn append_scope(&mut self, scope: &Scope) { + fn append_scope(&mut self, scope: &'i Scope) { match scope { Scope::DependentBlock { .. } | Scope::ParallelBlock { .. } => { self.append_step(scope); @@ -749,7 +753,7 @@ impl Formatter { subscopes, } => { self.append_attributes(attributes); - self.add_static(Syntax::Newline, "\n"); + self.add_fragment_reference(Syntax::Newline, "\n"); if subscopes.len() == 0 { return; @@ -776,27 +780,27 @@ impl Formatter { match expression { Expression::Tablet(_) => { self.indent(); - self.add_static(Syntax::Structure, "{"); - self.add_static(Syntax::Newline, "\n"); + self.add_fragment_reference(Syntax::Structure, "{"); + self.add_fragment_reference(Syntax::Newline, "\n"); self.increase(4); self.indent(); self.append_expression(expression); - self.add_static(Syntax::Newline, "\n"); + self.add_fragment_reference(Syntax::Newline, "\n"); self.decrease(4); self.indent(); - self.add_static(Syntax::Structure, "}"); + self.add_fragment_reference(Syntax::Structure, "}"); } _ => { self.indent(); - self.add_static(Syntax::Structure, "{"); - self.add_static(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Structure, "{"); + self.add_fragment_reference(Syntax::Neutral, " "); self.append_expression(expression); - self.add_static(Syntax::Neutral, " "); - self.add_static(Syntax::Structure, "}"); + self.add_fragment_reference(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Structure, "}"); } } - self.add_static(Syntax::Newline, "\n"); + self.add_fragment_reference(Syntax::Newline, "\n"); // Format subscopes below this code block, if there are any. self.increase(4); @@ -814,15 +818,15 @@ impl Formatter { title, body, } => { - self.add_fragment(Syntax::StepItem, numeral); - self.add_static(Syntax::Structure, "."); + self.add_fragment_reference(Syntax::StepItem, numeral); + self.add_fragment_reference(Syntax::Structure, "."); if let Some(paragraph) = title { - self.add_static(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Neutral, " "); self.switch_syntax(Syntax::Section); self.append_descriptives(¶graph.0); self.reset_syntax(); } - self.add_static(Syntax::Newline, "\n"); + self.add_fragment_reference(Syntax::Newline, "\n"); // Sections headings always reset back to left margin let saved = self.nesting; @@ -833,7 +837,7 @@ impl Formatter { } } - fn append_scopes(&mut self, scopes: &Vec) { + fn append_scopes(&mut self, scopes: &'i Vec) { for (i, scope) in scopes .iter() .enumerate() @@ -847,59 +851,59 @@ impl Formatter { } } - fn append_attributes(&mut self, attributes: &Vec) { + fn append_attributes(&mut self, attributes: &'i Vec) { self.indent(); for (i, attribute) in attributes .iter() .enumerate() { if i > 0 { - self.add_static(Syntax::Neutral, " + "); + self.add_fragment_reference(Syntax::Neutral, " + "); } match attribute { Attribute::Role(name) => { - self.add_static(Syntax::Attribute, "@"); - self.add_fragment(Syntax::Attribute, name.0); + self.add_fragment_reference(Syntax::Attribute, "@"); + self.add_fragment_reference(Syntax::Attribute, name.0); } Attribute::Place(name) => { - self.add_static(Syntax::Attribute, "#"); - self.add_fragment(Syntax::Attribute, name.0); + self.add_fragment_reference(Syntax::Attribute, "#"); + self.add_fragment_reference(Syntax::Attribute, name.0); } } } } - pub fn append_expression(&mut self, expression: &Expression) { + pub fn append_expression(&mut self, expression: &'i Expression) { match expression { Expression::Variable(identifier) => { - self.add_fragment(Syntax::Variable, identifier.0); + self.add_fragment_reference(Syntax::Variable, identifier.0); } Expression::String(pieces) => { - self.add_static(Syntax::Quote, "\""); + self.add_fragment_reference(Syntax::Quote, "\""); for piece in pieces { match piece { Piece::Text(text) => { // Preserve user string content exactly as written - self.add_fragment(Syntax::String, text); + self.add_fragment_reference(Syntax::String, text); } Piece::Interpolation(expr) => { let fragments = self.render_inline_code(expr); for (syntax, content) in fragments { - self.add_fragment(syntax, &content); + self.add_fragment(syntax, content); } } } } - self.add_static(Syntax::Quote, "\""); + self.add_fragment_reference(Syntax::Quote, "\""); } Expression::Number(numeric) => self.append_numeric(numeric), Expression::Multiline(lang, lines) => { self.append_char('\n'); self.indent(); - self.add_static(Syntax::Quote, "```"); + self.add_fragment_reference(Syntax::Quote, "```"); if let Some(which) = lang { - self.add_fragment(Syntax::Language, which); + self.add_fragment_reference(Syntax::Language, which); } self.append_char('\n'); @@ -912,39 +916,39 @@ impl Formatter { .enumerate() { if i > 0 { - self.add_static(Syntax::Multiline, " "); + self.add_fragment_reference(Syntax::Multiline, " "); } - self.add_fragment(Syntax::Multiline, word); + self.add_fragment_reference(Syntax::Multiline, word); } self.append_char('\n'); } self.decrease(4); self.indent(); - self.add_static(Syntax::Quote, "```"); + self.add_fragment_reference(Syntax::Quote, "```"); self.append_char('\n'); } Expression::Repeat(expression) => { - self.add_static(Syntax::Keyword, "repeat"); - self.add_static(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Keyword, "repeat"); + self.add_fragment_reference(Syntax::Neutral, " "); self.append_expression(expression); } Expression::Foreach(variables, expression) => { - self.add_static(Syntax::Keyword, "foreach"); - self.add_static(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Keyword, "foreach"); + self.add_fragment_reference(Syntax::Neutral, " "); self.append_variables(variables); - self.add_static(Syntax::Neutral, " "); - self.add_static(Syntax::Keyword, "in"); - self.add_static(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Keyword, "in"); + self.add_fragment_reference(Syntax::Neutral, " "); self.append_expression(expression); } Expression::Application(invocation) => self.append_application(invocation), Expression::Execution(function) => self.append_function(function), Expression::Binding(expression, variables) => { self.append_expression(expression); - self.add_static(Syntax::Neutral, " "); - self.add_static(Syntax::Structure, "~"); - self.add_static(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Structure, "~"); + self.add_fragment_reference(Syntax::Neutral, " "); self.append_variables(variables); } Expression::Tablet(pairs) => self.append_tablet(pairs), @@ -953,65 +957,67 @@ impl Formatter { // When doing binding we omit the parenthesis in the most common case of // there only being one name being bound to. - fn append_variables(&mut self, variables: &Vec) { + fn append_variables(&mut self, variables: &'i Vec) { if variables.len() > 1 { - self.add_static(Syntax::Structure, "("); + self.add_fragment_reference(Syntax::Structure, "("); } for (i, variable) in variables .iter() .enumerate() { if i > 0 { - self.add_static(Syntax::Structure, ","); - self.add_static(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Structure, ","); + self.add_fragment_reference(Syntax::Neutral, " "); } - self.add_fragment(Syntax::Variable, variable.0); + self.add_fragment_reference(Syntax::Variable, variable.0); } if variables.len() > 1 { - self.add_static(Syntax::Structure, ")"); + self.add_fragment_reference(Syntax::Structure, ")"); } } - pub fn append_numeric(&mut self, numeric: &Numeric) { + pub fn append_numeric(&mut self, numeric: &'i Numeric) { match numeric { - Numeric::Integral(num) => self.add_fragment(Syntax::Numeric, &num.to_string()), + Numeric::Integral(num) => self.add_fragment_string(Syntax::Numeric, num.to_string()), Numeric::Scientific(quantity) => self.append_quantity(quantity), } } - pub fn append_quantity(&mut self, quantity: &Quantity) { + pub fn append_quantity(&mut self, quantity: &'i Quantity) { // Format the mantissa - self.add_fragment(Syntax::Numeric, &format!("{}", quantity.mantissa)); + self.add_fragment_string(Syntax::Numeric, format!("{}", quantity.mantissa)); // Add uncertainty if present if let Some(uncertainty) = &quantity.uncertainty { - self.add_static(Syntax::Neutral, " "); - self.add_static(Syntax::Numeric, "±"); - self.add_static(Syntax::Neutral, " "); - self.add_fragment(Syntax::Numeric, &format!("{}", uncertainty)); + self.add_fragment_reference(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Numeric, "±"); + self.add_fragment_reference(Syntax::Neutral, " "); + self.add_fragment_string(Syntax::Numeric, format!("{}", uncertainty)); } // Add magnitude if present if let Some(magnitude) = &quantity.magnitude { - self.add_static(Syntax::Neutral, " "); - self.add_static(Syntax::Numeric, "×"); - self.add_static(Syntax::Neutral, " "); - self.add_static(Syntax::Numeric, "10"); - self.add_fragment(Syntax::Numeric, &to_superscript(*magnitude)); + self.add_fragment_reference(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Numeric, "×"); + self.add_fragment_reference(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Numeric, "10"); + self.add_fragment_string(Syntax::Numeric, to_superscript(*magnitude)); } // Add unit symbol - self.add_static(Syntax::Neutral, " "); - self.add_fragment(Syntax::Numeric, quantity.symbol); + self.add_fragment_reference(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Numeric, quantity.symbol); } - pub fn append_application(&mut self, invocation: &Invocation) { - self.add_static(Syntax::Quote, "<"); + pub fn append_application(&mut self, invocation: &'i Invocation) { + self.add_fragment_reference(Syntax::Quote, "<"); match &invocation.target { - Target::Local(identifier) => self.add_fragment(Syntax::Invocation, identifier.0), - Target::Remote(external) => self.add_fragment(Syntax::Invocation, external.0), + Target::Local(identifier) => { + self.add_fragment_reference(Syntax::Invocation, identifier.0) + } + Target::Remote(external) => self.add_fragment_reference(Syntax::Invocation, external.0), } - self.add_static(Syntax::Quote, ">"); + self.add_fragment_reference(Syntax::Quote, ">"); if let Some(parameters) = &invocation.parameters { self.append_arguments(parameters); } @@ -1020,31 +1026,31 @@ impl Formatter { // This is the one that is for the generalized case where the arguments to // a function can be Expressions themselves (though usually are just // variable names) - fn append_arguments(&mut self, parameters: &Vec) { - self.add_static(Syntax::Structure, "("); + fn append_arguments(&mut self, parameters: &'i Vec) { + self.add_fragment_reference(Syntax::Structure, "("); for (i, parameter) in parameters .iter() .enumerate() { if i > 0 { - self.add_static(Syntax::Structure, ","); - self.add_static(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Structure, ","); + self.add_fragment_reference(Syntax::Neutral, " "); } self.append_expression(parameter); } - self.add_static(Syntax::Structure, ")"); + self.add_fragment_reference(Syntax::Structure, ")"); } - pub fn append_function(&mut self, function: &Function) { - self.add_fragment( + pub fn append_function(&mut self, function: &'i Function) { + self.add_fragment_reference( Syntax::Function, - &function + function .target .0, ); - self.add_static(Syntax::Structure, "("); + self.add_fragment_reference(Syntax::Structure, "("); let mut has_multiline = false; for parameter in &function.parameters { @@ -1060,8 +1066,8 @@ impl Formatter { .enumerate() { if i > 0 { - self.add_static(Syntax::Structure, ","); - self.add_static(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Structure, ","); + self.add_fragment_reference(Syntax::Neutral, " "); } self.append_expression(parameter); } @@ -1069,33 +1075,33 @@ impl Formatter { if has_multiline { self.indent(); } - self.add_static(Syntax::Structure, ")"); + self.add_fragment_reference(Syntax::Structure, ")"); } - fn append_tablet(&mut self, pairs: &Vec) { - self.add_static(Syntax::Structure, "["); + fn append_tablet(&mut self, pairs: &'i Vec) { + self.add_fragment_reference(Syntax::Structure, "["); self.append_char('\n'); self.increase(4); for pair in pairs { self.indent(); - self.add_static(Syntax::Quote, "\""); - self.add_fragment(Syntax::Label, pair.label); - self.add_static(Syntax::Quote, "\""); - self.add_static(Syntax::Neutral, " "); - self.add_static(Syntax::Structure, "="); - self.add_static(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Quote, "\""); + self.add_fragment_reference(Syntax::Label, pair.label); + self.add_fragment_reference(Syntax::Quote, "\""); + self.add_fragment_reference(Syntax::Neutral, " "); + self.add_fragment_reference(Syntax::Structure, "="); + self.add_fragment_reference(Syntax::Neutral, " "); self.append_expression(&pair.value); self.append_char('\n'); } self.decrease(4); self.indent(); - self.add_static(Syntax::Structure, "]"); + self.add_fragment_reference(Syntax::Structure, "]"); } } -impl ToString for Formatter { +impl<'i> ToString for Formatter<'i> { fn to_string(&self) -> String { let mut result = String::new(); @@ -1118,14 +1124,14 @@ impl ToString for Formatter { } } -struct Line<'a> { - output: &'a mut Formatter, // reference to parent - current: Vec<(Syntax, Cow<'static, str>)>, +struct Line<'a, 'i> { + output: &'a mut Formatter<'i>, // reference to parent + current: Vec<(Syntax, Cow<'i, str>)>, position: u8, } -impl<'a> Line<'a> { - fn new(output: &'a mut Formatter) -> Self { +impl<'a, 'i> Line<'a, 'i> { + fn new(output: &'a mut Formatter<'i>) -> Self { Line { current: Vec::new(), position: output.nesting, @@ -1133,44 +1139,49 @@ impl<'a> Line<'a> { } } - fn add_atomic(&mut self, syntax: Syntax, content: &str) { + fn add_atomic(&mut self, syntax: Syntax, content: &'i str) { + self.add_atomic_cow(syntax, Cow::Borrowed(content)); + } + + fn add_atomic_cow(&mut self, syntax: Syntax, content: Cow<'i, str>) { // Treat as atomic units - don't split them further + let len = content.len() as u8; if !self .current .is_empty() { - if self.position + content.len() as u8 + if self.position + len > self .output .width { self.wrap_line(); self.current - .push((syntax, Cow::Owned(content.to_string()))); + .push((syntax, content)); self.position = self .output .nesting - + content.len() as u8; + + len; } else { self.current - .push((syntax, Cow::Owned(content.to_string()))); - self.position += content.len() as u8; + .push((syntax, content)); + self.position += len; } } else { self.current - .push((syntax, Cow::Owned(content.to_string()))); - self.position += content.len() as u8; + .push((syntax, content)); + self.position += len; } } - fn add_breakable(&mut self, syntax: Syntax, content: &str) { + fn add_breakable(&mut self, syntax: Syntax, content: &'i str) { // Split content by whitespace for proper line wrapping for word in content.split_ascii_whitespace() { self.add_word(syntax, word); } } - fn add_word(&mut self, syntax: Syntax, word: &str) { + fn add_word(&mut self, syntax: Syntax, word: &'i str) { if !self .current .is_empty() @@ -1182,7 +1193,7 @@ impl<'a> Line<'a> { { self.wrap_line(); self.current - .push((syntax, Cow::Owned(word.to_string()))); + .push((syntax, Cow::Borrowed(word))); self.position = self .output .nesting @@ -1191,70 +1202,68 @@ impl<'a> Line<'a> { self.current .push((Syntax::Description, Cow::Borrowed(" "))); self.current - .push((syntax, Cow::Owned(word.to_string()))); + .push((syntax, Cow::Borrowed(word))); self.position += 1 + word.len() as u8; } } else { self.current - .push((syntax, Cow::Owned(word.to_string()))); + .push((syntax, Cow::Borrowed(word))); self.position += word.len() as u8; } } - fn add_inline_code(&mut self, expr: &Expression) { + fn add_inline_code(&mut self, expr: &'i Expression) { let fragments = self .output .render_inline_code(expr); self.add_fragments(fragments); } - fn add_application(&mut self, invocation: &Invocation) { + fn add_application(&mut self, invocation: &'i Invocation) { let fragments = self .output .render_application(invocation); self.add_fragments(fragments); } - fn add_binding(&mut self, inner_descriptive: &Descriptive, variables: &Vec) { + fn add_binding(&mut self, inner_descriptive: &'i Descriptive, variables: &'i Vec) { let fragments = self .output .render_binding(inner_descriptive, variables); self.add_fragments(fragments); } - fn add_fragments(&mut self, fragments: Vec<(Syntax, Cow<'static, str>)>) { + fn add_fragments(&mut self, fragments: Vec<(Syntax, Cow<'i, str>)>) { // All fragments should be atomic - the formatter is responsible for breaking up content for (syntax, content) in fragments { - self.add_atomic(syntax, &content); + self.add_atomic_cow(syntax, content); } } fn wrap_line(&mut self) { // Emit all current fragments to the output - for (syntax, content) in &self.current { + for (syntax, content) in self.current.drain(..) { self.output - .add_fragment(*syntax, content); + .add_fragment(syntax, content); } self.output .append_char('\n'); self.output .indent(); - self.current - .clear(); self.position = self .output .nesting; } - fn flush(self) { + fn flush(mut self) { if !self .current .is_empty() { // Emit all current fragments to the output - for (syntax, content) in &self.current { + for (syntax, content) in self.current.drain(..) { self.output - .add_fragment(*syntax, content); + .add_fragment(syntax, content); } } } @@ -1284,12 +1293,13 @@ mod check { assert_eq!(output.to_string(), "[Pilot]"); output.reset(); - output.append_genus(&Genus::Tuple(vec![ + let genus = Genus::Tuple(vec![ Forma("Kid"), Forma("Pilot"), Forma("Scoundrel"), Forma("Princess"), - ])); + ]); + output.append_genus(&genus); assert_eq!(output.to_string(), "(Kid, Pilot, Scoundrel, Princess)"); output.reset(); @@ -1313,10 +1323,11 @@ mod check { assert_eq!(output.to_string(), "[Clone] -> Army"); output.reset(); - output.append_signature(&Signature { + let signature = Signature { domain: Genus::Single(Forma("TaxationOfTradeRoutes")), range: Genus::Tuple(vec![Forma("Rebels"), Forma("Empire")]), - }); + }; + output.append_signature(&signature); assert_eq!( output.to_string(), "TaxationOfTradeRoutes -> (Rebels, Empire)" diff --git a/src/formatting/renderer.rs b/src/formatting/renderer.rs index e3dd271..0ec994a 100644 --- a/src/formatting/renderer.rs +++ b/src/formatting/renderer.rs @@ -63,18 +63,18 @@ pub fn render(renderer: &impl Render, technique: &Document, width: u8) -> String } /// Pass 1: Convert AST to semantic fragments. -fn format_to_fragments( - technique: &Document, +fn format_to_fragments<'i>( + technique: &'i Document, width: u8, -) -> Vec<(Syntax, std::borrow::Cow<'static, str>)> { +) -> Vec<(Syntax, std::borrow::Cow<'i, str>)> { // Use the existing formatter.rs logic but collect fragments instead crate::formatting::formatter::format_with_renderer(technique, width) } /// Pass 2: apply markup to fragments via style() and combine. -fn render_to_string( +fn render_to_string<'i>( renderer: &impl Render, - fragments: Vec<(Syntax, std::borrow::Cow<'static, str>)>, + fragments: Vec<(Syntax, std::borrow::Cow<'i, str>)>, ) -> String { let mut output = String::new(); diff --git a/tests/formatting/formatter.rs b/tests/formatting/formatter.rs index 7bfb5eb..c4f26a5 100644 --- a/tests/formatting/formatter.rs +++ b/tests/formatting/formatter.rs @@ -9,7 +9,7 @@ mod verify { tail } - fn combine(fragments: Vec<(Syntax, std::borrow::Cow<'static, str>)>) -> String { + fn combine<'i>(fragments: Vec<(Syntax, std::borrow::Cow<'i, str>)>) -> String { let mut result = String::new(); for fragment in fragments { result.push_str(&fragment.1);