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}1415#[must_use]16pub fn format_comments(comments: &ChildTrivia, loc: CommentLocation) -> PrintItems {17 let mut pi = p!(new:);1819 for c in comments {20 match c.kind() {21 TriviaKind::Whitespace => {}22 TriviaKind::MultiLineComment => {23 let mut text = c24 .text()25 .strip_prefix("/*")26 .expect("ml comment starts with /*")27 .strip_suffix("*/")28 .expect("ml comment ends with */");29 30 let doc = if text.starts_with('*') {31 text = &text[1..];32 true33 } else {34 false35 };36 37 let mut immediate_start = true;38 let mut lines = text39 .split('\n')40 .map(|l| l.trim_end())41 .skip_while(|l| {42 if l.is_empty() {43 immediate_start = false;44 true45 } else {46 false47 }48 })49 .collect::<Vec<_>>();50 while lines.last().map(|l| l.is_empty()).unwrap_or(false) {51 lines.pop();52 }53 if lines.len() == 1 && !doc {54 p!(pi: str("/* ") str(lines[0].trim()) str(" */") nl)55 } else if !lines.is_empty() {56 fn common_ws_prefix<'a>(a: &'a str, b: &str) -> &'a str {57 let offset = a58 .bytes()59 .zip(b.bytes())60 .take_while(|(a, b)| a == b && (a.is_ascii_whitespace() || *a == b'*'))61 .count();62 &a[..offset]63 }64 65 let mut common_ws_padding = if immediate_start && lines.len() > 1 {66 common_ws_prefix(lines[1], lines[1])67 } else {68 common_ws_prefix(lines[0], lines[0])69 };70 for line in lines71 .iter()72 .skip(if immediate_start { 2 } else { 1 })73 .filter(|l| !l.is_empty())74 {75 common_ws_padding = common_ws_prefix(common_ws_padding, line);76 }77 for line in lines78 .iter_mut()79 .skip(if immediate_start { 1 } else { 0 })80 .filter(|l| !l.is_empty())81 {82 *line = line83 .strip_prefix(common_ws_padding)84 .expect("all non-empty lines start with this padding");85 }8687 p!(pi: str("/*"));88 if doc {89 p!(pi: str("*"));90 }91 p!(pi: nl);92 for mut line in lines {93 if doc {94 p!(pi: str(" *"));95 }96 if line.is_empty() {97 p!(pi: nl);98 } else {99 if doc {100 p!(pi: str(" "));101 }102 while let Some(new_line) = line.strip_prefix('\t') {103 if doc {104 p!(pi: str(" "));105 } else {106 p!(pi: tab);107 }108 line = new_line;109 }110 p!(pi: str(line) nl)111 }112 }113 if doc {114 p!(pi: str(" "));115 }116 p!(pi: str("*/") nl)117 }118 }119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 TriviaKind::SingleLineHashComment => {136 if matches!(loc, CommentLocation::ItemInline) {137 p!(pi: str(" "))138 }139 p!(pi: str("# ") str(c.text().strip_prefix('#').expect("hash comment starts with #").trim()));140 if !matches!(loc, CommentLocation::ItemInline) {141 p!(pi: nl)142 }143 }144 TriviaKind::SingleLineSlashComment => {145 if matches!(loc, CommentLocation::ItemInline) {146 p!(pi: str(" "))147 }148 p!(pi: str("// ") str(c.text().strip_prefix("//").expect("comment starts with //").trim()));149 if !matches!(loc, CommentLocation::ItemInline) {150 p!(pi: nl)151 }152 }153 154 TriviaKind::ErrorCommentTooShort => p!(pi: str("/*/")),155 TriviaKind::ErrorCommentUnterminated => p!(pi: str(c.text())),156 }157 }158159 pi160}