123use 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>>;111213pub 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}3637pub 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 90 >= 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 143 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 156 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 175 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 224 pub should_start_with_newline: bool,225 226 227 228 229 230 231 pub before_trivia: ChildTrivia,232 pub value: T,233 234 235 236 237 238 239 240 pub inline_trivia: ChildTrivia,241}242243pub struct EndingComments {244 245 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}