git.delta.rocks / jrsonnet / refs/commits / 00fbb919e422

difftreelog

fix various parsing fixes

Yaroslav Bolyukin2024-03-17parent: #bfcd43b.patch.diff
in: master

4 files changed

modifiedcmds/jrsonnet-fmt/src/main.rsdiffbeforeafterboth
--- a/cmds/jrsonnet-fmt/src/main.rs
+++ b/cmds/jrsonnet-fmt/src/main.rs
@@ -4,14 +4,18 @@
 	io::{self, Write},
 	path::PathBuf,
 	process,
+	rc::Rc,
 };
 
 use children::{children_between, trivia_before};
 use clap::Parser;
-use dprint_core::formatting::{PrintItems, PrintOptions};
+use dprint_core::formatting::{
+	condition_helpers::is_multiple_lines, condition_resolvers::true_resolver,
+	ConditionResolverContext, LineNumber, PrintItems, PrintOptions,
+};
 use jrsonnet_rowan_parser::{
 	nodes::{
-		ArgsDesc, Assertion, BinaryOperator, Bind, CompSpec, Destruct, DestructArrayPart,
+		Arg, ArgsDesc, Assertion, BinaryOperator, Bind, CompSpec, Destruct, DestructArrayPart,
 		DestructRest, Expr, ExprBase, FieldName, ForSpec, IfSpec, ImportKind, Literal, Member,
 		Name, Number, ObjBody, ObjLocal, ParamsDesc, SliceDesc, SourceFile, Stmt, Suffix, Text,
 		UnaryOperator, Visibility,
@@ -64,6 +68,55 @@
 		$o.push_signal(dprint_core::formatting::Signal::FinishIndent);
 		pi!(@s; $o: $($t)*);
 	}};
+	(@s; $o:ident: info($v:expr) $($t:tt)*) => {{
+		$o.push_info($v);
+		pi!(@s; $o: $($t)*);
+	}};
+	(@s; $o:ident: if($s:literal, $cond:expr, $($i:tt)*) $($t:tt)*) => {{
+		$o.push_condition(dprint_core::formatting::conditions::if_true(
+			$s,
+			$cond.clone(),
+			{
+				let mut o = PrintItems::new();
+				p!(o, $($i)*);
+				o
+			},
+		));
+		pi!(@s; $o: $($t)*);
+	}};
+	(@s; $o:ident: if_else($s:literal, $cond:expr, $($i:tt)*)($($e:tt)+) $($t:tt)*) => {{
+		$o.push_condition(dprint_core::formatting::conditions::if_true_or(
+			$s,
+			$cond.clone(),
+			{
+				let mut o = PrintItems::new();
+				p!(o, $($i)*);
+				o
+			},
+			{
+				let mut o = PrintItems::new();
+				p!(o, $($e)*);
+				o
+			},
+		));
+		pi!(@s; $o: $($t)*);
+	}};
+	(@s; $o:ident: if_not($s:literal, $cond:expr, $($e:tt)*) $($t:tt)*) => {{
+		$o.push_condition(dprint_core::formatting::conditions::if_true_or(
+			$s,
+			$cond.clone(),
+			{
+				let o = PrintItems::new();
+				o
+			},
+			{
+				let mut o = PrintItems::new();
+				p!(o, $($e)*);
+				o
+			},
+		));
+		pi!(@s; $o: $($t)*);
+	}};
 	(@s; $o:ident: {$expr:expr} $($t:tt)*) => {{
 		$expr.print($o);
 		pi!(@s; $o: $($t)*);
@@ -244,14 +297,38 @@
 }
 impl Printable for ArgsDesc {
 	fn print(&self, out: &mut PrintItems) {
-		p!(out, str("(") >i nl);
-		for arg in self.args() {
+		let start = LineNumber::new("start");
+		let end = LineNumber::new("end");
+		let multi_line = Rc::new(move |condition_context: &mut ConditionResolverContext| {
+			is_multiple_lines(condition_context, start, end).map(|v| !v)
+		});
+		p!(out, str("(") info(start) if("start args", multi_line, >i nl));
+		let (children, end_comments) = children_between::<Arg>(
+			self.syntax().clone(),
+			self.l_paren_token().map(Into::into).as_ref(),
+			self.r_paren_token().map(Into::into).as_ref(),
+			None,
+		);
+		let mut args = children.into_iter().peekable();
+		while let Some(ele) = args.next() {
+			if ele.should_start_with_newline {
+				p!(out, nl);
+			}
+			format_comments(&ele.before_trivia, CommentLocation::AboveItem, out);
+			let arg = ele.value;
 			if arg.name().is_some() || arg.assign_token().is_some() {
 				p!(out, {arg.name()} str(" = "));
 			}
-			p!(out, {arg.expr()} str(",") nl)
+			let comma_between = if args.peek().is_some() {
+				true_resolver()
+			} else {
+				multi_line.clone()
+			};
+			p!(out, {arg.expr()} if("arg comma", comma_between, str(",") if_not("between args", multi_line, str(" "))));
+			format_comments(&ele.inline_trivia, CommentLocation::ItemInline, out);
+			p!(out, if("between args", multi_line, nl));
 		}
-		p!(out, <i str(")"));
+		p!(out, if("end args", multi_line, <i info(end)) str(")"));
 	}
 }
 impl Printable for SliceDesc {
@@ -513,6 +590,7 @@
 						format_comments(&bind.before_trivia, CommentLocation::AboveItem, out);
 						p!(out, {bind.value} str(","));
 						format_comments(&bind.inline_trivia, CommentLocation::ItemInline, out);
+						p!(out, nl)
 					}
 					if end_comments.should_start_with_newline {
 						p!(out, nl)
modifiedcmds/jrsonnet-fmt/src/tests.rsdiffbeforeafterboth
--- a/cmds/jrsonnet-fmt/src/tests.rs
+++ b/cmds/jrsonnet-fmt/src/tests.rs
@@ -21,55 +21,6 @@
 	)
 }
 
-macro_rules! assert_formatted {
-	($input:literal, $output:literal) => {
-		let formatted = reformat(indoc!($input));
-		let mut expected = indoc!($output).to_owned();
-		expected.push('\n');
-		if formatted != expected {
-			panic!(
-				"bad formatting, expected\n```\n{formatted}\n```\nto be equal to\n```\n{expected}\n```",
-			)
-		}
-	};
-}
-
-#[test]
-fn padding_stripped_for_multiline_comment() {
-	assert_formatted!(
-		"{
-            /*
-                Hello
-                    World
-            */
-            _: null,
-        }",
-		"{
-          /*
-          Hello
-              World
-          */
-          _: null,
-        }"
-	);
-}
-
-#[test]
-fn last_comment_respects_spacing_with_inline_comment_above() {
-	assert_formatted!(
-		"{
-			a: '', // Inline
-
-			// Comment
-        }",
-		"{
-		  a: '', // Inline
-
-		  // Comment
-		}"
-	);
-}
-
 #[test]
 fn complex_comments_snapshot() {
 	insta::assert_display_snapshot!(reformat(indoc!(
modifiedcrates/jrsonnet-rowan-parser/src/parser.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/parser.rs
+++ b/crates/jrsonnet-rowan-parser/src/parser.rs
@@ -450,7 +450,7 @@
 			p.bump();
 			break;
 		}
-		if p.at_ts(COMPSPEC) {
+		if p.at_ts(TS![for]) {
 			if elems == 0 {
 				let m = p.start();
 				m.complete_missing(p, ExpectedSyntax::Named("field definition"));
@@ -612,7 +612,7 @@
 			p.bump();
 			break;
 		}
-		if elems != 0 && p.at_ts(COMPSPEC) {
+		if elems != 0 && p.at_ts(TS![for]) {
 			while p.at_ts(COMPSPEC) {
 				compspecs.push(compspec(p));
 			}
modifiedcrates/jrsonnet-rowan-parser/src/tests.rsdiffbeforeafterboth
before · crates/jrsonnet-rowan-parser/src/tests.rs
1#![cfg(test)]23use miette::{4	Diagnostic, GraphicalReportHandler, GraphicalTheme, LabeledSpan, ThemeCharacters, ThemeStyles,5};6use thiserror::Error;78use crate::{parse, AstNode};910#[derive(Debug, Error)]11#[error("syntax error")]12struct MyDiagnostic {13	code: String,14	spans: Vec<LabeledSpan>,15}16impl Diagnostic for MyDiagnostic {17	fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {18		None19	}2021	fn severity(&self) -> Option<miette::Severity> {22		None23	}2425	fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {26		None27	}2829	fn url<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {30		None31	}3233	fn source_code(&self) -> Option<&dyn miette::SourceCode> {34		Some(&self.code)35	}3637	fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {38		Some(Box::new(self.spans.clone().into_iter()))39	}4041	fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {42		None43	}44}4546fn process(text: &str) -> String {47	use std::fmt::Write;48	let mut out = String::new();49	let (node, errors) = parse(text);50	write!(out, "{:#?}", node.syntax()).unwrap();51	if !errors.is_empty() && !text.is_empty() {52		writeln!(out, "===").unwrap();53		for err in &errors {54			writeln!(out, "{:?}", err).unwrap();55		}56		let mut code = text.to_string();5758		// Prettier errors at EOF position59		if code.ends_with('\n') {60			code.truncate(code.len() - 1);61			code += " ";62		}63		code += " ";6465		let diag = MyDiagnostic {66			code,67			spans: errors.into_iter().map(|e| e.into()).collect(),68		};6970		let handler = GraphicalReportHandler::new_themed(GraphicalTheme {71			characters: ThemeCharacters::ascii(),72			styles: ThemeStyles::none(),73		});7475		writeln!(out, "===").unwrap();76		handler77			.render_report(&mut out, &diag)78			.expect("fmt error?..");79	}80	out.split('\n')81		.map(|s| s.trim_end().to_string())82		.collect::<Vec<String>>()83		.join("\n")84		.trim_end()85		.to_string()86}87macro_rules! mk_test {88		($($name:ident => $test:expr)+) => {$(89			#[test]90			fn $name() {91				let src = indoc::indoc!($test);92				let result = process(&src);93				insta::assert_snapshot!(stringify!($name), result, src);9495			}96		)+};97	}98mk_test!(99	empty => r#" "#100	function => r#"101		function(a, b = 1) a + b102	"#103	function_error_no_value => r#"104		function(a, b = ) a + b105	"#106	function_error_rparen => r#"107		function(a, b108	"#109	function_error_body => r#"110		function(a, b)111	"#112	local_novalue => r#"113		local a =114	"#115	local_no_value_recovery => r#"116		local a =117		local b = 3;118		1119	"#120121122	no_rhs => r#"123		a +124	"#125	no_lhs => r#"126		+ 2127	"#128	no_operator => "129		2 2130	"131132	named_before_positional => "133		a(1, 2, b=4, 3, 5, k = 12, 6)134	"135136	wrong_field_end => "137		{138			a: 1;139			b: 2;140		}141	"142143144	plain_call => "145		std.substr(a, 0, std.length(b)) == b146	"147148	destruct => "149		local [a, b, c] = arr;150		local [a, ...] = arr_rest;151		local [..., a] = rest_arr;152		local [...] = rest_in_arr;153		local [a, ...n] = arr_rest_n;154		local [...n, a] = rest_arr_n;155		local [...n] = rest_in_arr_n;156157		local {a, b, c} = obj;158		local {a, b, c, ...} = obj_rest;159		local {a, b, c, ...n} = obj_rest_n;160161		null162	"163164	str_block_missing_indent => "165		|||166	"167	str_block_missing_termination => "168		|||169			hello170	"171	str_block_missing_newline => "172		|||hello173	"174	str_block_missing_indent_text => "175		|||176		hello177	"178179	unexpected_destruct => "180		local * = 1;181		a182	"183	arr_compspec => r#"184		[a for a in [1, 2, 3]]185	"#186	arr_compspec_comma => "187		[a, for a in [1, 2, 3]]188	"189	arr_compspec_no_elems => "190		[for a in [1, 2, 3]]191	"192	arr_compspec_incompatible_with_multiple_elems => r#"193		[a for a in [1, 2, 3], b]194	"#195	arr_compspec_incompatible_with_multiple_elems_w => r#"196		[a, b, for a in [1, 2, 3], c]197	"#198199	obj_compspec => r#"200		{a:1 for a in [1, 2, 3]}201	"#202	obj_compspec_comma => "203		{a:1, for a in [1, 2, 3]}204	"205	obj_compspec_no_elems => "206		{for a in [1, 2, 3]}207	"208	obj_compspec_incompatible_with_multiple_elems => r#"209		{a:1 for a in [1, 2, 3], b:1}210	"#211	obj_compspec_incompatible_with_multiple_elems_w => r#"212		{a:1, b:1, for a in [1, 2, 3], c:1}213	"#214215	obj_compspec_incompatible_with_asserts => r#"216		{assert 1, a: 1 for a in [1,2,3]}217	"#218219	local_method => r#"220		local221			a(x) = x,222			a = function(x) x,223		; c224	"#225	obj_method => r#"226		{227			a(x): x,228			a: function(x) x,229		}230	"#231232	continue_after_total_failure => r#"233		local intr = $intrinsic(test);234235		local a = 1, b = 2, c = a + b;236237		[c]238	"#239);240241#[test]242fn stdlib() {243	let src = include_str!("../../jrsonnet-stdlib/src/std.jsonnet");244	let result = process(src);245	insta::assert_snapshot!("stdlib", result, src);246}247#[test]248fn eval_simple() {249	let src = "local a = 1, b = 2; a + local c = 1; c";250	let (node, errors) = parse(src);251	assert!(errors.is_empty());252253	dbg!(node);254}
after · crates/jrsonnet-rowan-parser/src/tests.rs
1#![cfg(test)]23use miette::{4	Diagnostic, GraphicalReportHandler, GraphicalTheme, LabeledSpan, ThemeCharacters, ThemeStyles,5};6use thiserror::Error;78use crate::{parse, AstNode};910#[derive(Debug, Error)]11#[error("syntax error")]12struct MyDiagnostic {13	code: String,14	spans: Vec<LabeledSpan>,15}16impl Diagnostic for MyDiagnostic {17	fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {18		None19	}2021	fn severity(&self) -> Option<miette::Severity> {22		None23	}2425	fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {26		None27	}2829	fn url<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {30		None31	}3233	fn source_code(&self) -> Option<&dyn miette::SourceCode> {34		Some(&self.code)35	}3637	fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {38		Some(Box::new(self.spans.clone().into_iter()))39	}4041	fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {42		None43	}44}4546fn process(text: &str) -> String {47	use std::fmt::Write;48	let mut out = String::new();49	let (node, errors) = parse(text);50	write!(out, "{:#?}", node.syntax()).unwrap();51	if !errors.is_empty() && !text.is_empty() {52		writeln!(out, "===").unwrap();53		for err in &errors {54			writeln!(out, "{:?}", err).unwrap();55		}56		let mut code = text.to_string();5758		// Prettier errors at EOF position59		if code.ends_with('\n') {60			code.truncate(code.len() - 1);61			code += " ";62		}63		code += " ";6465		let diag = MyDiagnostic {66			code,67			spans: errors.into_iter().map(|e| e.into()).collect(),68		};6970		let handler = GraphicalReportHandler::new_themed(GraphicalTheme {71			characters: ThemeCharacters::ascii(),72			styles: ThemeStyles::none(),73		});7475		writeln!(out, "===").unwrap();76		handler77			.render_report(&mut out, &diag)78			.expect("fmt error?..");79	}80	out.split('\n')81		.map(|s| s.trim_end().to_string())82		.collect::<Vec<String>>()83		.join("\n")84		.trim_end()85		.to_string()86}87macro_rules! mk_test {88		($($name:ident => $test:expr)+) => {$(89			#[test]90			fn $name() {91				let src = indoc::indoc!($test);92				let result = process(&src);93				insta::assert_snapshot!(stringify!($name), result, src);9495			}96		)+};97	}98mk_test!(99	empty => r#" "#100	function => r#"101		function(a, b = 1) a + b102	"#103	function_error_no_value => r#"104		function(a, b = ) a + b105	"#106	function_error_rparen => r#"107		function(a, b108	"#109	function_error_body => r#"110		function(a, b)111	"#112	local_novalue => r#"113		local a =114	"#115	local_no_value_recovery => r#"116		local a =117		local b = 3;118		1119	"#120121122	no_rhs => r#"123		a +124	"#125	no_lhs => r#"126		+ 2127	"#128	no_operator => "129		2 2130	"131132	named_before_positional => "133		a(1, 2, b=4, 3, 5, k = 12, 6)134	"135136	wrong_field_end => "137		{138			a: 1;139			b: 2;140		}141	"142143144	plain_call => "145		std.substr(a, 0, std.length(b)) == b146	"147148	destruct => "149		local [a, b, c] = arr;150		local [a, ...] = arr_rest;151		local [..., a] = rest_arr;152		local [...] = rest_in_arr;153		local [a, ...n] = arr_rest_n;154		local [...n, a] = rest_arr_n;155		local [...n] = rest_in_arr_n;156157		local {a, b, c} = obj;158		local {a, b, c, ...} = obj_rest;159		local {a, b, c, ...n} = obj_rest_n;160161		null162	"163164	str_block_missing_indent => "165		|||166	"167	str_block_missing_termination => "168		|||169			hello170	"171	str_block_missing_newline => "172		|||hello173	"174	str_block_missing_indent_text => "175		|||176		hello177	"178179	unexpected_destruct => "180		local * = 1;181		a182	"183	arr_compspec => r#"184		[a for a in [1, 2, 3]]185	"#186	arr_compspec_comma => "187		[a, for a in [1, 2, 3]]188	"189	arr_compspec_no_elems => "190		[for a in [1, 2, 3]]191	"192	arr_compspec_incompatible_with_multiple_elems => r#"193		[a for a in [1, 2, 3], b]194	"#195	arr_compspec_incompatible_with_multiple_elems_w => r#"196		[a, b, for a in [1, 2, 3], c]197	"#198199	obj_compspec => r#"200		{a:1 for a in [1, 2, 3]}201	"#202	obj_compspec_comma => "203		{a:1, for a in [1, 2, 3]}204	"205	obj_compspec_no_elems => "206		{for a in [1, 2, 3]}207	"208	obj_compspec_incompatible_with_multiple_elems => r#"209		{a:1 for a in [1, 2, 3], b:1}210	"#211	obj_compspec_incompatible_with_multiple_elems_w => r#"212		{a:1, b:1, for a in [1, 2, 3], c:1}213	"#214215	obj_compspec_incompatible_with_asserts => r#"216		{assert 1, a: 1 for a in [1,2,3]}217	"#218219	local_method => r#"220		local221			a(x) = x,222			a = function(x) x,223		; c224	"#225	obj_method => r#"226		{227			a(x): x,228			a: function(x) x,229		}230	"#231232	continue_after_total_failure => r#"233		local intr = $intrinsic(test);234235		local a = 1, b = 2, c = a + b;236237		[c]238	"#239);240241#[test]242fn stdlib() {243	let src = include_str!("../../jrsonnet-stdlib/src/std.jsonnet");244	let result = process(src);245	insta::assert_snapshot!("stdlib", result, src);246}247#[test]248fn eval_simple() {249	let src = "local a = 1, b = 2; a + local c = 1; c";250	let (node, _errors) = parse(src);251252	dbg!(node);253}