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

difftreelog

source

cmds/jrsonnet-fmt/src/children.rs6.1 KiBsourcehistory
1// TODO: Return errors as trivia23use std::{fmt::Debug, mem};45use jrsonnet_rowan_parser::{6	nodes::{CustomError, Trivia, TriviaKind},7	AstNode, AstToken, SyntaxElement, SyntaxNode, TS,8};910pub type ChildTrivia = Vec<Result<Trivia, String>>;1112/// Node should have no non-trivia tokens before element13pub fn trivia_before(node: SyntaxNode, end: Option<&SyntaxElement>) -> ChildTrivia {14	let mut out = Vec::new();15	for item in node.children_with_tokens() {16		if Some(&item) == end {17			break;18		}1920		if let Some(trivia) = item.as_token().cloned().and_then(Trivia::cast) {21			out.push(Ok(trivia));22		} else if CustomError::can_cast(item.kind()) {23			out.push(Err(item.to_string()));24		} else if end.is_none() {25			break;26		} else {27			assert!(28				TS![, ;].contains(item.kind()),29				"silently eaten token: {:?}",30				item.kind()31			);32		}33	}34	out35}36/// Node should have no non-trivia tokens after element37pub fn trivia_after(node: SyntaxNode, start: Option<&SyntaxElement>) -> ChildTrivia {38	if start.is_none() {39		return Vec::new();40	}41	let mut iter = node.children_with_tokens().peekable();42	while iter.peek() != start {43		iter.next();44	}45	iter.next();46	let mut out = Vec::new();47	for item in iter {48		if let Some(trivia) = item.as_token().cloned().and_then(Trivia::cast) {49			out.push(Ok(trivia));50		} else if CustomError::can_cast(item.kind()) {51			out.push(Err(item.to_string()));52		} else {53			assert!(54				TS![, ;].contains(item.kind()),55				"silently eaten token: {:?}",56				item.kind()57			);58		}59	}60	out61}6263pub fn children_between<T: AstNode + Debug>(64	node: SyntaxNode,65	start: Option<&SyntaxElement>,66	end: Option<&SyntaxElement>,67	trailing: Option<ChildTrivia>,68) -> (Vec<Child<T>>, EndingComments) {69	let mut iter = node.children_with_tokens().peekable();70	if start.is_some() {71		while iter.peek() != start {72			iter.next();73		}74		iter.next();75	}76	children(77		iter.take_while(|i| Some(i) != end),78		start.is_none() && end.is_none(),79		trailing,80	)81}8283pub fn should_start_with_newline(prev_inline: Option<&ChildTrivia>, tt: &ChildTrivia) -> bool {84	count_newlines_before(tt)85		+ prev_inline86			.map(count_newlines_after)87			.unwrap_or_default()8889		// First for previous item end, second for current item90		>= 291}9293fn count_newlines_before(tt: &ChildTrivia) -> usize {94	let mut nl_count = 0;95	for t in tt {96		match t {97			Ok(t) => match t.kind() {98				TriviaKind::Whitespace => {99					nl_count += t.text().bytes().filter(|b| *b == b'\n').count();100				}101				_ => break,102			},103			Err(_) => {104				nl_count += 1;105			}106		}107	}108	nl_count109}110fn count_newlines_after(tt: &ChildTrivia) -> usize {111	let mut nl_count = 0;112	for t in tt.iter().rev() {113		match t {114			Ok(t) => match t.kind() {115				TriviaKind::Whitespace => {116					nl_count += t.text().bytes().filter(|b| *b == b'\n').count();117				}118				TriviaKind::SingleLineHashComment | TriviaKind::SingleLineSlashComment => {119					nl_count += 1;120					break;121				}122				_ => {}123			},124			Err(_) => nl_count += 1,125		}126	}127	nl_count128}129130pub fn children<T: AstNode + Debug>(131	items: impl Iterator<Item = SyntaxElement>,132	loose: bool,133	mut trailing: Option<ChildTrivia>,134) -> (Vec<Child<T>>, EndingComments) {135	let mut out = Vec::new();136	let mut current_child = None::<Child<T>>;137	let mut next = ChildTrivia::new();138	// Previous element ended, do not add more inline comments139	let mut started_next = false;140	let mut had_some = false;141142	for item in items {143		if let Some(value) = item.as_node().cloned().and_then(T::cast) {144			let before_trivia = if let Some(trailing) = trailing.take() {145				assert!(next.is_empty());146				trailing147			} else {148				mem::take(&mut next)149			};150			let last_child = current_child.replace(Child {151				// First item should not start with newline152				should_start_with_newline: had_some153					&& should_start_with_newline(154						current_child.as_ref().map(|c| &c.inline_trivia),155						&before_trivia,156					),157				before_trivia,158				value,159				inline_trivia: Vec::new(),160			});161			if let Some(last_child) = last_child {162				out.push(last_child);163			}164			had_some = true;165			started_next = false;166		} else if let Some(trivia) = item.as_token().cloned().and_then(Trivia::cast) {167			let is_single_line_comment = trivia.kind() == TriviaKind::SingleLineHashComment168				|| trivia.kind() == TriviaKind::SingleLineSlashComment;169			if trailing.is_some() {170				// Someone have already parsed trivia for us171				continue;172			} else if started_next173				|| current_child.is_none()174				|| trivia.text().contains('\n') && !is_single_line_comment175			{176				next.push(Ok(trivia.clone()));177				started_next = true;178			} else {179				let cur = current_child.as_mut().expect("checked not none");180				cur.inline_trivia.push(Ok(trivia));181				if is_single_line_comment {182					started_next = true;183				}184			}185			had_some = true;186		} else if CustomError::can_cast(item.kind()) {187			next.push(Err(item.to_string()));188		} else if loose {189			if had_some {190				break;191			}192			started_next = true;193		} else {194			assert!(195				TS![, ;].contains(item.kind()),196				"silently eaten token: {:?}",197				item.kind()198			);199		}200	}201202	let ending_comments = EndingComments {203		should_start_with_newline: should_start_with_newline(204			current_child.as_ref().map(|c| &c.inline_trivia),205			&next,206		),207		trivia: next,208	};209210	if let Some(current_child) = current_child {211		out.push(current_child);212	}213214	(out, ending_comments)215}216217#[derive(Debug)]218pub struct Child<T> {219	/// If this child has two newlines above in source code, so it needs to have it in the output220	pub should_start_with_newline: bool,221	/// Comment before item, i.e222	///223	/// ```ignore224	/// // Comment225	/// item226	/// ```227	pub before_trivia: ChildTrivia,228	pub value: T,229	/// Comment after line, but located at same line230	///231	/// ```ignore232	/// item1, // Inline comment233	/// // Not inline comment234	/// item2,235	/// ```236	pub inline_trivia: ChildTrivia,237}238239pub struct EndingComments {240	/// If this child has two newlines above in source code, so it needs to have it in the output241	pub should_start_with_newline: bool,242	pub trivia: ChildTrivia,243}244impl EndingComments {245	pub fn is_empty(&self) -> bool {246		!self.should_start_with_newline && self.trivia.is_empty()247	}248	pub fn extract_trailing(&mut self) -> ChildTrivia {249		mem::take(&mut self.trivia)250	}251}