git.delta.rocks / jrsonnet / refs/commits / 9ebfdebec2e2

difftreelog

source

cmds/jrsonnet-fmt/src/children.rs6.9 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 trivia_between(64	node: SyntaxNode,65	start: Option<&SyntaxElement>,66	end: Option<&SyntaxElement>,67) -> EndingComments {68	let mut iter = node.children_with_tokens().peekable();69	while iter.peek() != start {70		iter.next();71	}72	iter.next();7374	let loose = start.is_none() || end.is_none();7576	let mut out = Vec::new();77	for item in iter.take_while(|i| Some(i) != end) {78		if let Some(trivia) = item.as_token().cloned().and_then(Trivia::cast) {79			out.push(Ok(trivia));80		} else if CustomError::can_cast(item.kind()) {81			out.push(Err(item.to_string()))82		} else if loose {83			break;84		} else {85			assert!(86				TS![, ;].contains(item.kind()),87				"silently eaten token: {:?}",88				item.kind()89			)90		}91	}92	EndingComments {93		should_start_with_newline: should_start_with_newline(None, &out),94		trivia: out,95	}96}9798pub fn children_between<T: AstNode + Debug>(99	node: SyntaxNode,100	start: Option<&SyntaxElement>,101	end: Option<&SyntaxElement>,102	trailing: Option<ChildTrivia>,103) -> (Vec<Child<T>>, EndingComments) {104	let mut iter = node.children_with_tokens().peekable();105	if start.is_some() {106		while iter.peek() != start {107			iter.next();108		}109		iter.next();110	}111	children(112		iter.take_while(|i| Some(i) != end),113		start.is_none() && end.is_none(),114		trailing,115	)116}117118pub fn should_start_with_newline(prev_inline: Option<&ChildTrivia>, tt: &ChildTrivia) -> bool {119	count_newlines_before(tt)120		+ prev_inline121			.map(count_newlines_after)122			.unwrap_or_default()123124		// First for previous item end, second for current item125		>= 2126}127128fn count_newlines_before(tt: &ChildTrivia) -> usize {129	let mut nl_count = 0;130	for t in tt {131		match t {132			Ok(t) => match t.kind() {133				TriviaKind::Whitespace => {134					nl_count += t.text().bytes().filter(|b| *b == b'\n').count();135				}136				_ => break,137			},138			Err(_) => {139				nl_count += 1;140			}141		}142	}143	nl_count144}145fn count_newlines_after(tt: &ChildTrivia) -> usize {146	let mut nl_count = 0;147	for t in tt.iter().rev() {148		match t {149			Ok(t) => match t.kind() {150				TriviaKind::Whitespace => {151					nl_count += t.text().bytes().filter(|b| *b == b'\n').count();152				}153				TriviaKind::SingleLineHashComment => {154					nl_count += 1;155					break;156				}157				TriviaKind::SingleLineSlashComment => {158					nl_count += 1;159					break;160				}161				_ => {}162			},163			Err(_) => nl_count += 1,164		}165	}166	nl_count167}168169pub fn children<T: AstNode + Debug>(170	items: impl Iterator<Item = SyntaxElement>,171	loose: bool,172	mut trailing: Option<ChildTrivia>,173) -> (Vec<Child<T>>, EndingComments) {174	let mut out = Vec::new();175	let mut current_child = None::<Child<T>>;176	let mut next = ChildTrivia::new();177	// Previous element ended, do not add more inline comments178	let mut started_next = false;179	let mut had_some = false;180181	for item in items {182		if let Some(value) = item.as_node().cloned().and_then(T::cast) {183			let before_trivia = if let Some(trailing) = trailing.take() {184				assert!(next.is_empty());185				trailing186			} else {187				mem::take(&mut next)188			};189			let last_child = current_child.replace(Child {190				// First item should not start with newline191				should_start_with_newline: had_some192					&& should_start_with_newline(193						current_child.as_ref().map(|c| &c.inline_trivia),194						&before_trivia,195					),196				before_trivia,197				value,198				inline_trivia: Vec::new(),199			});200			if let Some(last_child) = last_child {201				out.push(last_child)202			}203			had_some = true;204			started_next = false;205		} else if let Some(trivia) = item.as_token().cloned().and_then(Trivia::cast) {206			let is_single_line_comment = trivia.kind() == TriviaKind::SingleLineHashComment207				|| trivia.kind() == TriviaKind::SingleLineSlashComment;208			if trailing.is_some() {209				// Someone have already parsed trivia for us210				continue;211			} else if started_next212				|| current_child.is_none()213				|| trivia.text().contains('\n') && !is_single_line_comment214			{215				next.push(Ok(trivia.clone()));216				started_next = true;217			} else {218				let cur = current_child.as_mut().expect("checked not none");219				cur.inline_trivia.push(Ok(trivia));220				if is_single_line_comment {221					started_next = true;222				}223			}224			had_some = true;225		} else if CustomError::can_cast(item.kind()) {226			next.push(Err(item.to_string()))227		} else if loose {228			if had_some {229				break;230			}231			started_next = true;232		} else {233			assert!(234				TS![, ;].contains(item.kind()),235				"silently eaten token: {:?}",236				item.kind()237			)238		}239	}240241	let ending_comments = EndingComments {242		should_start_with_newline: should_start_with_newline(243			current_child.as_ref().map(|c| &c.inline_trivia),244			&next,245		),246		trivia: next,247	};248249	if let Some(current_child) = current_child {250		out.push(current_child);251	}252253	(out, ending_comments)254}255256#[derive(Debug)]257pub struct Child<T> {258	/// If this child has two newlines above in source code, so it needs to have it in the output259	pub should_start_with_newline: bool,260	/// Comment before item, i.e261	///262	/// ```ignore263	/// // Comment264	/// item265	/// ```266	pub before_trivia: ChildTrivia,267	pub value: T,268	/// Comment after line, but located at same line269	///270	/// ```ignore271	/// item1, // Inline comment272	/// // Not inline comment273	/// item2,274	/// ```275	pub inline_trivia: ChildTrivia,276}277278pub struct EndingComments {279	/// If this child has two newlines above in source code, so it needs to have it in the output280	pub should_start_with_newline: bool,281	pub trivia: ChildTrivia,282}283impl EndingComments {284	pub fn is_empty(&self) -> bool {285		!self.should_start_with_newline && self.trivia.is_empty()286	}287	pub fn extract_trailing(&mut self) -> ChildTrivia {288		mem::take(&mut self.trivia)289	}290}