refactor: remove needles string allocation in pretty printer

Many times, in order to create a `ven_pretty::Doc` containing a text
node, the pattern `alloc.text(format!(...))` would be used. This code
then creates a fresh string that is then used in the `Doc`. However,
many times only a small string is necessary and so the allocation could
be optimized. The `ven_pretty` crate supports this through a `SmallString`
type. Allocating a fresh string with `format!` also moves control away
from the `DocAllocator` which isn't ideal, since it could also handle
the string allocations. So, instead of creating a fresh string, one can
simply call `alloc.as_string(format_args!(...))` and delegate the
allocation to the `DocAllocator` without any loss in expressivity. So,
in order to encorage this pattern, this commit also introduces the
`text!` macro.

In order to find all instances of the code pattern, the following
tree-sitter query was used:

```scm
(call_expression
    function: (field_expression
        field: (field_identifier) @field.name
               (#eq? @field.name "text"))
    arguments: (arguments
        (macro_invocation
            macro: (identifier) @macro.name
            (#eq? @macro.name "format")))) @reference.call
```
This commit is contained in:
Gabriel Dertoni 2023-05-03 21:09:23 -03:00
parent d84a9fa8ba
commit 7d0027f428
No known key found for this signature in database
GPG key ID: 14AD03CD1BDB5584
8 changed files with 83 additions and 99 deletions

View file

@ -9,7 +9,7 @@ use crate::pattern::{Pattern, RecordDestruct, TupleDestruct};
use roc_module::symbol::{Interns, ModuleId, Symbol};
use ven_pretty::{Arena, DocAllocator, DocBuilder};
use ven_pretty::{text, Arena, DocAllocator, DocBuilder};
pub struct Ctx<'a> {
pub home: ModuleId,
@ -163,10 +163,10 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
use EPrec::*;
match e {
Num(_, n, _, _) | Int(_, _, n, _, _) | Float(_, _, n, _, _) => f.text(&**n),
Str(s) => f.text(format!(r#""{}""#, s)),
SingleQuote(_, _, c, _) => f.text(format!("'{}'", c)),
Str(s) => text!(f, r#""{}""#, s),
SingleQuote(_, _, c, _) => text!(f, "'{}'", c),
IngestedFile(file_path, bytes, _) => {
f.text(format!("<ingested {:?}, {} bytes>", file_path, bytes.len()))
text!(f, "<ingested {:?}, {} bytes>", file_path, bytes.len())
}
List {
elem_var: _,
@ -354,15 +354,15 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
RecordAccess {
loc_expr, field, ..
} => expr(c, AppArg, f, &loc_expr.value)
.append(f.text(format!(".{}", field.as_str())))
.append(text!(f, ".{}", field.as_str()))
.group(),
TupleAccess {
loc_expr, index, ..
} => expr(c, AppArg, f, &loc_expr.value)
.append(f.text(format!(".{index}")))
.append(text!(f, ".{index}"))
.group(),
OpaqueWrapFunction(OpaqueWrapFunctionData { opaque_name, .. }) => {
f.text(format!("@{}", opaque_name.as_str(c.interns)))
text!(f, "@{}", opaque_name.as_str(c.interns))
}
RecordAccessor(_) => todo!(),
RecordUpdate {
@ -436,11 +436,12 @@ fn pp_sym<'a>(c: &Ctx, f: &'a Arena<'a>, sym: Symbol) -> DocBuilder<'a, Arena<'a
if sym.module_id() == c.home {
f.text(sym.as_str(c.interns).to_owned())
} else {
f.text(format!(
text!(
f,
"{}.{}",
sym.module_string(c.interns),
sym.as_str(c.interns),
))
)
}
}
@ -516,8 +517,7 @@ fn pattern<'a>(
),
UnwrappedOpaque {
opaque, argument, ..
} => f
.text(format!("@{} ", opaque.module_string(c.interns)))
} => text!(f, "@{} ", opaque.module_string(c.interns))
.append(pattern(c, Free, f, &argument.1.value))
.group(),
RecordDestructure { destructs, .. } => f
@ -559,8 +559,8 @@ fn pattern<'a>(
NumLiteral(_, n, _, _) | IntLiteral(_, _, n, _, _) | FloatLiteral(_, _, n, _, _) => {
f.text(&**n)
}
StrLiteral(s) => f.text(format!(r#""{}""#, s)),
SingleQuote(_, _, c, _) => f.text(format!("'{}'", c)),
StrLiteral(s) => text!(f, r#""{}""#, s),
SingleQuote(_, _, c, _) => text!(f, "'{}'", c),
Underscore => f.text("_"),
Shadowed(_, _, _) => todo!(),