summaryrefslogtreecommitdiff
path: root/vendor/prettyplease/src/mac.rs
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-07-02 18:36:06 -0600
committermo khan <mo@mokhan.ca>2025-07-02 18:36:06 -0600
commit8cdfa445d6629ffef4cb84967ff7017654045bc2 (patch)
tree22f0b0907c024c78d26a731e2e1f5219407d8102 /vendor/prettyplease/src/mac.rs
parent4351c74c7c5f97156bc94d3a8549b9940ac80e3f (diff)
chore: add vendor directory
Diffstat (limited to 'vendor/prettyplease/src/mac.rs')
-rw-r--r--vendor/prettyplease/src/mac.rs706
1 files changed, 706 insertions, 0 deletions
diff --git a/vendor/prettyplease/src/mac.rs b/vendor/prettyplease/src/mac.rs
new file mode 100644
index 00000000..ddb2b5fe
--- /dev/null
+++ b/vendor/prettyplease/src/mac.rs
@@ -0,0 +1,706 @@
+use crate::algorithm::Printer;
+use crate::path::PathKind;
+use crate::token::Token;
+use crate::INDENT;
+use proc_macro2::{Delimiter, Spacing, TokenStream};
+use syn::{Ident, Macro, MacroDelimiter};
+
+impl Printer {
+ pub fn mac(&mut self, mac: &Macro, ident: Option<&Ident>, semicolon: bool) {
+ if mac.path.is_ident("macro_rules") {
+ if let Some(ident) = ident {
+ self.macro_rules(ident, &mac.tokens);
+ return;
+ }
+ }
+ #[cfg(feature = "verbatim")]
+ if ident.is_none() && self.standard_library_macro(mac, semicolon) {
+ return;
+ }
+ self.path(&mac.path, PathKind::Simple);
+ self.word("!");
+ if let Some(ident) = ident {
+ self.nbsp();
+ self.ident(ident);
+ }
+ let (open, close, delimiter_break) = match mac.delimiter {
+ MacroDelimiter::Paren(_) => ("(", ")", Self::zerobreak as fn(&mut Self)),
+ MacroDelimiter::Brace(_) => (" {", "}", Self::hardbreak as fn(&mut Self)),
+ MacroDelimiter::Bracket(_) => ("[", "]", Self::zerobreak as fn(&mut Self)),
+ };
+ self.word(open);
+ if !mac.tokens.is_empty() {
+ self.cbox(INDENT);
+ delimiter_break(self);
+ self.ibox(0);
+ self.macro_rules_tokens(mac.tokens.clone(), false);
+ self.end();
+ delimiter_break(self);
+ self.offset(-INDENT);
+ self.end();
+ }
+ self.word(close);
+ if semicolon {
+ self.word(";");
+ }
+ }
+
+ fn macro_rules(&mut self, name: &Ident, rules: &TokenStream) {
+ enum State {
+ Start,
+ Matcher,
+ Equal,
+ Greater,
+ Expander,
+ }
+
+ use State::*;
+
+ self.word("macro_rules! ");
+ self.ident(name);
+ self.word(" {");
+ self.cbox(INDENT);
+ self.hardbreak_if_nonempty();
+ let mut state = State::Start;
+ for tt in rules.clone() {
+ let token = Token::from(tt);
+ match (state, token) {
+ (Start, Token::Group(delimiter, stream)) => {
+ self.delimiter_open(delimiter);
+ if !stream.is_empty() {
+ self.cbox(INDENT);
+ self.zerobreak();
+ self.ibox(0);
+ self.macro_rules_tokens(stream, true);
+ self.end();
+ self.zerobreak();
+ self.offset(-INDENT);
+ self.end();
+ }
+ self.delimiter_close(delimiter);
+ state = Matcher;
+ }
+ (Matcher, Token::Punct('=', Spacing::Joint)) => {
+ self.word(" =");
+ state = Equal;
+ }
+ (Equal, Token::Punct('>', Spacing::Alone)) => {
+ self.word(">");
+ state = Greater;
+ }
+ (Greater, Token::Group(_delimiter, stream)) => {
+ self.word(" {");
+ self.neverbreak();
+ if !stream.is_empty() {
+ self.cbox(INDENT);
+ self.hardbreak();
+ self.ibox(0);
+ self.macro_rules_tokens(stream, false);
+ self.end();
+ self.hardbreak();
+ self.offset(-INDENT);
+ self.end();
+ }
+ self.word("}");
+ state = Expander;
+ }
+ (Expander, Token::Punct(';', Spacing::Alone)) => {
+ self.word(";");
+ self.hardbreak();
+ state = Start;
+ }
+ _ => unimplemented!("bad macro_rules syntax"),
+ }
+ }
+ match state {
+ Start => {}
+ Expander => {
+ self.word(";");
+ self.hardbreak();
+ }
+ _ => self.hardbreak(),
+ }
+ self.offset(-INDENT);
+ self.end();
+ self.word("}");
+ }
+
+ pub fn macro_rules_tokens(&mut self, stream: TokenStream, matcher: bool) {
+ #[derive(PartialEq)]
+ enum State {
+ Start,
+ Dollar,
+ DollarCrate,
+ DollarIdent,
+ DollarIdentColon,
+ DollarParen,
+ DollarParenSep,
+ Pound,
+ PoundBang,
+ Dot,
+ Colon,
+ Colon2,
+ Ident,
+ IdentBang,
+ Delim,
+ Other,
+ }
+
+ use State::*;
+
+ let mut state = Start;
+ let mut previous_is_joint = true;
+ for tt in stream {
+ let token = Token::from(tt);
+ let (needs_space, next_state) = match (&state, &token) {
+ (Dollar, Token::Ident(_)) if matcher => (false, DollarIdent),
+ (Dollar, Token::Ident(ident)) if ident == "crate" => (false, DollarCrate),
+ (Dollar, Token::Ident(_)) => (false, Other),
+ (DollarIdent, Token::Punct(':', Spacing::Alone)) => (false, DollarIdentColon),
+ (DollarIdentColon, Token::Ident(_)) => (false, Other),
+ (DollarParen, Token::Punct('+' | '*' | '?', Spacing::Alone)) => (false, Other),
+ (DollarParen, Token::Ident(_) | Token::Literal(_)) => (false, DollarParenSep),
+ (DollarParen, Token::Punct(_, Spacing::Joint)) => (false, DollarParen),
+ (DollarParen, Token::Punct(_, Spacing::Alone)) => (false, DollarParenSep),
+ (DollarParenSep, Token::Punct('+' | '*', _)) => (false, Other),
+ (Pound, Token::Punct('!', _)) => (false, PoundBang),
+ (Dollar, Token::Group(Delimiter::Parenthesis, _)) => (false, DollarParen),
+ (Pound | PoundBang, Token::Group(Delimiter::Bracket, _)) => (false, Other),
+ (Ident, Token::Group(Delimiter::Parenthesis | Delimiter::Bracket, _)) => {
+ (false, Delim)
+ }
+ (Ident, Token::Punct('!', Spacing::Alone)) => (false, IdentBang),
+ (IdentBang, Token::Group(Delimiter::Parenthesis | Delimiter::Bracket, _)) => {
+ (false, Other)
+ }
+ (Colon, Token::Punct(':', _)) => (false, Colon2),
+ (_, Token::Group(Delimiter::Parenthesis | Delimiter::Bracket, _)) => (true, Delim),
+ (_, Token::Group(Delimiter::Brace | Delimiter::None, _)) => (true, Other),
+ (_, Token::Ident(ident)) if !is_keyword(ident) => {
+ (state != Dot && state != Colon2, Ident)
+ }
+ (_, Token::Literal(lit)) if lit.to_string().ends_with('.') => (state != Dot, Other),
+ (_, Token::Literal(_)) => (state != Dot, Ident),
+ (_, Token::Punct(',' | ';', _)) => (false, Other),
+ (_, Token::Punct('.', _)) if !matcher => (state != Ident && state != Delim, Dot),
+ (_, Token::Punct(':', Spacing::Joint)) => {
+ (state != Ident && state != DollarCrate, Colon)
+ }
+ (_, Token::Punct('$', _)) => (true, Dollar),
+ (_, Token::Punct('#', _)) => (true, Pound),
+ (_, _) => (true, Other),
+ };
+ if !previous_is_joint {
+ if needs_space {
+ self.space();
+ } else if let Token::Punct('.', _) = token {
+ self.zerobreak();
+ }
+ }
+ previous_is_joint = match token {
+ Token::Punct(_, Spacing::Joint) | Token::Punct('$', _) => true,
+ _ => false,
+ };
+ self.single_token(
+ token,
+ if matcher {
+ |printer, stream| printer.macro_rules_tokens(stream, true)
+ } else {
+ |printer, stream| printer.macro_rules_tokens(stream, false)
+ },
+ );
+ state = next_state;
+ }
+ }
+}
+
+pub(crate) fn requires_semi(delimiter: &MacroDelimiter) -> bool {
+ match delimiter {
+ MacroDelimiter::Paren(_) | MacroDelimiter::Bracket(_) => true,
+ MacroDelimiter::Brace(_) => false,
+ }
+}
+
+fn is_keyword(ident: &Ident) -> bool {
+ match ident.to_string().as_str() {
+ "as" | "async" | "await" | "box" | "break" | "const" | "continue" | "crate" | "dyn"
+ | "else" | "enum" | "extern" | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop"
+ | "macro" | "match" | "mod" | "move" | "mut" | "pub" | "ref" | "return" | "static"
+ | "struct" | "trait" | "type" | "unsafe" | "use" | "where" | "while" | "yield" => true,
+ _ => false,
+ }
+}
+
+#[cfg(feature = "verbatim")]
+mod standard_library {
+ use crate::algorithm::Printer;
+ use crate::expr;
+ use crate::fixup::FixupContext;
+ use crate::iter::IterDelimited;
+ use crate::path::PathKind;
+ use crate::INDENT;
+ use syn::ext::IdentExt;
+ use syn::parse::{Parse, ParseStream, Parser, Result};
+ use syn::punctuated::Punctuated;
+ use syn::{
+ parenthesized, token, Attribute, Expr, ExprAssign, ExprPath, Ident, Lit, Macro, Pat, Path,
+ Token, Type, Visibility,
+ };
+
+ enum KnownMacro {
+ Expr(Expr),
+ Exprs(Vec<Expr>),
+ Cfg(Cfg),
+ Matches(Matches),
+ ThreadLocal(Vec<ThreadLocal>),
+ VecArray(Punctuated<Expr, Token![,]>),
+ VecRepeat { elem: Expr, n: Expr },
+ }
+
+ enum Cfg {
+ Eq(Ident, Option<Lit>),
+ Call(Ident, Vec<Cfg>),
+ }
+
+ struct Matches {
+ expression: Expr,
+ pattern: Pat,
+ guard: Option<Expr>,
+ }
+
+ struct ThreadLocal {
+ attrs: Vec<Attribute>,
+ vis: Visibility,
+ name: Ident,
+ ty: Type,
+ init: Expr,
+ }
+
+ struct FormatArgs {
+ format_string: Expr,
+ args: Vec<Expr>,
+ }
+
+ impl Parse for FormatArgs {
+ fn parse(input: ParseStream) -> Result<Self> {
+ let format_string: Expr = input.parse()?;
+
+ let mut args = Vec::new();
+ while !input.is_empty() {
+ input.parse::<Token![,]>()?;
+ if input.is_empty() {
+ break;
+ }
+ let arg = if input.peek(Ident::peek_any)
+ && input.peek2(Token![=])
+ && !input.peek2(Token![==])
+ {
+ let key = input.call(Ident::parse_any)?;
+ let eq_token: Token![=] = input.parse()?;
+ let value: Expr = input.parse()?;
+ Expr::Assign(ExprAssign {
+ attrs: Vec::new(),
+ left: Box::new(Expr::Path(ExprPath {
+ attrs: Vec::new(),
+ qself: None,
+ path: Path::from(key),
+ })),
+ eq_token,
+ right: Box::new(value),
+ })
+ } else {
+ input.parse()?
+ };
+ args.push(arg);
+ }
+
+ Ok(FormatArgs {
+ format_string,
+ args,
+ })
+ }
+ }
+
+ impl KnownMacro {
+ fn parse_expr(input: ParseStream) -> Result<Self> {
+ let expr: Expr = input.parse()?;
+ Ok(KnownMacro::Expr(expr))
+ }
+
+ fn parse_expr_comma(input: ParseStream) -> Result<Self> {
+ let expr: Expr = input.parse()?;
+ input.parse::<Option<Token![,]>>()?;
+ Ok(KnownMacro::Exprs(vec![expr]))
+ }
+
+ fn parse_exprs(input: ParseStream) -> Result<Self> {
+ let exprs = input.parse_terminated(Expr::parse, Token![,])?;
+ Ok(KnownMacro::Exprs(Vec::from_iter(exprs)))
+ }
+
+ fn parse_assert(input: ParseStream) -> Result<Self> {
+ let mut exprs = Vec::new();
+ let cond: Expr = input.parse()?;
+ exprs.push(cond);
+ if input.parse::<Option<Token![,]>>()?.is_some() && !input.is_empty() {
+ let format_args: FormatArgs = input.parse()?;
+ exprs.push(format_args.format_string);
+ exprs.extend(format_args.args);
+ }
+ Ok(KnownMacro::Exprs(exprs))
+ }
+
+ fn parse_assert_cmp(input: ParseStream) -> Result<Self> {
+ let mut exprs = Vec::new();
+ let left: Expr = input.parse()?;
+ exprs.push(left);
+ input.parse::<Token![,]>()?;
+ let right: Expr = input.parse()?;
+ exprs.push(right);
+ if input.parse::<Option<Token![,]>>()?.is_some() && !input.is_empty() {
+ let format_args: FormatArgs = input.parse()?;
+ exprs.push(format_args.format_string);
+ exprs.extend(format_args.args);
+ }
+ Ok(KnownMacro::Exprs(exprs))
+ }
+
+ fn parse_cfg(input: ParseStream) -> Result<Self> {
+ fn parse_single(input: ParseStream) -> Result<Cfg> {
+ let ident: Ident = input.parse()?;
+ if input.peek(token::Paren) && (ident == "all" || ident == "any") {
+ let content;
+ parenthesized!(content in input);
+ let list = content.call(parse_multiple)?;
+ Ok(Cfg::Call(ident, list))
+ } else if input.peek(token::Paren) && ident == "not" {
+ let content;
+ parenthesized!(content in input);
+ let cfg = content.call(parse_single)?;
+ content.parse::<Option<Token![,]>>()?;
+ Ok(Cfg::Call(ident, vec![cfg]))
+ } else if input.peek(Token![=]) {
+ input.parse::<Token![=]>()?;
+ let string: Lit = input.parse()?;
+ Ok(Cfg::Eq(ident, Some(string)))
+ } else {
+ Ok(Cfg::Eq(ident, None))
+ }
+ }
+
+ fn parse_multiple(input: ParseStream) -> Result<Vec<Cfg>> {
+ let mut vec = Vec::new();
+ while !input.is_empty() {
+ let cfg = input.call(parse_single)?;
+ vec.push(cfg);
+ if input.is_empty() {
+ break;
+ }
+ input.parse::<Token![,]>()?;
+ }
+ Ok(vec)
+ }
+
+ let cfg = input.call(parse_single)?;
+ input.parse::<Option<Token![,]>>()?;
+ Ok(KnownMacro::Cfg(cfg))
+ }
+
+ fn parse_env(input: ParseStream) -> Result<Self> {
+ let mut exprs = Vec::new();
+ let name: Expr = input.parse()?;
+ exprs.push(name);
+ if input.parse::<Option<Token![,]>>()?.is_some() && !input.is_empty() {
+ let error_msg: Expr = input.parse()?;
+ exprs.push(error_msg);
+ input.parse::<Option<Token![,]>>()?;
+ }
+ Ok(KnownMacro::Exprs(exprs))
+ }
+
+ fn parse_format_args(input: ParseStream) -> Result<Self> {
+ let format_args: FormatArgs = input.parse()?;
+ let mut exprs = format_args.args;
+ exprs.insert(0, format_args.format_string);
+ Ok(KnownMacro::Exprs(exprs))
+ }
+
+ fn parse_matches(input: ParseStream) -> Result<Self> {
+ let expression: Expr = input.parse()?;
+ input.parse::<Token![,]>()?;
+ let pattern = input.call(Pat::parse_multi_with_leading_vert)?;
+ let guard = if input.parse::<Option<Token![if]>>()?.is_some() {
+ Some(input.parse()?)
+ } else {
+ None
+ };
+ input.parse::<Option<Token![,]>>()?;
+ Ok(KnownMacro::Matches(Matches {
+ expression,
+ pattern,
+ guard,
+ }))
+ }
+
+ fn parse_thread_local(input: ParseStream) -> Result<Self> {
+ let mut items = Vec::new();
+ while !input.is_empty() {
+ let attrs = input.call(Attribute::parse_outer)?;
+ let vis: Visibility = input.parse()?;
+ input.parse::<Token![static]>()?;
+ let name: Ident = input.parse()?;
+ input.parse::<Token![:]>()?;
+ let ty: Type = input.parse()?;
+ input.parse::<Token![=]>()?;
+ let init: Expr = input.parse()?;
+ if input.is_empty() {
+ break;
+ }
+ input.parse::<Token![;]>()?;
+ items.push(ThreadLocal {
+ attrs,
+ vis,
+ name,
+ ty,
+ init,
+ });
+ }
+ Ok(KnownMacro::ThreadLocal(items))
+ }
+
+ fn parse_vec(input: ParseStream) -> Result<Self> {
+ if input.is_empty() {
+ return Ok(KnownMacro::VecArray(Punctuated::new()));
+ }
+ let first: Expr = input.parse()?;
+ if input.parse::<Option<Token![;]>>()?.is_some() {
+ let len: Expr = input.parse()?;
+ Ok(KnownMacro::VecRepeat {
+ elem: first,
+ n: len,
+ })
+ } else {
+ let mut vec = Punctuated::new();
+ vec.push_value(first);
+ while !input.is_empty() {
+ let comma: Token![,] = input.parse()?;
+ vec.push_punct(comma);
+ if input.is_empty() {
+ break;
+ }
+ let next: Expr = input.parse()?;
+ vec.push_value(next);
+ }
+ Ok(KnownMacro::VecArray(vec))
+ }
+ }
+
+ fn parse_write(input: ParseStream) -> Result<Self> {
+ let mut exprs = Vec::new();
+ let dst: Expr = input.parse()?;
+ exprs.push(dst);
+ input.parse::<Token![,]>()?;
+ let format_args: FormatArgs = input.parse()?;
+ exprs.push(format_args.format_string);
+ exprs.extend(format_args.args);
+ Ok(KnownMacro::Exprs(exprs))
+ }
+
+ fn parse_writeln(input: ParseStream) -> Result<Self> {
+ let mut exprs = Vec::new();
+ let dst: Expr = input.parse()?;
+ exprs.push(dst);
+ if input.parse::<Option<Token![,]>>()?.is_some() && !input.is_empty() {
+ let format_args: FormatArgs = input.parse()?;
+ exprs.push(format_args.format_string);
+ exprs.extend(format_args.args);
+ }
+ Ok(KnownMacro::Exprs(exprs))
+ }
+ }
+
+ impl Printer {
+ pub fn standard_library_macro(&mut self, mac: &Macro, mut semicolon: bool) -> bool {
+ let name = mac.path.segments.last().unwrap().ident.to_string();
+ let parser = match name.as_str() {
+ "addr_of" | "addr_of_mut" => KnownMacro::parse_expr,
+ "assert" | "debug_assert" => KnownMacro::parse_assert,
+ "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne" => {
+ KnownMacro::parse_assert_cmp
+ }
+ "cfg" => KnownMacro::parse_cfg,
+ "compile_error" | "include" | "include_bytes" | "include_str" | "option_env" => {
+ KnownMacro::parse_expr_comma
+ }
+ "concat" | "concat_bytes" | "dbg" => KnownMacro::parse_exprs,
+ "const_format_args" | "eprint" | "eprintln" | "format" | "format_args"
+ | "format_args_nl" | "panic" | "print" | "println" | "todo" | "unimplemented"
+ | "unreachable" => KnownMacro::parse_format_args,
+ "env" => KnownMacro::parse_env,
+ "matches" => KnownMacro::parse_matches,
+ "thread_local" => KnownMacro::parse_thread_local,
+ "vec" => KnownMacro::parse_vec,
+ "write" => KnownMacro::parse_write,
+ "writeln" => KnownMacro::parse_writeln,
+ _ => return false,
+ };
+
+ let known_macro = match parser.parse2(mac.tokens.clone()) {
+ Ok(known_macro) => known_macro,
+ Err(_) => return false,
+ };
+
+ self.path(&mac.path, PathKind::Simple);
+ self.word("!");
+
+ match &known_macro {
+ KnownMacro::Expr(expr) => {
+ self.word("(");
+ self.cbox(INDENT);
+ self.zerobreak();
+ self.expr(expr, FixupContext::NONE);
+ self.zerobreak();
+ self.offset(-INDENT);
+ self.end();
+ self.word(")");
+ }
+ KnownMacro::Exprs(exprs) => {
+ self.word("(");
+ self.cbox(INDENT);
+ self.zerobreak();
+ for elem in exprs.iter().delimited() {
+ self.expr(&elem, FixupContext::NONE);
+ self.trailing_comma(elem.is_last);
+ }
+ self.offset(-INDENT);
+ self.end();
+ self.word(")");
+ }
+ KnownMacro::Cfg(cfg) => {
+ self.word("(");
+ self.cfg(cfg);
+ self.word(")");
+ }
+ KnownMacro::Matches(matches) => {
+ self.word("(");
+ self.cbox(INDENT);
+ self.zerobreak();
+ self.expr(&matches.expression, FixupContext::NONE);
+ self.word(",");
+ self.space();
+ self.pat(&matches.pattern);
+ if let Some(guard) = &matches.guard {
+ self.space();
+ self.word("if ");
+ self.expr(guard, FixupContext::NONE);
+ }
+ self.zerobreak();
+ self.offset(-INDENT);
+ self.end();
+ self.word(")");
+ }
+ KnownMacro::ThreadLocal(items) => {
+ self.word(" {");
+ self.cbox(INDENT);
+ self.hardbreak_if_nonempty();
+ for item in items {
+ self.outer_attrs(&item.attrs);
+ self.cbox(0);
+ self.visibility(&item.vis);
+ self.word("static ");
+ self.ident(&item.name);
+ self.word(": ");
+ self.ty(&item.ty);
+ self.word(" = ");
+ self.neverbreak();
+ self.expr(&item.init, FixupContext::NONE);
+ self.word(";");
+ self.end();
+ self.hardbreak();
+ }
+ self.offset(-INDENT);
+ self.end();
+ self.word("}");
+ semicolon = false;
+ }
+ KnownMacro::VecArray(vec) => {
+ if vec.is_empty() {
+ self.word("[]");
+ } else if expr::simple_array(vec) {
+ self.cbox(INDENT);
+ self.word("[");
+ self.zerobreak();
+ self.ibox(0);
+ for elem in vec.iter().delimited() {
+ self.expr(&elem, FixupContext::NONE);
+ if !elem.is_last {
+ self.word(",");
+ self.space();
+ }
+ }
+ self.end();
+ self.trailing_comma(true);
+ self.offset(-INDENT);
+ self.word("]");
+ self.end();
+ } else {
+ self.word("[");
+ self.cbox(INDENT);
+ self.zerobreak();
+ for elem in vec.iter().delimited() {
+ self.expr(&elem, FixupContext::NONE);
+ self.trailing_comma(elem.is_last);
+ }
+ self.offset(-INDENT);
+ self.end();
+ self.word("]");
+ }
+ }
+ KnownMacro::VecRepeat { elem, n } => {
+ self.word("[");
+ self.cbox(INDENT);
+ self.zerobreak();
+ self.expr(elem, FixupContext::NONE);
+ self.word(";");
+ self.space();
+ self.expr(n, FixupContext::NONE);
+ self.zerobreak();
+ self.offset(-INDENT);
+ self.end();
+ self.word("]");
+ }
+ }
+
+ if semicolon {
+ self.word(";");
+ }
+
+ true
+ }
+
+ fn cfg(&mut self, cfg: &Cfg) {
+ match cfg {
+ Cfg::Eq(ident, value) => {
+ self.ident(ident);
+ if let Some(value) = value {
+ self.word(" = ");
+ self.lit(value);
+ }
+ }
+ Cfg::Call(ident, args) => {
+ self.ident(ident);
+ self.word("(");
+ self.cbox(INDENT);
+ self.zerobreak();
+ for arg in args.iter().delimited() {
+ self.cfg(&arg);
+ self.trailing_comma(arg.is_last);
+ }
+ self.offset(-INDENT);
+ self.end();
+ self.word(")");
+ }
+ }
+ }
+ }
+}