1use super::super::configuration::Configuration;
2use super::context::Context;
3use super::token_finder::TokenFinder;
4use dprint_core::formatting::conditions::if_true_or;
5use dprint_core::formatting::ir_helpers::SingleLineOptions;
6use dprint_core::formatting::*;
7use dprint_core_macros::sc;
8use jsonc_parser::ast::*;
9use jsonc_parser::common::Range;
10use jsonc_parser::common::Ranged;
11use jsonc_parser::tokens::TokenAndRange;
12use std::collections::HashSet;
13use std::rc::Rc;
14use text_lines::TextLines;
15
16use crate::configuration::*;
17
18pub fn generate(
19 parse_result: jsonc_parser::ParseResult,
20 text: &str,
21 config: &Configuration,
22 is_jsonc: bool,
23) -> PrintItems {
24 let comments = parse_result.comments.unwrap();
25 let tokens = parse_result.tokens.unwrap();
26 let node_value = parse_result.value;
27 let text_info = TextLines::new(text);
28 let mut context = Context {
29 config,
30 text,
31 text_info,
32 is_jsonc,
33 handled_comments: HashSet::new(),
34 parent_stack: Vec::new(),
35 current_node: None,
36 comments: &comments,
37 token_finder: TokenFinder::new(&tokens),
38 };
39
40 let mut items = PrintItems::new();
41 if let Some(node_value) = &node_value {
42 items.extend(gen_node(node_value.into(), &mut context));
43 items.extend(gen_trailing_comments_as_statements(node_value, &mut context));
44 } else if let Some(comments) = comments.get(&0) {
45 items.extend(gen_comments_as_statements(comments.iter(), None, &mut context));
46 }
47 items.push_condition(conditions::if_true(
48 "endOfFileNewLine",
49 Rc::new(|context| Some(context.writer_info.column_number > 0 || context.writer_info.line_number > 0)),
50 Signal::NewLine.into(),
51 ));
52
53 items
54}
55
56fn gen_node<'a>(node: Node<'a, 'a>, context: &mut Context<'a, '_>) -> PrintItems {
57 gen_node_with_inner(node, context, |items, _| items)
58}
59
60fn gen_node_with_inner<'a>(
61 node: Node<'a, 'a>,
62 context: &mut Context<'a, '_>,
63 inner_gen: impl FnOnce(PrintItems, &mut Context<'a, '_>) -> PrintItems,
64) -> PrintItems {
65 let past_current_node = context.current_node.replace(node);
67 let parent_end = past_current_node.as_ref().map(|n| n.end());
68 let node_end = node.end();
69 let is_root = past_current_node.is_none();
70
71 if let Some(past_current_node) = past_current_node {
72 context.parent_stack.push(past_current_node);
73 }
74
75 let mut items = PrintItems::new();
77
78 if let Some(comments) = context.comments.get(&node.start()) {
80 items.extend(gen_comments_as_leading(&node, comments.iter(), context));
81 }
82
83 if has_ignore_comment(&node, context) {
85 items.push_force_current_line_indentation();
86 items.extend(inner_gen(
87 ir_helpers::gen_from_raw_string(node.text(context.text)),
88 context,
89 ));
90 } else {
91 items.extend(inner_gen(gen_node_inner(&node, context), context))
92 }
93
94 if (is_root || parent_end.is_some() && parent_end.unwrap() != node_end)
96 && let Some(comments) = context.comments.get(&node_end)
97 {
98 items.extend(gen_comments_as_trailing(&node, comments.iter(), context));
99 }
100
101 context.current_node = context.parent_stack.pop();
102
103 return items;
104
105 #[inline]
106 fn gen_node_inner<'a>(node: &Node<'a, 'a>, context: &mut Context<'a, '_>) -> PrintItems {
107 match node {
108 Node::Array(node) => gen_array(node, context),
109 Node::BooleanLit(node) => node.value.to_string().into(),
110 Node::NullKeyword(_) => "null".into(),
111 Node::NumberLit(node) => node.value.to_string().into(),
112 Node::Object(node) => gen_object(node, context),
113 Node::ObjectProp(node) => gen_object_prop(node, context),
114 Node::StringLit(node) => gen_string_lit(node, context),
115 Node::WordLit(node) => gen_word_lit(node, context),
116 }
117 }
118}
119
120fn gen_array<'a>(node: &'a Array<'a>, context: &mut Context<'a, '_>) -> PrintItems {
121 let force_multi_lines = !context.config.array_prefer_single_line
122 && (should_break_up_single_line(node, context)
123 || context.text_info.line_index(node.start())
124 < node
125 .elements
126 .first()
127 .map(|p| context.text_info.line_index(p.start()))
128 .unwrap_or_else(|| context.text_info.line_index(node.start())));
129
130 gen_surrounded_by_tokens(
131 |context| {
132 let mut items = PrintItems::new();
133 items.extend(gen_comma_separated_values(
134 GenCommaSeparatedValuesOptions {
135 nodes: node.elements.iter().map(|x| Some(x.into())).collect(),
136 prefer_hanging: false,
137 force_use_new_lines: force_multi_lines,
138 allow_blank_lines: true,
139 single_line_space_at_start: false,
140 single_line_space_at_end: false,
141 custom_single_line_separator: None,
142 multi_line_options: ir_helpers::MultiLineOptions::surround_newlines_indented(),
143 force_possible_newline_at_start: false,
144 },
145 context,
146 ));
147 items
148 },
149 GenSurroundedByTokensOptions {
150 open_token: sc!("["),
151 close_token: sc!("]"),
152 range: node.range,
153 first_member: node.elements.first().map(|f| f.range()),
154 prefer_single_line_when_empty: true,
155 },
156 context,
157 )
158}
159
160fn gen_object<'a>(obj: &'a Object, context: &mut Context<'a, '_>) -> PrintItems {
161 let force_multi_lines = !context.config.object_prefer_single_line
162 && (should_break_up_single_line(obj, context)
163 || context.text_info.line_index(obj.start())
164 < obj
165 .properties
166 .first()
167 .map(|p| context.text_info.line_index(p.start()))
168 .unwrap_or_else(|| context.text_info.line_index(obj.end())));
169
170 gen_surrounded_by_tokens(
171 |context| {
172 let mut items = PrintItems::new();
173 items.extend(gen_comma_separated_values(
174 GenCommaSeparatedValuesOptions {
175 nodes: obj.properties.iter().map(|x| Some(Node::ObjectProp(x))).collect(),
176 prefer_hanging: false,
177 force_use_new_lines: force_multi_lines,
178 allow_blank_lines: true,
179 single_line_space_at_start: true,
180 single_line_space_at_end: true,
181 custom_single_line_separator: None,
182 multi_line_options: ir_helpers::MultiLineOptions::surround_newlines_indented(),
183 force_possible_newline_at_start: false,
184 },
185 context,
186 ));
187 items
188 },
189 GenSurroundedByTokensOptions {
190 open_token: sc!("{"),
191 close_token: sc!("}"),
192 range: obj.range,
193 first_member: obj.properties.first().map(|f| f.range()),
194 prefer_single_line_when_empty: false,
195 },
196 context,
197 )
198}
199
200fn gen_object_prop<'a>(node: &'a ObjectProp, context: &mut Context<'a, '_>) -> PrintItems {
201 let mut items = PrintItems::new();
202 items.extend(gen_node((&node.name).into(), context));
203 items.push_sc(sc!(": "));
204 items.extend(gen_node((&node.value).into(), context));
205
206 items
207}
208
209const DOUBLE_QUOTE_SC: &StringContainer = sc!("\"");
210
211fn gen_string_lit<'a>(node: &'a StringLit, context: &mut Context<'a, '_>) -> PrintItems {
212 let text = node.text(context.text);
213 let is_double_quotes = text.starts_with('"');
214 let mut items = PrintItems::new();
215 let text = &text[1..text.len() - 1];
216 items.push_sc(DOUBLE_QUOTE_SC);
217 if is_double_quotes {
218 items.push_string(text.to_string());
219 } else {
220 let text = text.replace("\\'", "'");
221 items.push_string(text.replace('"', "\\\""));
222 }
223 items.push_sc(DOUBLE_QUOTE_SC);
224 items
225}
226
227fn gen_word_lit<'a>(node: &'a WordLit<'a>, _: &mut Context<'a, '_>) -> PrintItems {
228 let mut items = PrintItems::new();
230 items.push_sc(DOUBLE_QUOTE_SC);
231 items.push_string(node.value.to_string());
232 items.push_sc(DOUBLE_QUOTE_SC);
233 items
234}
235
236struct GenCommaSeparatedValuesOptions<'a> {
237 nodes: Vec<Option<Node<'a, 'a>>>,
238 prefer_hanging: bool,
239 force_use_new_lines: bool,
240 allow_blank_lines: bool,
241 single_line_space_at_start: bool,
242 single_line_space_at_end: bool,
243 custom_single_line_separator: Option<PrintItems>,
244 multi_line_options: ir_helpers::MultiLineOptions,
245 force_possible_newline_at_start: bool,
246}
247
248fn gen_comma_separated_values<'a>(
249 opts: GenCommaSeparatedValuesOptions<'a>,
250 context: &mut Context<'a, '_>,
251) -> PrintItems {
252 let nodes = opts.nodes;
253 let indent_width = context.config.indent_width;
254 let compute_lines_span = opts.allow_blank_lines && opts.force_use_new_lines; ir_helpers::gen_separated_values(
256 |is_multi_line_or_hanging_ref| {
257 let mut generated_nodes = Vec::new();
258 let nodes_count = nodes.len();
259 for (i, value) in nodes.into_iter().enumerate() {
260 let (allow_inline_multi_line, allow_inline_single_line) = if let Some(value) = &value {
261 (value.kind() == NodeKind::Object, false)
262 } else {
263 (false, false)
264 };
265 let lines_span = if compute_lines_span {
266 value.as_ref().map(|x| ir_helpers::LinesSpan {
267 start_line: context.start_line_with_comments(x),
268 end_line: context.end_line_with_comments(x),
269 })
270 } else {
271 None
272 };
273 let items = ir_helpers::new_line_group({
274 let is_final_node = i == nodes_count - 1;
275 let use_comma_for_last = !is_final_node
276 || match context.config.trailing_commas {
277 TrailingCommaKind::Always => true,
278 TrailingCommaKind::Maintain => match &value {
279 Some(value) => context.token_finder.get_next_token_if_comma(&value.range()).is_some(),
280 None => false,
281 },
282 TrailingCommaKind::Jsonc => context.is_jsonc,
283 TrailingCommaKind::Never => false,
284 };
285 let maybe_comma = if !is_final_node {
286 ",".into()
287 } else if use_comma_for_last {
288 let is_multi_line = is_multi_line_or_hanging_ref.create_resolver();
289 if_true_or("is_multi_line", is_multi_line, ",".into(), PrintItems::new()).into()
290 } else {
291 PrintItems::new()
292 };
293 gen_comma_separated_value(value, maybe_comma, context)
294 });
295 generated_nodes.push(ir_helpers::GeneratedValue {
296 items,
297 lines_span,
298 allow_inline_multi_line,
299 allow_inline_single_line,
300 });
301 }
302
303 generated_nodes
304 },
305 ir_helpers::GenSeparatedValuesOptions {
306 prefer_hanging: opts.prefer_hanging,
307 force_use_new_lines: opts.force_use_new_lines,
308 allow_blank_lines: opts.allow_blank_lines,
309 single_line_options: SingleLineOptions {
310 space_at_start: opts.single_line_space_at_start,
311 space_at_end: opts.single_line_space_at_end,
312 separator: opts
313 .custom_single_line_separator
314 .unwrap_or_else(|| Signal::SpaceOrNewLine.into()),
315 },
316 indent_width,
317 multi_line_options: opts.multi_line_options,
318 force_possible_newline_at_start: opts.force_possible_newline_at_start,
319 },
320 )
321 .items
322}
323
324fn gen_comma_separated_value<'a>(
325 value: Option<Node<'a, 'a>>,
326 generated_comma: PrintItems,
327 context: &mut Context<'a, '_>,
328) -> PrintItems {
329 let mut items = PrintItems::new();
330 let comma_token = get_comma_token(&value, context);
331
332 if let Some(element) = value {
333 let generated_comma = generated_comma.into_rc_path();
334 items.extend(gen_node_with_inner(element, context, move |mut items, _| {
335 items.push_optional_path(generated_comma);
337 items
338 }));
339 } else {
340 items.extend(generated_comma);
341 }
342
343 if let Some(comma_token) = comma_token {
345 items.extend(gen_trailing_comments(comma_token, context));
346 }
347
348 return items;
349
350 fn get_comma_token<'a, 'b>(element: &Option<Node>, context: &mut Context<'a, 'b>) -> Option<&'b TokenAndRange<'a>> {
351 if let Some(element) = element {
352 context.token_finder.get_next_token_if_comma(element)
353 } else {
354 None
355 }
356 }
357}
358
359struct GenSurroundedByTokensOptions {
360 open_token: &'static StringContainer,
361 close_token: &'static StringContainer,
362 range: Range,
363 first_member: Option<Range>,
364 prefer_single_line_when_empty: bool,
365}
366
367fn gen_surrounded_by_tokens<'a, 'b>(
368 gen_inner: impl FnOnce(&mut Context<'a, 'b>) -> PrintItems,
369 opts: GenSurroundedByTokensOptions,
370 context: &mut Context<'a, 'b>,
371) -> PrintItems {
372 let open_token_end = opts.range.start + opts.open_token.text.len();
373 let close_token_start = opts.range.end - opts.close_token.text.len();
374
375 #[cfg(debug_assertions)]
377 context.assert_text(opts.range.start, open_token_end, opts.open_token.text);
378 #[cfg(debug_assertions)]
379 context.assert_text(close_token_start, opts.range.end, opts.close_token.text);
380
381 let mut items = PrintItems::new();
383 let open_token_start_line = context.text_info.line_index(opts.range.start);
384
385 items.push_sc(opts.open_token);
386 if let Some(first_member) = opts.first_member {
387 let first_member_start_line = context.text_info.line_index(first_member.start);
388 if open_token_start_line < first_member_start_line
389 && let Some(trailing_comments) = context.comments.get(&open_token_end)
390 {
391 items.extend(gen_first_line_trailing_comment(
392 open_token_start_line,
393 trailing_comments.iter(),
394 context,
395 ));
396 }
397 items.extend(gen_inner(context));
398
399 let before_trailing_comments_lc = LineAndColumn::new("beforeTrailingComments");
400 items.push_line_and_column(before_trailing_comments_lc);
401 items.extend(ir_helpers::with_indent(gen_trailing_comments_as_statements(
402 &Range::from_byte_index(open_token_end),
403 context,
404 )));
405 if let Some(leading_comments) = context.comments.get(&close_token_start) {
406 items.extend(ir_helpers::with_indent(gen_comments_as_statements(
407 leading_comments.iter(),
408 None,
409 context,
410 )));
411 }
412 items.push_condition(conditions::if_true(
413 "newLineIfHasCommentsAndNotStartOfNewLine",
414 Rc::new(move |context| {
415 let had_comments = !condition_helpers::is_at_same_position(context, before_trailing_comments_lc)?;
416 Some(had_comments && !context.writer_info.is_start_of_line())
417 }),
418 Signal::NewLine.into(),
419 ));
420 } else {
421 let range_end_line = context.text_info.line_index(opts.range.end);
422 let is_single_line = open_token_start_line == range_end_line;
423 if let Some(comments) = context.comments.get(&open_token_end) {
424 if !is_single_line {
426 items.extend(gen_first_line_trailing_comment(
427 open_token_start_line,
428 comments.iter(),
429 context,
430 ));
431 }
432
433 if has_unhandled_comment(comments.iter(), context) {
435 if is_single_line {
436 let indent_width = context.config.indent_width;
437 items.extend(
438 ir_helpers::gen_separated_values(
439 |_| {
440 let mut generated_comments = Vec::new();
441 for c in comments.iter() {
442 let start_line = context.text_info.line_index(c.start());
443 let end_line = context.text_info.line_index(c.end());
444 if let Some(items) = gen_comment(c, context) {
445 generated_comments.push(ir_helpers::GeneratedValue {
446 items,
447 lines_span: Some(ir_helpers::LinesSpan { start_line, end_line }),
448 allow_inline_multi_line: false,
449 allow_inline_single_line: false,
450 });
451 }
452 }
453 generated_comments
454 },
455 ir_helpers::GenSeparatedValuesOptions {
456 prefer_hanging: false,
457 force_use_new_lines: !is_single_line,
458 allow_blank_lines: true,
459 single_line_options: ir_helpers::SingleLineOptions {
460 space_at_start: false,
461 space_at_end: false,
462 separator: Signal::SpaceOrNewLine.into(),
463 },
464 indent_width,
465 multi_line_options: ir_helpers::MultiLineOptions::surround_newlines_indented(),
466 force_possible_newline_at_start: false,
467 },
468 )
469 .items,
470 );
471 } else {
472 items.push_signal(Signal::NewLine);
473 items.extend(ir_helpers::with_indent(gen_comments_as_statements(
474 comments.iter(),
475 None,
476 context,
477 )));
478 items.push_signal(Signal::NewLine);
479 }
480 }
481 } else if !is_single_line && !opts.prefer_single_line_when_empty {
482 items.push_signal(Signal::NewLine);
483 }
484 }
485
486 items.push_sc(opts.close_token);
487
488 return items;
489
490 fn gen_first_line_trailing_comment<'a: 'b, 'b>(
491 open_token_start_line: usize,
492 comments: impl Iterator<Item = &'b Comment<'a>>,
493 context: &mut Context,
494 ) -> PrintItems {
495 let mut items = PrintItems::new();
496 let mut comments = comments;
497 if let Some(first_comment) = comments.next()
498 && first_comment.kind() == CommentKind::Line
499 && context.text_info.line_index(first_comment.start()) == open_token_start_line
500 && let Some(generated_comment) = gen_comment(first_comment, context)
501 {
502 items.push_signal(Signal::StartForceNoNewLines);
503 items.push_space();
504 items.extend(generated_comment);
505 items.push_signal(Signal::FinishForceNoNewLines);
506 }
507 items
508 }
509}
510
511fn has_unhandled_comment<'a: 'b, 'b>(
514 mut comments: impl Iterator<Item = &'b Comment<'a>>,
515 context: &mut Context,
516) -> bool {
517 comments.any(|c| !context.has_handled_comment(c))
518}
519
520fn gen_trailing_comments(node: &dyn Ranged, context: &mut Context) -> PrintItems {
521 if let Some(trailing_comments) = context.comments.get(&node.end()) {
522 gen_comments_as_trailing(node, trailing_comments.iter(), context)
523 } else {
524 PrintItems::new()
525 }
526}
527
528fn gen_trailing_comments_as_statements(node: &dyn Ranged, context: &mut Context) -> PrintItems {
529 let unhandled_comments = get_trailing_comments_as_statements(node, context);
530 gen_comments_as_statements(unhandled_comments.into_iter(), Some(node), context)
531}
532
533fn get_trailing_comments_as_statements<'a, 'b>(
534 node: &dyn Ranged,
535 context: &mut Context<'a, 'b>,
536) -> Vec<&'b Comment<'a>> {
537 let mut comments = Vec::new();
538 let node_end_line = context.text_info.line_index(node.end());
539 if let Some(trailing_comments) = context.comments.get(&node.end()) {
540 for comment in trailing_comments.iter() {
541 if !context.has_handled_comment(comment) && node_end_line < context.text_info.line_index(comment.end()) {
542 comments.push(comment);
543 }
544 }
545 }
546 comments
547}
548
549fn gen_comments_as_statements<'a: 'b, 'b>(
550 comments: impl Iterator<Item = &'b Comment<'a>>,
551 last_node: Option<&dyn Ranged>,
552 context: &mut Context<'a, 'b>,
553) -> PrintItems {
554 let mut last_node = last_node;
555 let mut items = PrintItems::new();
556 for comment in comments {
557 if !context.has_handled_comment(comment) {
558 items.extend(gen_comment_based_on_last_node(
559 comment,
560 &last_node,
561 GenCommentBasedOnLastNodeOptions {
562 separate_with_newlines: true,
563 },
564 context,
565 ));
566 last_node = Some(comment);
567 }
568 }
569 items
570}
571
572fn gen_comments_as_leading<'a: 'b, 'b>(
573 node: &dyn Ranged,
574 comments: impl Iterator<Item = &'b Comment<'a>>,
575 context: &mut Context,
576) -> PrintItems {
577 let mut items = PrintItems::new();
578 let comments = comments.filter(|c| !context.has_handled_comment(c)).collect::<Vec<_>>();
579
580 if !comments.is_empty() {
581 let last_comment = comments.last().unwrap();
582 let last_comment_end_line = context.text_info.line_index(last_comment.end());
583 let last_comment_kind = last_comment.kind();
584 items.extend(gen_comment_collection(comments.into_iter(), None, Some(node), context));
585
586 let node_start_line = context.text_info.line_index(node.start());
587 if node_start_line > last_comment_end_line {
588 items.push_signal(Signal::NewLine);
589
590 if node_start_line - 1 > last_comment_end_line {
591 items.push_signal(Signal::NewLine);
592 }
593 } else if last_comment_kind == CommentKind::Block && node_start_line == last_comment_end_line {
594 items.push_signal(Signal::SpaceIfNotTrailing);
595 }
596 }
597
598 items
599}
600
601fn gen_comments_as_trailing<'a: 'b, 'b>(
602 node: &dyn Ranged,
603 comments: impl Iterator<Item = &'b Comment<'a>>,
604 context: &mut Context,
605) -> PrintItems {
606 let node_end_line = context.text_info.line_index(node.end());
608 let trailing_comments_on_same_line = comments
609 .filter(|c| context.text_info.line_index(c.start()) <= node_end_line)
610 .collect::<Vec<_>>();
611
612 let first_unhandled_comment = trailing_comments_on_same_line
613 .iter()
614 .find(|c| !context.has_handled_comment(c));
615 let mut items = PrintItems::new();
616
617 if let Some(Comment::Block(_)) = first_unhandled_comment {
618 items.push_space();
619 }
620
621 items.extend(gen_comment_collection(
622 trailing_comments_on_same_line.into_iter(),
623 Some(node),
624 None,
625 context,
626 ));
627
628 items
629}
630
631fn gen_comment_collection<'a: 'b, 'b>(
632 comments: impl Iterator<Item = &'b Comment<'a>>,
633 last_node: Option<&dyn Ranged>,
634 next_node: Option<&dyn Ranged>,
635 context: &mut Context,
636) -> PrintItems {
637 let mut last_node = last_node;
638 let mut items = PrintItems::new();
639 let next_node_start_line = next_node.map(|n| context.text_info.line_index(n.start()));
640
641 for comment in comments {
642 if !context.has_handled_comment(comment) {
643 items.extend(gen_comment_based_on_last_node(
644 comment,
645 &last_node,
646 GenCommentBasedOnLastNodeOptions {
647 separate_with_newlines: if let Some(next_node_start_line) = next_node_start_line {
648 context.text_info.line_index(comment.start()) != next_node_start_line
649 } else {
650 false
651 },
652 },
653 context,
654 ));
655 last_node = Some(comment);
656 }
657 }
658
659 items
660}
661
662struct GenCommentBasedOnLastNodeOptions {
663 separate_with_newlines: bool,
664}
665
666fn gen_comment_based_on_last_node(
667 comment: &Comment,
668 last_node: &Option<&dyn Ranged>,
669 opts: GenCommentBasedOnLastNodeOptions,
670 context: &mut Context,
671) -> PrintItems {
672 let mut items = PrintItems::new();
673 let mut pushed_ignore_new_lines = false;
674
675 if let Some(last_node) = last_node {
676 let comment_start_line = context.text_info.line_index(comment.start());
677 let last_node_end_line = context.text_info.line_index(last_node.end());
678
679 if opts.separate_with_newlines || comment_start_line > last_node_end_line {
680 items.push_signal(Signal::NewLine);
681
682 if comment_start_line > last_node_end_line + 1 {
683 items.push_signal(Signal::NewLine);
684 }
685 } else if comment.kind() == CommentKind::Line {
686 items.push_signal(Signal::StartForceNoNewLines);
687 items.push_space();
688 pushed_ignore_new_lines = true;
689 } else if last_node.text(context.text).starts_with("/*") {
690 items.push_space();
691 }
692 }
693
694 if let Some(generated_comment) = gen_comment(comment, context) {
695 items.extend(generated_comment);
696 }
697
698 if pushed_ignore_new_lines {
699 items.push_signal(Signal::FinishForceNoNewLines);
700 }
701
702 items
703}
704
705fn gen_comment(comment: &Comment, context: &mut Context) -> Option<PrintItems> {
706 if context.has_handled_comment(comment) {
708 return None;
709 }
710
711 context.mark_comment_handled(comment);
713 Some(match comment {
714 Comment::Block(comment) => ir_helpers::gen_js_like_comment_block(comment.text),
715 Comment::Line(comment) => {
716 ir_helpers::gen_js_like_comment_line(comment.text, context.config.comment_line_force_space_after_slashes)
717 }
718 })
719}
720
721fn has_ignore_comment(node: &dyn Ranged, context: &Context) -> bool {
722 if let Some(last_comment) = context.comments.get(&(node.start())).and_then(|c| c.last()) {
723 ir_helpers::text_has_dprint_ignore(last_comment.text(), &context.config.ignore_node_comment_text)
724 } else {
725 false
726 }
727}
728
729fn should_break_up_single_line(ranged: &impl Ranged, context: &Context) -> bool {
730 let range = ranged.range();
734
735 context.text_info.line_index(range.start) == context.text_info.line_index(range.end)
739 && range.width() > (context.config.line_width * 2) as usize
740}