git.delta.rocks / jrsonnet / refs/commits / fc45c6ca2bef

difftreelog

source

crates/jrsonnet-formatter/src/lib.rs20.9 KiBsourcehistory
1use std::{any::type_name, rc::Rc};23use children::{children_between, trivia_before};4use dprint_core::formatting::{5	condition_helpers::is_multiple_lines,6	condition_resolvers::true_resolver,7	ir_helpers::{new_line_group, with_indent},8	ConditionResolver, ConditionResolverContext, LineNumber, PrintItems, PrintOptions,9};10use hi_doc::{Formatting, SnippetBuilder};11use jrsonnet_rowan_parser::{12	nodes::{13		Arg, ArgsDesc, Assertion, BinaryOperator, Bind, CompSpec, Destruct, DestructArrayPart,14		DestructRest, Expr, ExprBase, FieldName, ForSpec, IfSpec, ImportKind, Literal, Member,15		Name, Number, ObjBody, ObjLocal, ParamsDesc, SliceDesc, SourceFile, Stmt, Suffix, Text,16		UnaryOperator, Visibility,17	},18	AstNode, AstToken as _, SyntaxToken,19};2021use crate::{22	children::{trivia_after, Child, EndingComments},23	comments::{format_comments, CommentLocation},24};2526mod children;27mod comments;28#[cfg(test)]29mod tests;3031fn with_indent_eoi(cond: ConditionResolver, o: PrintItems, e: EndingComments) -> PrintItems {32	let end_comments_items = {33		let mut items = PrintItems::new();34		if e.should_start_with_newline {35			p!(&mut items, nl);36		}37		format_comments(&e.trivia, CommentLocation::EndOfItems, &mut items);38		items.into_rc_path()39	};40	let items =41		new_line_group(pi!(@i; items(o.into()) items(end_comments_items.into()))).into_rc_path();4243	let indented = with_indent(pi!(@i; nl items(items.into())));4445	pi!(@i; if_else("indented body", cond, items(indented))(str(" ") items(items.into())))46}4748pub trait Printable {49	fn print(&self, out: &mut PrintItems);50}5152macro_rules! pi {53	(@i; $($t:tt)*) => {{54		#[allow(unused_mut)]55		let mut o = dprint_core::formatting::PrintItems::new();56		pi!(@s; o: $($t)*);57		o58	}};59	(@s; $o:ident: str($e:expr $(,)?) $($t:tt)*) => {{60		$o.push_string($e.to_owned());61		pi!(@s; $o: $($t)*);62	}};63	(@s; $o:ident: string($e:expr $(,)?) $($t:tt)*) => {{64		$o.push_string($e);65		pi!(@s; $o: $($t)*);66	}};67	(@s; $o:ident: nl $($t:tt)*) => {{68		$o.push_signal(dprint_core::formatting::Signal::NewLine);69		pi!(@s; $o: $($t)*);70	}};71	(@s; $o:ident: sonl $($t:tt)*) => {{72		$o.push_signal(dprint_core::formatting::Signal::SpaceOrNewLine);73		pi!(@s; $o: $($t)*);74	}};75	(@s; $o:ident: tab $($t:tt)*) => {{76		$o.push_signal(dprint_core::formatting::Signal::Tab);77		pi!(@s; $o: $($t)*);78	}};79	(@s; $o:ident: >i $($t:tt)*) => {{80		$o.push_signal(dprint_core::formatting::Signal::StartIndent);81		pi!(@s; $o: $($t)*);82	}};83	(@s; $o:ident: <i $($t:tt)*) => {{84		$o.push_signal(dprint_core::formatting::Signal::FinishIndent);85		pi!(@s; $o: $($t)*);86	}};87	(@s; $o:ident: info($v:expr) $($t:tt)*) => {{88		$o.push_info($v);89		pi!(@s; $o: $($t)*);90	}};91	(@s; $o:ident: ln_anchor($v:expr) $($t:tt)*) => {{92		$o.push_anchor(LineNumberAnchor::new($v));93		pi!(@s; $o: $($t)*);94	}};95	(@s; $o:ident: if($s:literal, $cond:expr, $($i:tt)*) $($t:tt)*) => {{96		$o.push_condition(dprint_core::formatting::conditions::if_true(97			$s,98			$cond.clone(),99			{100				let mut o = PrintItems::new();101				p!(o, $($i)*);102				o103			},104		));105		pi!(@s; $o: $($t)*);106	}};107	(@s; $o:ident: if_else($s:literal, $cond:expr, $($i:tt)*)($($e:tt)+) $($t:tt)*) => {{108		$o.push_condition(dprint_core::formatting::conditions::if_true_or(109			$s,110			$cond.clone(),111			{112				let mut o = PrintItems::new();113				p!(o, $($i)*);114				o115			},116			{117				let mut o = PrintItems::new();118				p!(o, $($e)*);119				o120			},121		));122		pi!(@s; $o: $($t)*);123	}};124	(@s; $o:ident: if_not($s:literal, $cond:expr, $($e:tt)*) $($t:tt)*) => {{125		$o.push_condition(dprint_core::formatting::conditions::if_true_or(126			$s,127			$cond.clone(),128			{129				let o = PrintItems::new();130				o131			},132			{133				let mut o = PrintItems::new();134				p!(o, $($e)*);135				o136			},137		));138		pi!(@s; $o: $($t)*);139	}};140	(@s; $o:ident: {$expr:expr} $($t:tt)*) => {{141		$expr.print($o);142		pi!(@s; $o: $($t)*);143	}};144	(@s; $o:ident: items($expr:expr) $($t:tt)*) => {{145		$o.extend($expr);146		pi!(@s; $o: $($t)*);147	}};148	(@s; $o:ident: if ($e:expr)($($then:tt)*) $($t:tt)*) => {{149		if $e {150			pi!(@s; $o: $($then)*);151		}152		pi!(@s; $o: $($t)*);153	}};154	(@s; $o:ident: ifelse ($e:expr)($($then:tt)*)($($else:tt)*) $($t:tt)*) => {{155		if $e {156			pi!(@s; $o: $($then)*);157		} else {158			pi!(@s; $o: $($else)*);159		}160		pi!(@s; $o: $($t)*);161	}};162	(@s; $i:ident:) => {}163}164macro_rules! p {165	($o:ident, $($t:tt)*) => {166		pi!(@s; $o: $($t)*)167	};168	(&mut $o:ident, $($t:tt)*) => {169		let om = &mut $o;170		pi!(@s; om: $($t)*)171	};172}173pub(crate) use p;174pub(crate) use pi;175176impl<P> Printable for Option<P>177where178	P: Printable,179{180	fn print(&self, out: &mut PrintItems) {181		if let Some(v) = self {182			v.print(out);183		} else {184			p!(185				out,186				string(format!(187					"/*missing {}*/",188					type_name::<P>().replace("jrsonnet_rowan_parser::generated::nodes::", "")189				),)190			);191		}192	}193}194195impl Printable for SyntaxToken {196	fn print(&self, out: &mut PrintItems) {197		p!(out, string(self.to_string()));198	}199}200201impl Printable for Text {202	fn print(&self, out: &mut PrintItems) {203		p!(out, string(format!("{}", self)));204	}205}206impl Printable for Number {207	fn print(&self, out: &mut PrintItems) {208		p!(out, string(format!("{}", self)));209	}210}211212impl Printable for Name {213	fn print(&self, out: &mut PrintItems) {214		p!(out, { self.ident_lit() });215	}216}217218impl Printable for DestructRest {219	fn print(&self, out: &mut PrintItems) {220		p!(out, str("..."));221		if let Some(name) = self.into() {222			p!(out, { name });223		}224	}225}226227impl Printable for Destruct {228	fn print(&self, out: &mut PrintItems) {229		match self {230			Self::DestructFull(f) => {231				p!(out, { f.name() });232			}233			Self::DestructSkip(_) => p!(out, str("?")),234			Self::DestructArray(a) => {235				p!(out, str("[") >i nl);236				for el in a.destruct_array_parts() {237					match el {238						DestructArrayPart::DestructArrayElement(e) => {239							p!(out, {e.destruct()} str(",") nl);240						}241						DestructArrayPart::DestructRest(d) => {242							p!(out, {d} str(",") nl);243						}244					}245				}246				p!(out, <i str("]"));247			}248			Self::DestructObject(o) => {249				p!(out, str("{") >i nl);250				for item in o.destruct_object_fields() {251					p!(out, { item.field() });252					if let Some(des) = item.destruct() {253						p!(out, str(": ") {des});254					}255					if let Some(def) = item.expr() {256						p!(out, str(" = ") {def});257					}258					p!(out, str(",") nl);259				}260				if let Some(rest) = o.destruct_rest() {261					p!(out, {rest} nl);262				}263				p!(out, <i str("}"));264			}265		}266	}267}268269impl Printable for FieldName {270	fn print(&self, out: &mut PrintItems) {271		match self {272			Self::FieldNameFixed(f) => {273				if let Some(id) = f.id() {274					p!(out, { id });275				} else if let Some(str) = f.text() {276					p!(out, { str });277				} else {278					p!(out, str("/*missing FieldName*/"));279				}280			}281			Self::FieldNameDynamic(d) => {282				p!(out, str("[") {d.expr()} str("]"));283			}284		}285	}286}287288impl Printable for Visibility {289	fn print(&self, out: &mut PrintItems) {290		p!(out, string(self.to_string()));291	}292}293294impl Printable for ObjLocal {295	fn print(&self, out: &mut PrintItems) {296		p!(out, str("local ") {self.bind()});297	}298}299300impl Printable for Assertion {301	fn print(&self, out: &mut PrintItems) {302		p!(out, str("assert ") {self.condition()});303		if self.colon_token().is_some() || self.message().is_some() {304			p!(out, str(": ") {self.message()});305		}306	}307}308309impl Printable for ParamsDesc {310	fn print(&self, out: &mut PrintItems) {311		p!(out, str("(") >i nl);312		for param in self.params() {313			p!(out, { param.destruct() });314			if param.assign_token().is_some() || param.expr().is_some() {315				p!(out, str(" = ") {param.expr()});316			}317			p!(out, str(",") nl);318		}319		p!(out, <i str(")"));320	}321}322impl Printable for ArgsDesc {323	fn print(&self, out: &mut PrintItems) {324		let start = LineNumber::new("args start line");325		let end = LineNumber::new("args end line");326		let multi_line = Rc::new(move |condition_context: &mut ConditionResolverContext| {327			is_multiple_lines(condition_context, start, end)328		});329330		let (children, end_comments) = children_between::<Arg>(331			self.syntax().clone(),332			self.l_paren_token().map(Into::into).as_ref(),333			self.r_paren_token().map(Into::into).as_ref(),334			None,335		);336337		fn gen_args(children: Vec<Child<Arg>>, multi_line: ConditionResolver) -> PrintItems {338			let mut _out = PrintItems::new();339			let out = &mut _out;340341			let mut args = children.into_iter().peekable();342			while let Some(ele) = args.next() {343				if ele.should_start_with_newline {344					p!(out, nl);345				}346				format_comments(&ele.before_trivia, CommentLocation::AboveItem, out);347				let arg = ele.value;348				if arg.name().is_some() || arg.assign_token().is_some() {349					p!(out, {arg.name()} str(" = "));350				}351				p!(out, { arg.expr() });352				let has_more = args.peek().is_some();353				if has_more {354					p!(out, str(","));355				} else {356					p!(out, if("trailing comma", multi_line, str(",")));357				}358				format_comments(&ele.inline_trivia, CommentLocation::ItemInline, out);359				if has_more {360					p!(out, if_else("arg separator", multi_line, nl)(sonl));361				}362			}363			_out364		}365366		let args_items = new_line_group(gen_args(children, multi_line.clone())).into_rc_path();367		let args_indented = with_indent(pi!(@i; nl items(args_items.into())));368369		p!(out, str("(") info(start));370		p!(out, if_else("args body", multi_line, items(args_indented) nl)(items(args_items.into())));371		if end_comments.should_start_with_newline {372			p!(out, nl);373		}374		format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);375		p!(out, str(")") info(end));376	}377}378impl Printable for SliceDesc {379	fn print(&self, out: &mut PrintItems) {380		p!(out, str("["));381		if self.from().is_some() {382			p!(out, { self.from() });383		}384		p!(out, str(":"));385		if self.end().is_some() {386			p!(out, { self.end().map(|e| e.expr()) });387		}388		// Keep only one : in case if we don't need step389		if self.step().is_some() {390			p!(out, str(":") {self.step().map(|e|e.expr())});391		}392		p!(out, str("]"));393	}394}395396impl Printable for Member {397	fn print(&self, out: &mut PrintItems) {398		match self {399			Self::MemberBindStmt(b) => {400				p!(out, { b.obj_local() });401			}402			Self::MemberAssertStmt(ass) => {403				p!(out, { ass.assertion() });404			}405			Self::MemberFieldNormal(n) => {406				p!(out, {n.field_name()} if(n.plus_token().is_some())({n.plus_token()}) {n.visibility()} str(" ") {n.expr()});407			}408			Self::MemberFieldMethod(m) => {409				p!(out, {m.field_name()} {m.params_desc()} {m.visibility()} str(" ") {m.expr()});410			}411		}412	}413}414415impl Printable for ObjBody {416	fn print(&self, out: &mut PrintItems) {417		match self {418			Self::ObjBodyComp(l) => {419				let (children, mut end_comments) = children_between::<Member>(420					l.syntax().clone(),421					l.l_brace_token().map(Into::into).as_ref(),422					Some(423						&(l.comp_specs()424							.next()425							.expect("at least one spec is defined")426							.syntax()427							.clone())428						.into(),429					),430					None,431				);432				let trailing_for_comp = end_comments.extract_trailing();433				p!(out, str("{") >i nl);434				for mem in children {435					if mem.should_start_with_newline {436						p!(out, nl);437					}438					format_comments(&mem.before_trivia, CommentLocation::AboveItem, out);439					p!(out, {mem.value} str(","));440					format_comments(&mem.inline_trivia, CommentLocation::ItemInline, out);441					p!(out, nl);442				}443444				if end_comments.should_start_with_newline {445					p!(out, nl);446				}447				format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);448449				let (compspecs, end_comments) = children_between::<CompSpec>(450					l.syntax().clone(),451					l.member_comps()452						.last()453						.map(|m| m.syntax().clone())454						.map(Into::into)455						.or_else(|| l.l_brace_token().map(Into::into))456						.as_ref(),457					l.r_brace_token().map(Into::into).as_ref(),458					Some(trailing_for_comp),459				);460				for mem in compspecs {461					if mem.should_start_with_newline {462						p!(out, nl);463					}464					format_comments(&mem.before_trivia, CommentLocation::AboveItem, out);465					p!(out, { mem.value });466					format_comments(&mem.inline_trivia, CommentLocation::ItemInline, out);467				}468				if end_comments.should_start_with_newline {469					p!(out, nl);470				}471				format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);472473				p!(out, nl <i str("}"));474			}475			Self::ObjBodyMemberList(l) => {476				let (children, end_comments) = children_between::<Member>(477					l.syntax().clone(),478					l.l_brace_token().map(Into::into).as_ref(),479					l.r_brace_token().map(Into::into).as_ref(),480					None,481				);482				if children.is_empty() && end_comments.is_empty() {483					p!(out, str("{ }"));484					return;485				}486487				let source_is_multiline = children.iter().any(|c| c.triggers_multiline)488					|| end_comments.should_start_with_newline;489490				let start = LineNumber::new("obj start line");491				let end = LineNumber::new("obj end line");492				let multi_line: ConditionResolver = if source_is_multiline {493					true_resolver()494				} else {495					Rc::new(move |ctx: &mut ConditionResolverContext| {496						is_multiple_lines(ctx, start, end)497					})498				};499500				fn gen_members(501					children: Vec<Child<Member>>,502					multi_line: ConditionResolver,503				) -> PrintItems {504					let mut _out = PrintItems::new();505					let out = &mut _out;506					let mut members = children.into_iter().peekable();507					while let Some(mem) = members.next() {508						if mem.should_start_with_newline {509							p!(out, nl);510						}511						format_comments(&mem.before_trivia, CommentLocation::AboveItem, out);512						p!(out, { mem.value });513						let has_more = members.peek().is_some();514						if has_more {515							p!(out, str(","));516						} else {517							p!(out, if("trailing comma", multi_line, str(",")));518						}519						format_comments(&mem.inline_trivia, CommentLocation::ItemInline, out);520						p!(out, if_else("member separator", multi_line, nl)(sonl));521					}522					_out523				}524525				let members_items =526					new_line_group(gen_members(children, multi_line.clone())).into_rc_path();527528				let members = with_indent_eoi(multi_line, members_items.into(), end_comments);529530				p!(out, str("{") info(start));531				p!(out, items(members));532				p!(out, str("}") info(end));533			}534		}535	}536}537impl Printable for UnaryOperator {538	fn print(&self, out: &mut PrintItems) {539		p!(out, string(self.text().to_string()));540	}541}542impl Printable for BinaryOperator {543	fn print(&self, out: &mut PrintItems) {544		p!(out, string(self.text().to_string()));545	}546}547impl Printable for Bind {548	fn print(&self, out: &mut PrintItems) {549		match self {550			Self::BindDestruct(d) => {551				p!(out, {d.into()} str(" = ") {d.value()});552			}553			Self::BindFunction(f) => {554				p!(out, {f.name()} {f.params()} str(" = ") {f.value()});555			}556		}557	}558}559impl Printable for Literal {560	fn print(&self, out: &mut PrintItems) {561		p!(out, string(self.syntax().to_string()));562	}563}564impl Printable for ImportKind {565	fn print(&self, out: &mut PrintItems) {566		p!(out, string(self.syntax().to_string()));567	}568}569impl Printable for ForSpec {570	fn print(&self, out: &mut PrintItems) {571		p!(out, str("for ") {self.bind()} str(" in ") {self.expr()});572	}573}574impl Printable for IfSpec {575	fn print(&self, out: &mut PrintItems) {576		p!(out, str("if ") {self.expr()});577	}578}579impl Printable for CompSpec {580	fn print(&self, out: &mut PrintItems) {581		match self {582			Self::ForSpec(f) => f.print(out),583			Self::IfSpec(i) => i.print(out),584		}585	}586}587impl Printable for Expr {588	fn print(&self, out: &mut PrintItems) {589		let (stmts, _ending) = children_between::<Stmt>(590			self.syntax().clone(),591			None,592			self.expr_base()593				.as_ref()594				.map(ExprBase::syntax)595				.cloned()596				.map(Into::into)597				.as_ref(),598			None,599		);600		for stmt in stmts {601			p!(out, { stmt.value });602		}603		p!(out, { self.expr_base() });604		let (suffixes, _ending) = children_between::<Suffix>(605			self.syntax().clone(),606			self.expr_base()607				.as_ref()608				.map(ExprBase::syntax)609				.cloned()610				.map(Into::into)611				.as_ref(),612			None,613			None,614		);615		for suffix in suffixes {616			p!(out, { suffix.value });617		}618	}619}620impl Printable for Suffix {621	fn print(&self, out: &mut PrintItems) {622		match self {623			Self::SuffixIndex(i) => {624				if i.question_mark_token().is_some() {625					p!(out, str("?"));626				}627				p!(out, str(".") {i.index()});628			}629			Self::SuffixIndexExpr(e) => {630				if e.question_mark_token().is_some() {631					p!(out, str(".?"));632				}633				p!(out, str("[") {e.index()} str("]"));634			}635			Self::SuffixSlice(d) => {636				p!(out, { d.slice_desc() });637			}638			Self::SuffixApply(a) => {639				p!(out, { a.args_desc() });640			}641		}642	}643}644impl Printable for Stmt {645	fn print(&self, out: &mut PrintItems) {646		match self {647			Self::StmtLocal(l) => {648				let (binds, end_comments) = children_between::<Bind>(649					l.syntax().clone(),650					l.local_kw_token().map(Into::into).as_ref(),651					l.semi_token().map(Into::into).as_ref(),652					None,653				);654				if binds.len() == 1 {655					let bind = &binds[0];656					format_comments(&bind.before_trivia, CommentLocation::AboveItem, out);657					p!(out, str("local ") {bind.value});658				// TODO: keep end_comments, child.inline_trivia somehow, force multiple locals formatting in case of presence?659				} else {660					p!(out,str("local") >i nl);661					for bind in binds {662						if bind.should_start_with_newline {663							p!(out, nl);664						}665						format_comments(&bind.before_trivia, CommentLocation::AboveItem, out);666						p!(out, {bind.value} str(","));667						format_comments(&bind.inline_trivia, CommentLocation::ItemInline, out);668						p!(out, nl);669					}670					if end_comments.should_start_with_newline {671						p!(out, nl);672					}673					format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);674					p!(out,<i);675				}676				p!(out,str(";") nl);677			}678			Self::StmtAssert(a) => {679				p!(out, {a.assertion()} str(";") nl);680			}681		}682	}683}684impl Printable for ExprBase {685	fn print(&self, out: &mut PrintItems) {686		match self {687			Self::ExprBinary(b) => {688				p!(out, {b.lhs()} str(" ") {b.binary_operator()} str(" ") {b.rhs()});689			}690			Self::ExprUnary(u) => p!(out, {u.unary_operator()} {u.rhs()}),691			// Self::ExprSlice(s) => {692			// 	p!(new: {s.expr()} {s.slice_desc()})693			// }694			// Self::ExprIndex(i) => {695			// 	p!(new: {i.expr()} str(".") {i.index()})696			// }697			// Self::ExprIndexExpr(i) => p!(new: {i.base()} str("[") {i.index()} str("]")),698			// Self::ExprApply(a) => {699			// 	let mut pi = p!(new: {a.expr()} {a.args_desc()});700			// 	if a.tailstrict_kw_token().is_some() {701			// 		p!(out,str(" tailstrict"));702			// 	}703			// 	pi704			// }705			Self::ExprObjExtend(ex) => {706				p!(out, {ex.lhs_work()} str(" ") {ex.rhs_work()});707			}708			Self::ExprParened(p) => {709				p!(out, str("(") {p.expr()} str(")"));710			}711			Self::ExprString(s) => p!(out, { s.text() }),712			Self::ExprNumber(n) => p!(out, { n.number() }),713			Self::ExprArray(a) => {714				p!(out, str("[") >i nl);715				for el in a.exprs() {716					p!(out, {el} str(",") nl);717				}718				p!(out, <i str("]"));719			}720			Self::ExprObject(obj) => {721				p!(out, { obj.obj_body() });722			}723			Self::ExprArrayComp(arr) => {724				p!(out, str("[") {arr.expr()});725				for spec in arr.comp_specs() {726					p!(out, str(" ") {spec});727				}728				p!(out, str("]"));729			}730			Self::ExprImport(v) => {731				p!(out, {v.import_kind()} str(" ") {v.text()});732			}733			Self::ExprVar(n) => p!(out, { n.name() }),734			// Self::ExprLocal(l) => {735			// }736			Self::ExprIfThenElse(ite) => {737				p!(out, str("if ") {ite.cond()} str(" then ") {ite.then().map(|t| t.expr())});738				if ite.else_kw_token().is_some() || ite.else_().is_some() {739					p!(out, str(" else ") {ite.else_().map(|t| t.expr())});740				}741			}742			Self::ExprFunction(f) => p!(out, str("function") {f.params_desc()} nl {f.expr()}),743			// Self::ExprAssert(a) => p!(new: {a.assertion()} str("; ") {a.expr()}),744			Self::ExprError(e) => p!(out, str("error ") {e.expr()}),745			Self::ExprLiteral(l) => {746				p!(out, { l.literal() });747			}748		}749	}750}751752impl Printable for SourceFile {753	fn print(&self, out: &mut PrintItems) {754		let before = trivia_before(755			self.syntax().clone(),756			self.expr()757				.map(|e| e.syntax().clone())758				.map(Into::into)759				.as_ref(),760		);761		let after = trivia_after(762			self.syntax().clone(),763			self.expr()764				.map(|e| e.syntax().clone())765				.map(Into::into)766				.as_ref(),767		);768		format_comments(&before, CommentLocation::AboveItem, out);769		p!(out, {self.expr()} nl);770		format_comments(&after, CommentLocation::EndOfItems, out);771	}772}773774pub struct FormatOptions {775	// 0 for hard tabs776	pub indent: u8,777}778pub fn format(input: &str, opts: &FormatOptions) -> Result<String, SnippetBuilder> {779	let (parsed, errors) = jrsonnet_rowan_parser::parse(input);780	if !errors.is_empty() {781		let mut builder = hi_doc::SnippetBuilder::new(input);782		for error in errors {783			builder784				.error(hi_doc::Text::fragment(785					format!("{:?}", error.error),786					Formatting::default(),787				))788				.range(789					error.range.start().into()790						..=(usize::from(error.range.end()) - 1).max(error.range.start().into()),791				)792				.build();793		}794		// let snippet = builder.build();795		return Err(builder);796		// It is possible to recover from this failure, but the output may be broken, as formatter is free to skip797		// ERROR rowan nodes.798		// Recovery needs to be enabled for LSP, though.799	}800	Ok(dprint_core::formatting::format(801		|| {802			let mut out = PrintItems::new();803			parsed.print(&mut out);804			out805		},806		PrintOptions {807			indent_width: if opts.indent == 0 {808				// Reasonable max length for both 2 and 4 space sized tabs.809				3810			} else {811				opts.indent812			},813			max_width: 100,814			use_tabs: opts.indent == 0,815			new_line_text: "\n",816		},817	))818}