cedar_policy_formatter/pprint/
utils.rs1use std::borrow::Borrow;
18
19use itertools::Itertools;
20use pretty::RcDoc;
21
22use crate::token::regex_constants;
23
24use super::token::{Comment, WrappedToken};
25
26pub fn add_brackets<'a>(d: RcDoc<'a>, leftp: RcDoc<'a>, rightp: RcDoc<'a>) -> RcDoc<'a> {
28 leftp.append(d.nest(1)).append(rightp)
29}
30
31pub fn get_leading_comment_doc_from_str<'src>(leading_comment: &[&'src str]) -> RcDoc<'src> {
33 if leading_comment.is_empty() {
34 RcDoc::nil()
35 } else {
36 RcDoc::hardline()
37 .append(create_multiline_doc(leading_comment))
38 .append(RcDoc::hardline())
39 }
40}
41
42fn create_multiline_doc<'src>(text: &[&'src str]) -> RcDoc<'src> {
47 RcDoc::intersperse(text.iter().map(|c| RcDoc::text(*c)), RcDoc::hardline())
48}
49
50pub fn get_trailing_comment_doc_from_str<'src>(
54 trailing_comment: &'src str,
55 next_doc: RcDoc<'src>,
56) -> RcDoc<'src> {
57 if !trailing_comment.is_empty() {
58 RcDoc::space()
59 .append(RcDoc::text(trailing_comment))
60 .append(RcDoc::hardline())
61 } else {
62 next_doc
63 }
64}
65
66fn get_token_at_start<'a, 'src>(
67 span: Option<miette::SourceSpan>,
68 tokens: &'a mut [WrappedToken<'src>],
69) -> Option<&'a mut WrappedToken<'src>> {
70 let span = span?;
71 tokens
72 .as_mut()
73 .iter_mut()
74 .find(|t| t.span.start == span.offset())
75}
76
77pub fn get_comment_at_start<'src>(
78 span: Option<miette::SourceSpan>,
79 tokens: &mut [WrappedToken<'src>],
80) -> Option<Comment<'src>> {
81 Some(get_token_at_start(span, tokens)?.consume_comment())
82}
83
84pub fn get_leading_comment_at_start<'src>(
85 span: Option<miette::SourceSpan>,
86 tokens: &mut [WrappedToken<'src>],
87) -> Option<Vec<&'src str>> {
88 Some(get_token_at_start(span, tokens)?.consume_leading_comment())
89}
90
91fn get_token_after_end<'a, 'src>(
92 span: Option<miette::SourceSpan>,
93 tokens: &'a mut [WrappedToken<'src>],
94) -> Option<&'a mut WrappedToken<'src>> {
95 let span = span?;
96 let end = span.offset() + span.len();
97 tokens.iter_mut().find_or_first(|t| t.span.start >= end)
98}
99
100fn get_token_at_end<'a, 'src>(
101 span: Option<miette::SourceSpan>,
102 tokens: &'a mut [WrappedToken<'src>],
103) -> Option<&'a mut WrappedToken<'src>> {
104 let span = span?;
105 let end = span.offset() + span.len();
106 tokens.iter_mut().find(|t| t.span.end == end)
107}
108
109pub fn get_comment_at_end<'src>(
110 span: Option<miette::SourceSpan>,
111 tokens: &mut [WrappedToken<'src>],
112) -> Option<Comment<'src>> {
113 Some(get_token_at_end(span, tokens)?.consume_comment())
114}
115
116pub fn get_comment_after_end<'src>(
117 span: Option<miette::SourceSpan>,
118 tokens: &mut [WrappedToken<'src>],
119) -> Option<Comment<'src>> {
120 Some(get_token_after_end(span, tokens)?.consume_comment())
121}
122
123pub fn get_comment_in_range<'src>(
124 span: Option<miette::SourceSpan>,
125 tokens: &mut [WrappedToken<'src>],
126) -> Option<Vec<Comment<'src>>> {
127 let span = span?;
128 Some(
129 tokens
130 .iter_mut()
131 .skip_while(|t| t.span.start < span.offset())
132 .take_while(|t| t.span.end <= span.offset() + span.len())
133 .map(|t| t.consume_comment())
134 .collect(),
135 )
136}
137
138pub fn add_comment<'src>(
142 d: RcDoc<'src>,
143 comment: impl Borrow<Comment<'src>>,
144 next_doc: RcDoc<'src>,
145) -> RcDoc<'src> {
146 let leading_comment = comment.borrow().leading_comment();
147 let trailing_comment = comment.borrow().trailing_comment();
148 let leading_comment_doc = get_leading_comment_doc_from_str(leading_comment);
149 let trailing_comment_doc = get_trailing_comment_doc_from_str(trailing_comment, next_doc);
150 leading_comment_doc.append(d).append(trailing_comment_doc)
151}
152
153fn remove_empty_interior_lines(s: &str) -> String {
159 let mut new_s = String::new();
160 if s.starts_with('\n') {
161 new_s.push('\n');
162 }
163 new_s.push_str(
164 s.split_inclusive('\n')
165 .filter(|ss| !ss.trim().is_empty() || !ss.contains('\n'))
168 .collect::<Vec<_>>()
169 .join("")
170 .as_str(),
171 );
172 new_s
173}
174
175pub fn remove_empty_lines(text: &str) -> String {
177 let mut index = 0;
178 let mut final_text = String::new();
179
180 while index < text.len() {
181 let comment_match = regex_constants::COMMENT.find_at(text, index);
186 let string_match = regex_constants::STRING.find_at(text, index);
187 match (comment_match, string_match) {
188 (Some(m1), Some(m2)) => {
189 let m = std::cmp::min_by_key(m1, m2, |m| m.start());
191 #[allow(clippy::indexing_slicing)]
193 final_text.push_str(&remove_empty_interior_lines(&text[index..m.start()]));
194 final_text.push_str(m.as_str());
195 index = m.end();
196 }
197 (Some(m), None) | (None, Some(m)) => {
198 #[allow(clippy::indexing_slicing)]
200 final_text.push_str(&remove_empty_interior_lines(&text[index..m.start()]));
201 final_text.push_str(m.as_str());
202 index = m.end();
203 }
204 (None, None) => {
205 #[allow(clippy::indexing_slicing)]
207 final_text.push_str(&remove_empty_interior_lines(&text[index..]));
208 break;
209 }
210 }
211 }
212 final_text.trim().to_string()
214}