git.delta.rocks / jrsonnet / refs/commits / 9ebfdebec2e2

difftreelog

source

cmds/jrsonnet-fmt/src/comments.rs4.7 KiBsourcehistory
1use dprint_core::formatting::PrintItems;2use jrsonnet_rowan_parser::{nodes::TriviaKind, AstToken};34use crate::{children::ChildTrivia, p, pi};56pub enum CommentLocation {7	/// Above local, field, other things8	AboveItem,9	/// After item10	ItemInline,11	/// After all items in object12	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		let Ok(c) = c else {21			let mut text = c.as_ref().unwrap_err() as &str;22			while !text.is_empty() {23				let pos = text.find(|c| c == '\n' || c == '\t').unwrap_or(text.len());24				let sliced = &text[..pos];25				p!(pi: string(sliced.to_string()));26				text = &text[pos..];27				if !text.is_empty() {28					match text.as_bytes()[0] {29						b'\n' => p!(pi: nl),30						b'\t' => p!(pi: tab),31						_ => unreachable!(),32					}33					text = &text[1..];34				}35			}36			continue;37		};38		match c.kind() {39			TriviaKind::Whitespace => {}40			TriviaKind::MultiLineComment => {41				let mut text = c42					.text()43					.strip_prefix("/*")44					.expect("ml comment starts with /*")45					.strip_suffix("*/")46					.expect("ml comment ends with */");47				// doc-style comment, /**48				let doc = if text.starts_with('*') {49					text = &text[1..];50					true51				} else {52					false53				};54				// Is comment starts with text immediatly, i.e /*text55				let mut immediate_start = true;56				let mut lines = text57					.split('\n')58					.map(|l| l.trim_end().to_string())59					.skip_while(|l| {60						if l.is_empty() {61							immediate_start = false;62							true63						} else {64							false65						}66					})67					.collect::<Vec<_>>();68				while lines.last().map(|l| l.is_empty()).unwrap_or(false) {69					lines.pop();70				}71				if lines.len() == 1 && !doc {72					if matches!(loc, CommentLocation::ItemInline) {73						p!(pi: str(" "));74					}75					p!(pi: str("/* ") string(lines[0].trim().to_string()) str(" */"))76				} else if !lines.is_empty() {77					fn common_ws_prefix<'a>(a: &'a str, b: &str) -> &'a str {78						let offset = a79							.bytes()80							.zip(b.bytes())81							.take_while(|(a, b)| a == b && (a.is_ascii_whitespace() || *a == b'*'))82							.count();83						&a[..offset]84					}85					// First line is not empty, extract ws prefix of it86					let mut common_ws_padding = (if immediate_start && lines.len() > 1 {87						common_ws_prefix(&lines[1], &lines[1])88					} else {89						common_ws_prefix(&lines[0], &lines[0])90					})91					.to_string();92					for line in lines93						.iter()94						.skip(if immediate_start { 2 } else { 1 })95						.filter(|l| !l.is_empty())96					{97						common_ws_padding = common_ws_prefix(&common_ws_padding, line).to_string();98					}99					for line in lines100						.iter_mut()101						.skip(if immediate_start { 1 } else { 0 })102						.filter(|l| !l.is_empty())103					{104						*line = line105							.strip_prefix(&common_ws_padding)106							.expect("all non-empty lines start with this padding")107							.to_string();108					}109110					p!(pi: str("/*"));111					if doc {112						p!(pi: str("*"));113					}114					p!(pi: nl);115					for mut line in lines {116						if doc {117							p!(pi: str(" *"));118						}119						if line.is_empty() {120							p!(pi: nl);121						} else {122							if doc {123								p!(pi: str(" "));124							}125							while let Some(new_line) = line.strip_prefix('\t') {126								if doc {127									p!(pi: str("    "));128								} else {129									p!(pi: tab);130								}131								line = new_line.to_string();132							}133							p!(pi: string(line.to_string()) nl)134						}135					}136					if doc {137						p!(pi: str(" "));138					}139					p!(pi: str("*/") nl)140				}141			}142			// TODO: Keep common padding for multiple continous lines of single-line comments143			// I.e144			// ```145			// #  Line1146			// #    Line2147			// ```148			// Should be reformatted as149			// ```150			// # Line1151			// #   Line2152			// ```153			// But currently comment formatter is not aware of continous comment lines, and reformats it as154			// ```155			// # Line1156			// # Line2157			// ```158			TriviaKind::SingleLineHashComment => {159				if matches!(loc, CommentLocation::ItemInline) {160					p!(pi: str(" "))161				}162				p!(pi: str("# ") string(c.text().strip_prefix('#').expect("hash comment starts with #").trim().to_string()));163				if !matches!(loc, CommentLocation::ItemInline) {164					p!(pi: nl)165				}166			}167			TriviaKind::SingleLineSlashComment => {168				if matches!(loc, CommentLocation::ItemInline) {169					p!(pi: str(" "))170				}171				p!(pi: str("// ") string(c.text().strip_prefix("//").expect("comment starts with //").trim().to_string()));172				if !matches!(loc, CommentLocation::ItemInline) {173					p!(pi: nl)174				}175			}176			// Garbage in - garbage out177			TriviaKind::ErrorCommentTooShort => p!(pi: str("/*/")),178			TriviaKind::ErrorCommentUnterminated => p!(pi: string(c.text().to_string())),179		}180	}181182	pi183}