123use std::{fmt::Debug, mem};45use jrsonnet_rowan_parser::{6 AstNode, AstToken, SyntaxElement, SyntaxNode, TS,7 nodes::{CustomError, Trivia, TriviaKind},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}828384pub fn should_start_with_newline(85 prev_inline: Option<&ChildTrivia>,86 tt: &ChildTrivia,87) -> (bool, bool) {88 let count =89 count_newlines_before(tt) + prev_inline.map(count_newlines_after).unwrap_or_default();9091 (92 93 count >= 2,94 count >= 1,95 )96}9798fn count_newlines_before(tt: &ChildTrivia) -> usize {99 let mut nl_count = 0;100 for t in tt {101 match t {102 Ok(t) => match t.kind() {103 TriviaKind::Whitespace => {104 nl_count += t.text().bytes().filter(|b| *b == b'\n').count();105 }106 _ => break,107 },108 Err(_) => {109 nl_count += 1;110 }111 }112 }113 nl_count114}115fn count_newlines_after(tt: &ChildTrivia) -> usize {116 let mut nl_count = 0;117 for t in tt.iter().rev() {118 match t {119 Ok(t) => match t.kind() {120 TriviaKind::Whitespace => {121 nl_count += t.text().bytes().filter(|b| *b == b'\n').count();122 }123 TriviaKind::SingleLineHashComment | TriviaKind::SingleLineSlashComment => {124 nl_count += 1;125 break;126 }127 _ => {}128 },129 Err(_) => nl_count += 1,130 }131 }132 nl_count133}134135pub fn children<T: AstNode + Debug>(136 items: impl Iterator<Item = SyntaxElement>,137 loose: bool,138 mut trailing: Option<ChildTrivia>,139) -> (Vec<Child<T>>, EndingComments) {140 let mut out = Vec::new();141 let mut current_child = None::<Child<T>>;142 let mut next = ChildTrivia::new();143 144 let mut started_next = false;145 let mut had_some = false;146147 for item in items {148 if let Some(value) = item.as_node().cloned().and_then(T::cast) {149 let before_trivia = if let Some(trailing) = trailing.take() {150 assert!(next.is_empty());151 trailing152 } else {153 mem::take(&mut next)154 };155 let (should_start_with_newline, triggers_multiline) = should_start_with_newline(156 current_child.as_ref().map(|c| &c.inline_trivia),157 &before_trivia,158 );159 let last_child = current_child.replace(Child {160 161 should_start_with_newline: had_some && should_start_with_newline,162 triggers_multiline,163 before_trivia,164 value,165 inline_trivia: Vec::new(),166 });167 if let Some(last_child) = last_child {168 out.push(last_child);169 }170 had_some = true;171 started_next = false;172 } else if let Some(trivia) = item.as_token().cloned().and_then(Trivia::cast) {173 let is_single_line_comment = trivia.kind() == TriviaKind::SingleLineHashComment174 || trivia.kind() == TriviaKind::SingleLineSlashComment;175 if trailing.is_some() {176 177 continue;178 } else if started_next179 || current_child.is_none()180 || trivia.text().contains('\n') && !is_single_line_comment181 {182 next.push(Ok(trivia.clone()));183 started_next = true;184 } else {185 let cur = current_child.as_mut().expect("checked not none");186 cur.inline_trivia.push(Ok(trivia));187 if is_single_line_comment {188 started_next = true;189 }190 }191 had_some = true;192 } else if CustomError::can_cast(item.kind()) {193 next.push(Err(item.to_string()));194 } else if loose {195 if had_some {196 break;197 }198 started_next = true;199 } else {200 assert!(201 TS![, ;].contains(item.kind()),202 "silently eaten token: {:?}",203 item.kind()204 );205 }206 }207208 let ending_comments = EndingComments {209 should_start_with_newline: should_start_with_newline(210 current_child.as_ref().map(|c| &c.inline_trivia),211 &next,212 )213 .0,214 trivia: next,215 };216217 if let Some(current_child) = current_child {218 out.push(current_child);219 }220221 (out, ending_comments)222}223224#[derive(Debug)]225pub struct Child<T> {226 227 pub should_start_with_newline: bool,228 229 230 231 232 233 234 pub before_trivia: ChildTrivia,235 pub value: T,236 237 238 239 240 241 242 243 pub inline_trivia: ChildTrivia,244 245 246 pub triggers_multiline: bool,247}248249pub struct EndingComments {250 251 pub should_start_with_newline: bool,252 pub trivia: ChildTrivia,253}254impl EndingComments {255 pub fn is_empty(&self) -> bool {256 !self.should_start_with_newline && self.trivia.is_empty()257 }258 pub fn extract_trailing(&mut self) -> ChildTrivia {259 mem::take(&mut self.trivia)260 }261}