1use dprint_core::formatting::PrintItems;2use jrsonnet_rowan_parser::{nodes::TriviaKind, AstToken};34use crate::{children::ChildTrivia, p, pi};56pub enum CommentLocation {7 8 AboveItem,9 10 ItemInline,11 12 EndOfItems,13}1415pub fn format_comments(comments: &ChildTrivia, loc: CommentLocation) -> PrintItems {16 let mut pi = p!(new:);1718 for c in comments {19 match c.kind() {20 TriviaKind::Whitespace => {}21 TriviaKind::MultiLineComment => {22 let mut text = c23 .text()24 .strip_prefix("/*")25 .expect("ml comment starts with /*")26 .strip_suffix("*/")27 .expect("ml comment ends with */");28 29 let doc = if text.starts_with('*') {30 text = &text[1..];31 true32 } else {33 false34 };35 36 let mut immediate_start = true;37 let mut lines = text38 .split('\n')39 .map(|l| l.trim_end())40 .skip_while(|l| {41 if l.is_empty() {42 immediate_start = false;43 true44 } else {45 false46 }47 })48 .collect::<Vec<_>>();49 while lines.last().map(|l| l.is_empty()).unwrap_or(false) {50 lines.pop();51 }52 if lines.len() == 1 && !doc {53 p!(pi: str("/* ") str(lines[0].trim()) str(" */") nl)54 } else if !lines.is_empty() {55 fn common_ws_prefix<'a>(a: &'a str, b: &str) -> &'a str {56 let offset = a57 .bytes()58 .zip(b.bytes())59 .take_while(|(a, b)| a == b && (a.is_ascii_whitespace() || *a == b'*'))60 .count();61 &a[..offset]62 }63 64 let mut common_ws_padding = if immediate_start && lines.len() > 1 {65 common_ws_prefix(lines[1], lines[1])66 } else {67 common_ws_prefix(lines[0], lines[0])68 };69 for line in lines70 .iter()71 .skip(if immediate_start { 2 } else { 1 })72 .filter(|l| !l.is_empty())73 {74 common_ws_padding = common_ws_prefix(common_ws_padding, line);75 }76 for line in lines77 .iter_mut()78 .skip(if immediate_start { 1 } else { 0 })79 .filter(|l| !l.is_empty())80 {81 *line = line82 .strip_prefix(common_ws_padding)83 .expect("all non-empty lines start with this padding");84 }8586 p!(pi: str("/*"));87 if doc {88 p!(pi: str("*"));89 }90 p!(pi: nl);91 for mut line in lines {92 if doc {93 p!(pi: str(" *"));94 }95 if line.is_empty() {96 p!(pi: nl);97 } else {98 if doc {99 p!(pi: str(" "));100 }101 while let Some(new_line) = line.strip_prefix('\t') {102 if doc {103 p!(pi: str(" "));104 } else {105 p!(pi: tab);106 }107 line = new_line;108 }109 p!(pi: str(line) nl)110 }111 }112 if doc {113 p!(pi: str(" "));114 }115 p!(pi: str("*/") nl)116 }117 }118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 TriviaKind::SingleLineHashComment => {135 if matches!(loc, CommentLocation::ItemInline) {136 p!(pi: str(" "))137 }138 p!(pi: str("# ") str(c.text().strip_prefix('#').expect("hash comment starts with #").trim()));139 if !matches!(loc, CommentLocation::ItemInline) {140 p!(pi: nl)141 }142 }143 TriviaKind::SingleLineSlashComment => {144 if matches!(loc, CommentLocation::ItemInline) {145 p!(pi: str(" "))146 }147 p!(pi: str("// ") str(c.text().strip_prefix("//").expect("comment starts with //").trim()));148 if !matches!(loc, CommentLocation::ItemInline) {149 p!(pi: nl)150 }151 }152 153 TriviaKind::ErrorCommentTooShort => p!(pi: str("/*/")),154 TriviaKind::ErrorCommentUnterminated => p!(pi: str(c.text())),155 }156 }157158 pi159}