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

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 => {119					nl_count += 1;120					break;121				}122				TriviaKind::SingleLineSlashComment => {123					nl_count += 1;124					break;125				}126				_ => {}127			},128			Err(_) => nl_count += 1,129		}130	}131	nl_count132}133134pub fn children<T: AstNode + Debug>(135	items: impl Iterator<Item = SyntaxElement>,136	loose: bool,137	mut trailing: Option<ChildTrivia>,138) -> (Vec<Child<T>>, EndingComments) {139	let mut out = Vec::new();140	let mut current_child = None::<Child<T>>;141	let mut next = ChildTrivia::new();142	// Previous element ended, do not add more inline comments143	let mut started_next = false;144	let mut had_some = false;145146	for item in items {147		if let Some(value) = item.as_node().cloned().and_then(T::cast) {148			let before_trivia = if let Some(trailing) = trailing.take() {149				assert!(next.is_empty());150				trailing151			} else {152				mem::take(&mut next)153			};154			let last_child = current_child.replace(Child {155				// First item should not start with newline156				should_start_with_newline: had_some157					&& should_start_with_newline(158						current_child.as_ref().map(|c| &c.inline_trivia),159						&before_trivia,160					),161				before_trivia,162				value,163				inline_trivia: Vec::new(),164			});165			if let Some(last_child) = last_child {166				out.push(last_child)167			}168			had_some = true;169			started_next = false;170		} else if let Some(trivia) = item.as_token().cloned().and_then(Trivia::cast) {171			let is_single_line_comment = trivia.kind() == TriviaKind::SingleLineHashComment172				|| trivia.kind() == TriviaKind::SingleLineSlashComment;173			if trailing.is_some() {174				// Someone have already parsed trivia for us175				continue;176			} else if started_next177				|| current_child.is_none()178				|| trivia.text().contains('\n') && !is_single_line_comment179			{180				next.push(Ok(trivia.clone()));181				started_next = true;182			} else {183				let cur = current_child.as_mut().expect("checked not none");184				cur.inline_trivia.push(Ok(trivia));185				if is_single_line_comment {186					started_next = true;187				}188			}189			had_some = true;190		} else if CustomError::can_cast(item.kind()) {191			next.push(Err(item.to_string()))192		} else if loose {193			if had_some {194				break;195			}196			started_next = true;197		} else {198			assert!(199				TS![, ;].contains(item.kind()),200				"silently eaten token: {:?}",201				item.kind()202			)203		}204	}205206	let ending_comments = EndingComments {207		should_start_with_newline: should_start_with_newline(208			current_child.as_ref().map(|c| &c.inline_trivia),209			&next,210		),211		trivia: next,212	};213214	if let Some(current_child) = current_child {215		out.push(current_child);216	}217218	(out, ending_comments)219}220221#[derive(Debug)]222pub struct Child<T> {223	/// If this child has two newlines above in source code, so it needs to have it in the output224	pub should_start_with_newline: bool,225	/// Comment before item, i.e226	///227	/// ```ignore228	/// // Comment229	/// item230	/// ```231	pub before_trivia: ChildTrivia,232	pub value: T,233	/// Comment after line, but located at same line234	///235	/// ```ignore236	/// item1, // Inline comment237	/// // Not inline comment238	/// item2,239	/// ```240	pub inline_trivia: ChildTrivia,241}242243pub struct EndingComments {244	/// If this child has two newlines above in source code, so it needs to have it in the output245	pub should_start_with_newline: bool,246	pub trivia: ChildTrivia,247}248impl EndingComments {249	pub fn is_empty(&self) -> bool {250		!self.should_start_with_newline && self.trivia.is_empty()251	}252	pub fn extract_trailing(&mut self) -> ChildTrivia {253		mem::take(&mut self.trivia)254	}255}