json_syntax/print/
mod.rs

1use std::fmt;
2
3#[cfg(feature = "contextual")]
4mod contextual;
5
6#[cfg(feature = "contextual")]
7pub use self::contextual::*;
8
9#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
10pub enum Indent {
11	Spaces(u8),
12	Tabs(u8),
13}
14
15impl Indent {
16	pub fn by(self, n: usize) -> IndentBy {
17		IndentBy(self, n)
18	}
19}
20
21impl fmt::Display for Indent {
22	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
23		match self {
24			Self::Spaces(n) => {
25				for _ in 0..*n {
26					f.write_str(" ")?
27				}
28			}
29			Self::Tabs(n) => {
30				for _ in 0..*n {
31					f.write_str("\t")?
32				}
33			}
34		}
35
36		Ok(())
37	}
38}
39
40pub struct IndentBy(Indent, usize);
41
42impl fmt::Display for IndentBy {
43	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
44		for _ in 0..self.1 {
45			self.0.fmt(f)?
46		}
47
48		Ok(())
49	}
50}
51
52pub struct Spaces(pub usize);
53
54impl fmt::Display for Spaces {
55	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56		for _ in 0..self.0 {
57			f.write_str(" ")?
58		}
59
60		Ok(())
61	}
62}
63
64#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
65pub enum Padding {
66	Spaces(u8),
67	NewLine,
68}
69
70impl fmt::Display for Padding {
71	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
72		match self {
73			Self::Spaces(n) => {
74				for _ in 0..*n {
75					f.write_str(" ")?
76				}
77			}
78			Self::NewLine => f.write_str("\n")?,
79		}
80
81		Ok(())
82	}
83}
84
85#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
86pub enum Limit {
87	/// Always expanded, even if empty.
88	Always,
89
90	/// Expanded if the array/object has more than the given number of items.
91	Item(usize),
92
93	/// Expanded if the representation of the array/object is more than the
94	/// given number of characters long.
95	Width(usize),
96
97	/// Expanded if the array/object has more than the given number of items
98	/// (first argument), or if its the representation is more than the
99	/// given number of characters long (second argument).
100	ItemOrWidth(usize, usize),
101}
102
103/// Print options.
104#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
105#[non_exhaustive]
106pub struct Options {
107	/// Indentation string.
108	pub indent: Indent,
109
110	/// String added after `[`.
111	pub array_begin: usize,
112
113	/// String added before `]`.
114	pub array_end: usize,
115
116	/// Number of spaces inside an inlined empty array.
117	pub array_empty: usize,
118
119	/// Number of spaces before a comma in an array.
120	pub array_before_comma: usize,
121
122	/// Number of spaces after a comma in an array.
123	pub array_after_comma: usize,
124
125	/// Limit after which an array is expanded.
126	pub array_limit: Option<Limit>,
127
128	/// String added after `{`.
129	pub object_begin: usize,
130
131	/// String added before `}`.
132	pub object_end: usize,
133
134	/// Number of spaces inside an inlined empty object.
135	pub object_empty: usize,
136
137	/// Number of spaces before a comma in an object.
138	pub object_before_comma: usize,
139
140	/// Number of spaces after a comma in an object.
141	pub object_after_comma: usize,
142
143	/// Number of spaces before a colon in an object.
144	pub object_before_colon: usize,
145
146	/// Number of spaces after a colon in an object.
147	pub object_after_colon: usize,
148
149	/// Limit after which an array is expanded.
150	pub object_limit: Option<Limit>,
151}
152
153impl Options {
154	/// Pretty print options.
155	#[inline(always)]
156	pub fn pretty() -> Self {
157		Self {
158			indent: Indent::Spaces(2),
159			array_begin: 1,
160			array_end: 1,
161			array_empty: 0,
162			array_before_comma: 0,
163			array_after_comma: 1,
164			array_limit: Some(Limit::ItemOrWidth(1, 16)),
165			object_begin: 1,
166			object_end: 1,
167			object_empty: 0,
168			object_before_comma: 0,
169			object_after_comma: 1,
170			object_before_colon: 0,
171			object_after_colon: 1,
172			object_limit: Some(Limit::ItemOrWidth(1, 16)),
173		}
174	}
175
176	/// Compact print options.
177	///
178	/// Values will be formatted on a single line without spaces.
179	#[inline(always)]
180	pub fn compact() -> Self {
181		Self {
182			indent: Indent::Spaces(0),
183			array_begin: 0,
184			array_end: 0,
185			array_empty: 0,
186			array_before_comma: 0,
187			array_after_comma: 0,
188			array_limit: None,
189			object_begin: 0,
190			object_end: 0,
191			object_empty: 0,
192			object_before_comma: 0,
193			object_after_comma: 0,
194			object_before_colon: 0,
195			object_after_colon: 0,
196			object_limit: None,
197		}
198	}
199
200	/// Inline print options.
201	///
202	/// Values will be formatted on a single line with some spaces.
203	#[inline(always)]
204	pub fn inline() -> Self {
205		Self {
206			indent: Indent::Spaces(0),
207			array_begin: 1,
208			array_end: 1,
209			array_empty: 0,
210			array_before_comma: 0,
211			array_after_comma: 1,
212			array_limit: None,
213			object_begin: 1,
214			object_end: 1,
215			object_empty: 0,
216			object_before_comma: 0,
217			object_after_comma: 1,
218			object_before_colon: 0,
219			object_after_colon: 1,
220			object_limit: None,
221		}
222	}
223}
224
225/// The size of a value.
226#[derive(Clone, Copy)]
227pub enum Size {
228	/// The value (array or object) is expanded on multiple lines.
229	Expanded,
230
231	/// The value is formatted in a single line with the given character width.
232	Width(usize),
233}
234
235impl Size {
236	pub fn add(&mut self, other: Self) {
237		*self = match (*self, other) {
238			(Self::Width(a), Self::Width(b)) => Self::Width(a + b),
239			_ => Self::Expanded,
240		}
241	}
242}
243
244/// Print methods.
245pub trait Print {
246	/// Print the value with `Options::pretty` options.
247	#[inline(always)]
248	fn pretty_print(&self) -> Printed<'_, Self> {
249		self.print_with(Options::pretty())
250	}
251
252	/// Print the value with `Options::compact` options.
253	#[inline(always)]
254	fn compact_print(&self) -> Printed<'_, Self> {
255		self.print_with(Options::compact())
256	}
257
258	/// Print the value with `Options::inline` options.
259	#[inline(always)]
260	fn inline_print(&self) -> Printed<'_, Self> {
261		self.print_with(Options::inline())
262	}
263
264	/// Print the value with the given options.
265	#[inline(always)]
266	fn print_with(&self, options: Options) -> Printed<'_, Self> {
267		Printed(self, options, 0)
268	}
269
270	fn fmt_with(&self, f: &mut fmt::Formatter, options: &Options, indent: usize) -> fmt::Result;
271}
272
273impl<T: Print> Print for locspan::Stripped<T> {
274	fn fmt_with(&self, f: &mut fmt::Formatter, options: &Options, indent: usize) -> fmt::Result {
275		self.0.fmt_with(f, options, indent)
276	}
277}
278
279impl<T: Print, M> Print for locspan::Meta<T, M> {
280	fn fmt_with(&self, f: &mut fmt::Formatter, options: &Options, indent: usize) -> fmt::Result {
281		self.value().fmt_with(f, options, indent)
282	}
283}
284
285impl<'a, T: Print + ?Sized> Print for &'a T {
286	fn fmt_with(&self, f: &mut fmt::Formatter, options: &Options, indent: usize) -> fmt::Result {
287		(**self).fmt_with(f, options, indent)
288	}
289}
290
291pub trait PrintWithSize {
292	fn fmt_with_size(
293		&self,
294		f: &mut fmt::Formatter,
295		options: &Options,
296		indent: usize,
297		sizes: &[Size],
298		index: &mut usize,
299	) -> fmt::Result;
300}
301
302impl<T: PrintWithSize> PrintWithSize for locspan::Stripped<T> {
303	fn fmt_with_size(
304		&self,
305		f: &mut fmt::Formatter,
306		options: &Options,
307		indent: usize,
308		sizes: &[Size],
309		index: &mut usize,
310	) -> fmt::Result {
311		self.0.fmt_with_size(f, options, indent, sizes, index)
312	}
313}
314
315impl<T: PrintWithSize, M> PrintWithSize for locspan::Meta<T, M> {
316	fn fmt_with_size(
317		&self,
318		f: &mut fmt::Formatter,
319		options: &Options,
320		indent: usize,
321		sizes: &[Size],
322		index: &mut usize,
323	) -> fmt::Result {
324		self.value().fmt_with_size(f, options, indent, sizes, index)
325	}
326}
327
328impl<'a, T: PrintWithSize + ?Sized> PrintWithSize for &'a T {
329	fn fmt_with_size(
330		&self,
331		f: &mut fmt::Formatter,
332		options: &Options,
333		indent: usize,
334		sizes: &[Size],
335		index: &mut usize,
336	) -> fmt::Result {
337		(**self).fmt_with_size(f, options, indent, sizes, index)
338	}
339}
340
341/// Printed value.
342pub struct Printed<'t, T: ?Sized>(&'t T, Options, usize);
343
344impl<'t, T: Print> fmt::Display for Printed<'t, T> {
345	#[inline(always)]
346	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
347		self.0.fmt_with(f, &self.1, self.2)
348	}
349}
350
351impl Print for bool {
352	#[inline(always)]
353	fn fmt_with(&self, f: &mut fmt::Formatter, _options: &Options, _indent: usize) -> fmt::Result {
354		if *self {
355			f.write_str("true")
356		} else {
357			f.write_str("false")
358		}
359	}
360}
361
362impl Print for crate::NumberBuf {
363	#[inline(always)]
364	fn fmt_with(&self, f: &mut fmt::Formatter, _options: &Options, _indent: usize) -> fmt::Result {
365		fmt::Display::fmt(self, f)
366	}
367}
368
369/// Formats a string literal according to [RFC8785](https://www.rfc-editor.org/rfc/rfc8785#name-serialization-of-strings).
370pub fn string_literal(s: &str, f: &mut fmt::Formatter) -> fmt::Result {
371	use fmt::Display;
372	f.write_str("\"")?;
373
374	for c in s.chars() {
375		match c {
376			'\\' => f.write_str("\\\\")?,
377			'\"' => f.write_str("\\\"")?,
378			'\u{0008}' => f.write_str("\\b")?,
379			'\u{0009}' => f.write_str("\\t")?,
380			'\u{000a}' => f.write_str("\\n")?,
381			'\u{000c}' => f.write_str("\\f")?,
382			'\u{000d}' => f.write_str("\\r")?,
383			'\u{0000}'..='\u{001f}' => {
384				f.write_str("\\u")?;
385
386				let codepoint = c as u32;
387				let d = codepoint & 0x000f;
388				let c = (codepoint & 0x00f0) >> 4;
389				let b = (codepoint & 0x0f00) >> 8;
390				let a = (codepoint & 0xf000) >> 12;
391
392				digit(a).fmt(f)?;
393				digit(b).fmt(f)?;
394				digit(c).fmt(f)?;
395				digit(d).fmt(f)?
396			}
397			_ => c.fmt(f)?,
398		}
399	}
400
401	f.write_str("\"")
402}
403
404fn digit(c: u32) -> char {
405	match c {
406		0x0 => '0',
407		0x1 => '1',
408		0x2 => '2',
409		0x3 => '3',
410		0x4 => '4',
411		0x5 => '5',
412		0x6 => '6',
413		0x7 => '7',
414		0x8 => '8',
415		0x9 => '9',
416		0xa => 'a',
417		0xb => 'b',
418		0xc => 'c',
419		0xd => 'd',
420		0xe => 'e',
421		0xf => 'f',
422		_ => panic!("invalid input: {}", c),
423	}
424}
425
426/// Returns the byte length of string literal according to [RFC8785](https://www.rfc-editor.org/rfc/rfc8785#name-serialization-of-strings).
427pub fn printed_string_size(s: &str) -> usize {
428	let mut width = 2;
429
430	for c in s.chars() {
431		width += match c {
432			'\\' | '\"' | '\u{0008}' | '\u{0009}' | '\u{000a}' | '\u{000c}' | '\u{000d}' => 2,
433			'\u{0000}'..='\u{001f}' => 6,
434			_ => 1,
435		}
436	}
437
438	width
439}
440
441impl Print for crate::String {
442	#[inline(always)]
443	fn fmt_with(&self, f: &mut fmt::Formatter, _options: &Options, _indent: usize) -> fmt::Result {
444		string_literal(self, f)
445	}
446}
447
448pub fn print_array<I: IntoIterator>(
449	items: I,
450	f: &mut fmt::Formatter,
451	options: &Options,
452	indent: usize,
453	sizes: &[Size],
454	index: &mut usize,
455) -> fmt::Result
456where
457	I::IntoIter: ExactSizeIterator,
458	I::Item: PrintWithSize,
459{
460	use fmt::Display;
461	let size = sizes[*index];
462	*index += 1;
463
464	f.write_str("[")?;
465
466	let items = items.into_iter();
467	if items.len() == 0 {
468		match size {
469			Size::Expanded => {
470				f.write_str("\n")?;
471				options.indent.by(indent).fmt(f)?;
472			}
473			Size::Width(_) => Spaces(options.array_empty).fmt(f)?,
474		}
475	} else {
476		match size {
477			Size::Expanded => {
478				f.write_str("\n")?;
479
480				for (i, item) in items.enumerate() {
481					if i > 0 {
482						Spaces(options.array_before_comma).fmt(f)?;
483						f.write_str(",\n")?
484					}
485
486					options.indent.by(indent + 1).fmt(f)?;
487					item.fmt_with_size(f, options, indent + 1, sizes, index)?
488				}
489
490				f.write_str("\n")?;
491				options.indent.by(indent).fmt(f)?;
492			}
493			Size::Width(_) => {
494				Spaces(options.array_begin).fmt(f)?;
495				for (i, item) in items.enumerate() {
496					if i > 0 {
497						Spaces(options.array_before_comma).fmt(f)?;
498						f.write_str(",")?;
499						Spaces(options.array_after_comma).fmt(f)?
500					}
501
502					item.fmt_with_size(f, options, indent + 1, sizes, index)?
503				}
504				Spaces(options.array_end).fmt(f)?
505			}
506		}
507	}
508
509	f.write_str("]")
510}
511
512impl<T: PrintWithSize> PrintWithSize for Vec<T> {
513	#[inline(always)]
514	fn fmt_with_size(
515		&self,
516		f: &mut fmt::Formatter,
517		options: &Options,
518		indent: usize,
519		sizes: &[Size],
520		index: &mut usize,
521	) -> fmt::Result {
522		print_array(self, f, options, indent, sizes, index)
523	}
524}
525
526pub fn print_object<'a, V, I: IntoIterator<Item = (&'a str, V)>>(
527	entries: I,
528	f: &mut fmt::Formatter,
529	options: &Options,
530	indent: usize,
531	sizes: &[Size],
532	index: &mut usize,
533) -> fmt::Result
534where
535	I::IntoIter: ExactSizeIterator,
536	V: PrintWithSize,
537{
538	use fmt::Display;
539	let size = sizes[*index];
540	*index += 1;
541
542	f.write_str("{")?;
543
544	let entries = entries.into_iter();
545	if entries.len() == 0 {
546		match size {
547			Size::Expanded => {
548				f.write_str("\n")?;
549				options.indent.by(indent).fmt(f)?;
550			}
551			Size::Width(_) => Spaces(options.object_empty).fmt(f)?,
552		}
553	} else {
554		match size {
555			Size::Expanded => {
556				f.write_str("\n")?;
557
558				for (i, (key, value)) in entries.enumerate() {
559					if i > 0 {
560						Spaces(options.object_before_comma).fmt(f)?;
561						f.write_str(",\n")?
562					}
563
564					options.indent.by(indent + 1).fmt(f)?;
565
566					string_literal(key, f)?;
567					Spaces(options.object_before_colon).fmt(f)?;
568					f.write_str(":")?;
569					Spaces(options.object_after_colon).fmt(f)?;
570
571					value.fmt_with_size(f, options, indent + 1, sizes, index)?
572				}
573
574				f.write_str("\n")?;
575				options.indent.by(indent).fmt(f)?;
576			}
577			Size::Width(_) => {
578				Spaces(options.object_begin).fmt(f)?;
579				for (i, (key, value)) in entries.enumerate() {
580					if i > 0 {
581						Spaces(options.object_before_comma).fmt(f)?;
582						f.write_str(",")?;
583						Spaces(options.object_after_comma).fmt(f)?
584					}
585
586					string_literal(key, f)?;
587					Spaces(options.object_before_colon).fmt(f)?;
588					f.write_str(":")?;
589					Spaces(options.object_after_colon).fmt(f)?;
590
591					value.fmt_with_size(f, options, indent + 1, sizes, index)?
592				}
593				Spaces(options.object_end).fmt(f)?
594			}
595		}
596	}
597
598	f.write_str("}")
599}
600
601impl PrintWithSize for crate::Object {
602	#[inline(always)]
603	fn fmt_with_size(
604		&self,
605		f: &mut fmt::Formatter,
606		options: &Options,
607		indent: usize,
608		sizes: &[Size],
609		index: &mut usize,
610	) -> fmt::Result {
611		print_object(
612			self.iter().map(|e| (e.key.as_str(), &e.value)),
613			f,
614			options,
615			indent,
616			sizes,
617			index,
618		)
619	}
620}
621
622pub trait PrecomputeSize {
623	fn pre_compute_size(&self, options: &Options, sizes: &mut Vec<Size>) -> Size;
624}
625
626impl PrecomputeSize for bool {
627	#[inline(always)]
628	fn pre_compute_size(&self, _options: &Options, _sizes: &mut Vec<Size>) -> Size {
629		if *self {
630			Size::Width(4)
631		} else {
632			Size::Width(5)
633		}
634	}
635}
636
637impl PrecomputeSize for crate::Value {
638	fn pre_compute_size(&self, options: &Options, sizes: &mut Vec<Size>) -> Size {
639		match self {
640			crate::Value::Null => Size::Width(4),
641			crate::Value::Boolean(b) => b.pre_compute_size(options, sizes),
642			crate::Value::Number(n) => Size::Width(n.as_str().len()),
643			crate::Value::String(s) => Size::Width(printed_string_size(s)),
644			crate::Value::Array(a) => pre_compute_array_size(a, options, sizes),
645			crate::Value::Object(o) => pre_compute_object_size(
646				o.iter().map(|e| (e.key.as_str(), &e.value)),
647				options,
648				sizes,
649			),
650		}
651	}
652}
653
654impl<'a, T: PrecomputeSize + ?Sized> PrecomputeSize for &'a T {
655	fn pre_compute_size(&self, options: &Options, sizes: &mut Vec<Size>) -> Size {
656		(**self).pre_compute_size(options, sizes)
657	}
658}
659
660impl<T: PrecomputeSize> PrecomputeSize for locspan::Stripped<T> {
661	fn pre_compute_size(&self, options: &Options, sizes: &mut Vec<Size>) -> Size {
662		self.0.pre_compute_size(options, sizes)
663	}
664}
665
666impl<T: PrecomputeSize, M> PrecomputeSize for locspan::Meta<T, M> {
667	fn pre_compute_size(&self, options: &Options, sizes: &mut Vec<Size>) -> Size {
668		self.value().pre_compute_size(options, sizes)
669	}
670}
671
672pub fn pre_compute_array_size<I: IntoIterator>(
673	items: I,
674	options: &Options,
675	sizes: &mut Vec<Size>,
676) -> Size
677where
678	I::Item: PrecomputeSize,
679{
680	let index = sizes.len();
681	sizes.push(Size::Width(0));
682
683	let mut size = Size::Width(2 + options.object_begin + options.object_end);
684
685	let mut len = 0;
686	for (i, item) in items.into_iter().enumerate() {
687		if i > 0 {
688			size.add(Size::Width(
689				1 + options.array_before_comma + options.array_after_comma,
690			));
691		}
692
693		size.add(item.pre_compute_size(options, sizes));
694		len += 1
695	}
696
697	let size = match size {
698		Size::Expanded => Size::Expanded,
699		Size::Width(width) => match options.array_limit {
700			None => Size::Width(width),
701			Some(Limit::Always) => Size::Expanded,
702			Some(Limit::Item(i)) => {
703				if len > i {
704					Size::Expanded
705				} else {
706					Size::Width(width)
707				}
708			}
709			Some(Limit::ItemOrWidth(i, w)) => {
710				if len > i || width > w {
711					Size::Expanded
712				} else {
713					Size::Width(width)
714				}
715			}
716			Some(Limit::Width(w)) => {
717				if width > w {
718					Size::Expanded
719				} else {
720					Size::Width(width)
721				}
722			}
723		},
724	};
725
726	sizes[index] = size;
727	size
728}
729
730pub fn pre_compute_object_size<'a, V, I: IntoIterator<Item = (&'a str, V)>>(
731	entries: I,
732	options: &Options,
733	sizes: &mut Vec<Size>,
734) -> Size
735where
736	V: PrecomputeSize,
737{
738	let index = sizes.len();
739	sizes.push(Size::Width(0));
740
741	let mut size = Size::Width(2 + options.object_begin + options.object_end);
742
743	let mut len = 0;
744	for (i, (key, value)) in entries.into_iter().enumerate() {
745		if i > 0 {
746			size.add(Size::Width(
747				1 + options.object_before_comma + options.object_after_comma,
748			));
749		}
750
751		size.add(Size::Width(
752			printed_string_size(key) + 1 + options.object_before_colon + options.object_after_colon,
753		));
754		size.add(value.pre_compute_size(options, sizes));
755		len += 1;
756	}
757
758	let size = match size {
759		Size::Expanded => Size::Expanded,
760		Size::Width(width) => match options.object_limit {
761			None => Size::Width(width),
762			Some(Limit::Always) => Size::Expanded,
763			Some(Limit::Item(i)) => {
764				if len > i {
765					Size::Expanded
766				} else {
767					Size::Width(width)
768				}
769			}
770			Some(Limit::ItemOrWidth(i, w)) => {
771				if len > i || width > w {
772					Size::Expanded
773				} else {
774					Size::Width(width)
775				}
776			}
777			Some(Limit::Width(w)) => {
778				if width > w {
779					Size::Expanded
780				} else {
781					Size::Width(width)
782				}
783			}
784		},
785	};
786
787	sizes[index] = size;
788	size
789}
790
791impl Print for crate::Value {
792	fn fmt_with(&self, f: &mut fmt::Formatter, options: &Options, indent: usize) -> fmt::Result {
793		match self {
794			Self::Null => f.write_str("null"),
795			Self::Boolean(b) => b.fmt_with(f, options, indent),
796			Self::Number(n) => n.fmt_with(f, options, indent),
797			Self::String(s) => s.fmt_with(f, options, indent),
798			Self::Array(a) => {
799				let mut sizes =
800					Vec::with_capacity(self.count(|_, v| v.is_array() || v.is_object()));
801				self.pre_compute_size(options, &mut sizes);
802				let mut index = 0;
803				a.fmt_with_size(f, options, indent, &sizes, &mut index)
804			}
805			Self::Object(o) => {
806				let mut sizes =
807					Vec::with_capacity(self.count(|_, v| v.is_array() || v.is_object()));
808				self.pre_compute_size(options, &mut sizes);
809				let mut index = 0;
810				o.fmt_with_size(f, options, indent, &sizes, &mut index)
811			}
812		}
813	}
814}
815
816impl PrintWithSize for crate::Value {
817	fn fmt_with_size(
818		&self,
819		f: &mut fmt::Formatter,
820		options: &Options,
821		indent: usize,
822		sizes: &[Size],
823		index: &mut usize,
824	) -> fmt::Result {
825		match self {
826			Self::Null => f.write_str("null"),
827			Self::Boolean(b) => b.fmt_with(f, options, indent),
828			Self::Number(n) => n.fmt_with(f, options, indent),
829			Self::String(s) => s.fmt_with(f, options, indent),
830			Self::Array(a) => a.fmt_with_size(f, options, indent, sizes, index),
831			Self::Object(o) => o.fmt_with_size(f, options, indent, sizes, index),
832		}
833	}
834}