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

difftreelog

source

cmds/jrsonnet-fmt/src/comments.rs4.0 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		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				// doc-style comment, /**30				let doc = if text.starts_with('*') {31					text = &text[1..];32					true33				} else {34					false35				};36				// Is comment starts with text immediatly, i.e /*text37				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					// First line is not empty, extract ws prefix of it65					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			// TODO: Keep common padding for multiple continous lines of single-line comments120			// I.e121			// ```122			// #  Line1123			// #    Line2124			// ```125			// Should be reformatted as126			// ```127			// # Line1128			// #   Line2129			// ```130			// But currently comment formatter is not aware of continous comment lines, and reformats it as131			// ```132			// # Line1133			// # Line2134			// ```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			// Garbage in - garbage out154			TriviaKind::ErrorCommentTooShort => p!(pi: str("/*/")),155			TriviaKind::ErrorCommentUnterminated => p!(pi: str(c.text())),156		}157	}158159	pi160}