git.delta.rocks / jrsonnet / refs/commits / d349b9ef06a9

difftreelog

source

cmds/jrsonnet-fmt/src/comments.rs4.6 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}1415pub fn format_comments(comments: &ChildTrivia, loc: CommentLocation, out: &mut PrintItems) {16	for c in comments {17		let Ok(c) = c else {18			let mut text = c.as_ref().unwrap_err() as &str;19			while !text.is_empty() {20				let pos = text.find(|c| c == '\n' || c == '\t').unwrap_or(text.len());21				let sliced = &text[..pos];22				p!(out, string(sliced.to_string()));23				text = &text[pos..];24				if !text.is_empty() {25					match text.as_bytes()[0] {26						b'\n' => p!(out, nl),27						b'\t' => p!(out, tab),28						_ => unreachable!(),29					}30					text = &text[1..];31				}32			}33			continue;34		};35		match c.kind() {36			TriviaKind::Whitespace => {}37			TriviaKind::MultiLineComment => {38				let mut text = c39					.text()40					.strip_prefix("/*")41					.expect("ml comment starts with /*")42					.strip_suffix("*/")43					.expect("ml comment ends with */");44				// doc-style comment, /**45				let doc = if text.starts_with('*') {46					text = &text[1..];47					true48				} else {49					false50				};51				// Is comment starts with text immediatly, i.e /*text52				let mut immediate_start = true;53				let mut lines = text54					.split('\n')55					.map(|l| l.trim_end().to_string())56					.skip_while(|l| {57						if l.is_empty() {58							immediate_start = false;59							true60						} else {61							false62						}63					})64					.collect::<Vec<_>>();65				while lines.last().map(|l| l.is_empty()).unwrap_or(false) {66					lines.pop();67				}68				if lines.len() == 1 && !doc {69					if matches!(loc, CommentLocation::ItemInline) {70						p!(out, str(" "));71					}72					p!(out, str("/* ") string(lines[0].trim().to_string()) str(" */") nl)73				} else if !lines.is_empty() {74					fn common_ws_prefix<'a>(a: &'a str, b: &str) -> &'a str {75						let offset = a76							.bytes()77							.zip(b.bytes())78							.take_while(|(a, b)| a == b && (a.is_ascii_whitespace() || *a == b'*'))79							.count();80						&a[..offset]81					}82					// First line is not empty, extract ws prefix of it83					let mut common_ws_padding = (if immediate_start && lines.len() > 1 {84						common_ws_prefix(&lines[1], &lines[1])85					} else {86						common_ws_prefix(&lines[0], &lines[0])87					})88					.to_string();89					for line in lines90						.iter()91						.skip(if immediate_start { 2 } else { 1 })92						.filter(|l| !l.is_empty())93					{94						common_ws_padding = common_ws_prefix(&common_ws_padding, line).to_string();95					}96					for line in lines97						.iter_mut()98						.skip(if immediate_start { 1 } else { 0 })99						.filter(|l| !l.is_empty())100					{101						*line = line102							.strip_prefix(&common_ws_padding)103							.expect("all non-empty lines start with this padding")104							.to_string();105					}106107					p!(out, str("/*"));108					if doc {109						p!(out, str("*"));110					}111					p!(out, nl);112					for mut line in lines {113						if doc {114							p!(out, str(" *"));115						}116						if line.is_empty() {117							p!(out, nl);118						} else {119							if doc {120								p!(out, str(" "));121							}122							while let Some(new_line) = line.strip_prefix('\t') {123								if doc {124									p!(out, str("    "));125								} else {126									p!(out, tab);127								}128								line = new_line.to_string();129							}130							p!(out, string(line.to_string()) nl)131						}132					}133					if doc {134						p!(out, str(" "));135					}136					p!(out, str("*/") nl)137				}138			}139			// TODO: Keep common padding for multiple continous lines of single-line comments140			// I.e141			// ```142			// #  Line1143			// #    Line2144			// ```145			// Should be reformatted as146			// ```147			// # Line1148			// #   Line2149			// ```150			// But currently comment formatter is not aware of continous comment lines, and reformats it as151			// ```152			// # Line1153			// # Line2154			// ```155			TriviaKind::SingleLineHashComment => {156				if matches!(loc, CommentLocation::ItemInline) {157					p!(out, str(" "))158				}159				p!(out, str("# ") string(c.text().strip_prefix('#').expect("hash comment starts with #").trim().to_string()));160				if !matches!(loc, CommentLocation::ItemInline) {161					p!(out, nl)162				}163			}164			TriviaKind::SingleLineSlashComment => {165				if matches!(loc, CommentLocation::ItemInline) {166					p!(out, str(" "))167				}168				p!(out, str("// ") string(c.text().strip_prefix("//").expect("comment starts with //").trim().to_string()));169				if !matches!(loc, CommentLocation::ItemInline) {170					p!(out, nl)171				}172			}173			// Garbage in - garbage out174			TriviaKind::ErrorCommentTooShort => p!(out, str("/*/")),175			TriviaKind::ErrorCommentUnterminated => p!(out, string(c.text().to_string())),176		}177	}178}