|
| 1 | +use crate::formatter::render::comments_ast::render_comment_via_ast; |
1 | 2 | use rowan::TextRange; |
2 | 3 |
|
3 | | -use super::doc_comments::{ |
4 | | - normalize_doc_comment_block, normalize_mixed_comment_block, |
5 | | - should_preserve_doc_comment_block_raw, |
6 | | -}; |
7 | 4 | use super::*; |
8 | 5 |
|
9 | 6 | pub(crate) type RenderedTrailingComment = (Vec<DocIR>, TextRange, bool); |
@@ -191,212 +188,11 @@ pub(crate) fn render_comment_with_spacing( |
191 | 188 | comment: &LuaComment, |
192 | 189 | plan: &FormatPlan, |
193 | 190 | ) -> Vec<DocIR> { |
194 | | - if should_preserve_comment_raw(comment) || should_preserve_doc_comment_block_raw(comment) { |
| 191 | + if should_preserve_comment_raw(comment) { |
195 | 192 | return vec![ir::source_node_trimmed(comment.syntax().clone())]; |
196 | 193 | } |
197 | 194 |
|
198 | | - let raw = trim_end_comment_text(comment.syntax().text().to_string()); |
199 | | - let prefix_replacements = collect_comment_line_prefix_replacements(comment, plan); |
200 | | - let normalized_lines = collect_comment_line_spacing_normalized_texts(comment, plan); |
201 | | - let lines = if is_pure_doc_comment_block(&raw) { |
202 | | - normalize_doc_comment_block( |
203 | | - ctx, |
204 | | - comment, |
205 | | - &raw, |
206 | | - &prefix_replacements, |
207 | | - normalized_lines.as_slice(), |
208 | | - ) |
209 | | - } else if contains_doc_comment_line(&raw) { |
210 | | - normalize_mixed_comment_block( |
211 | | - ctx, |
212 | | - comment, |
213 | | - &raw, |
214 | | - &prefix_replacements, |
215 | | - normalized_lines.as_slice(), |
216 | | - ) |
217 | | - } else { |
218 | | - normalize_normal_comment_block(ctx, &raw, &prefix_replacements, normalized_lines.as_slice()) |
219 | | - }; |
220 | | - lines |
221 | | - .into_iter() |
222 | | - .enumerate() |
223 | | - .flat_map(|(index, line)| { |
224 | | - let mut docs = Vec::new(); |
225 | | - if index > 0 { |
226 | | - docs.push(ir::hard_line()); |
227 | | - } |
228 | | - if !line.is_empty() { |
229 | | - docs.push(ir::text(line)); |
230 | | - } |
231 | | - docs |
232 | | - }) |
233 | | - .collect() |
234 | | -} |
235 | | - |
236 | | -fn trim_end_comment_text(mut text: String) -> String { |
237 | | - while matches!(text.chars().last(), Some(' ' | '\t' | '\r' | '\n')) { |
238 | | - text.pop(); |
239 | | - } |
240 | | - text |
241 | | -} |
242 | | - |
243 | | -fn is_pure_doc_comment_block(raw: &str) -> bool { |
244 | | - raw.lines() |
245 | | - .filter(|line| !line.trim().is_empty()) |
246 | | - .all(|line| line.trim_start().starts_with("---")) |
247 | | -} |
248 | | - |
249 | | -fn contains_doc_comment_line(raw: &str) -> bool { |
250 | | - raw.lines() |
251 | | - .filter(|line| !line.trim().is_empty()) |
252 | | - .any(|line| line.trim_start().starts_with("---")) |
253 | | -} |
254 | | - |
255 | | -fn collect_comment_line_prefix_replacements( |
256 | | - comment: &LuaComment, |
257 | | - plan: &FormatPlan, |
258 | | -) -> Vec<Option<String>> { |
259 | | - let mut line_prefixes = Vec::new(); |
260 | | - let mut current_prefix = None; |
261 | | - let mut saw_token_on_line = false; |
262 | | - |
263 | | - for element in comment.syntax().descendants_with_tokens() { |
264 | | - let Some(token) = element.into_token() else { |
265 | | - continue; |
266 | | - }; |
267 | | - |
268 | | - match token.kind().to_token() { |
269 | | - LuaTokenKind::TkWhitespace => {} |
270 | | - LuaTokenKind::TkEndOfLine => { |
271 | | - line_prefixes.push(current_prefix.take()); |
272 | | - saw_token_on_line = false; |
273 | | - } |
274 | | - _ => { |
275 | | - if !saw_token_on_line { |
276 | | - current_prefix = comment_prefix_replacement_for_token(plan, &token); |
277 | | - saw_token_on_line = true; |
278 | | - } |
279 | | - } |
280 | | - } |
281 | | - } |
282 | | - |
283 | | - if saw_token_on_line || current_prefix.is_some() { |
284 | | - line_prefixes.push(current_prefix); |
285 | | - } |
286 | | - |
287 | | - line_prefixes |
288 | | -} |
289 | | - |
290 | | -fn comment_prefix_replacement_for_token( |
291 | | - plan: &FormatPlan, |
292 | | - token: &LuaSyntaxToken, |
293 | | -) -> Option<String> { |
294 | | - match token.kind().to_token() { |
295 | | - LuaTokenKind::TkNormalStart |
296 | | - | LuaTokenKind::TkDocStart |
297 | | - | LuaTokenKind::TkDocContinue |
298 | | - | LuaTokenKind::TkDocContinueOr => Some( |
299 | | - plan.spacing |
300 | | - .token_replace(LuaSyntaxId::from_token(token)) |
301 | | - .unwrap_or(token.text()) |
302 | | - .to_string(), |
303 | | - ), |
304 | | - _ => None, |
305 | | - } |
306 | | -} |
307 | | - |
308 | | -fn normalize_normal_comment_block( |
309 | | - ctx: &FormatContext, |
310 | | - raw: &str, |
311 | | - prefix_replacements: &[Option<String>], |
312 | | - normalized_lines: &[Option<String>], |
313 | | -) -> Vec<String> { |
314 | | - let lines: Vec<_> = raw.lines().collect(); |
315 | | - if lines.len() <= 1 { |
316 | | - return vec![normalize_single_normal_comment_line( |
317 | | - ctx, |
318 | | - raw, |
319 | | - prefix_replacements |
320 | | - .first() |
321 | | - .and_then(|prefix| prefix.as_deref()), |
322 | | - normalized_lines.first().and_then(|line| line.as_deref()), |
323 | | - false, |
324 | | - )]; |
325 | | - } |
326 | | - lines |
327 | | - .into_iter() |
328 | | - .enumerate() |
329 | | - .map(|(index, line)| { |
330 | | - let trimmed = line.trim_start(); |
331 | | - if trimmed.is_empty() { |
332 | | - String::new() |
333 | | - } else { |
334 | | - normalize_single_normal_comment_line( |
335 | | - ctx, |
336 | | - trimmed, |
337 | | - prefix_replacements |
338 | | - .get(index) |
339 | | - .and_then(|prefix| prefix.as_deref()), |
340 | | - normalized_lines.get(index).and_then(|line| line.as_deref()), |
341 | | - true, |
342 | | - ) |
343 | | - } |
344 | | - }) |
345 | | - .collect() |
346 | | -} |
347 | | - |
348 | | -pub(crate) fn normalize_single_normal_comment_line( |
349 | | - ctx: &FormatContext, |
350 | | - line: &str, |
351 | | - prefix_override: Option<&str>, |
352 | | - _normalized_line: Option<&str>, |
353 | | - preserve_extra_gap: bool, |
354 | | -) -> String { |
355 | | - let trimmed = line.trim_start(); |
356 | | - if !trimmed.starts_with("--") || trimmed.starts_with("---") { |
357 | | - return trimmed.to_string(); |
358 | | - } |
359 | | - let body_with_gap = &trimmed[2..]; |
360 | | - let preserved_gap = preserved_dash_gap(body_with_gap); |
361 | | - let prefix = prefix_override.map(str::to_string).unwrap_or_else(|| { |
362 | | - if ctx.config.comments.space_after_comment_dash { |
363 | | - "-- ".to_string() |
364 | | - } else { |
365 | | - "--".to_string() |
366 | | - } |
367 | | - }); |
368 | | - let body = preserved_gap |
369 | | - .as_ref() |
370 | | - .map(|gap| &body_with_gap[gap.len()..]) |
371 | | - .unwrap_or_else(|| body_with_gap.trim_start()); |
372 | | - if preserve_extra_gap && let Some(gap) = preserved_gap { |
373 | | - return if body.is_empty() { |
374 | | - "--".to_string() |
375 | | - } else { |
376 | | - format!("--{gap}{body}") |
377 | | - }; |
378 | | - } |
379 | | - if let Some(_gap) = preserved_gap { |
380 | | - return if body.is_empty() { |
381 | | - "--".to_string() |
382 | | - } else { |
383 | | - format!("{prefix}{body}") |
384 | | - }; |
385 | | - } |
386 | | - if prefix.trim_end() == "--" |
387 | | - && body_with_gap |
388 | | - .chars() |
389 | | - .next() |
390 | | - .is_some_and(char::is_whitespace) |
391 | | - && body.starts_with('[') |
392 | | - { |
393 | | - return format!("-- {body}"); |
394 | | - } |
395 | | - if body.is_empty() { |
396 | | - prefix.trim_end().to_string() |
397 | | - } else { |
398 | | - format!("{prefix}{body}") |
399 | | - } |
| 195 | + render_comment_via_ast(ctx, comment, plan) |
400 | 196 | } |
401 | 197 |
|
402 | 198 | pub(crate) fn render_direct_body_comment( |
@@ -433,155 +229,16 @@ pub(crate) fn comment_is_inline_after_anchor( |
433 | 229 | !text[start..end].chars().any(|ch| matches!(ch, '\n' | '\r')) |
434 | 230 | } |
435 | 231 |
|
436 | | -fn collect_comment_line_spacing_normalized_texts( |
437 | | - comment: &LuaComment, |
438 | | - plan: &FormatPlan, |
439 | | -) -> Vec<Option<String>> { |
440 | | - let mut lines = Vec::new(); |
441 | | - let mut current_line = Vec::new(); |
442 | | - |
443 | | - for element in comment.syntax().descendants_with_tokens() { |
444 | | - let Some(token) = element.into_token() else { |
445 | | - continue; |
446 | | - }; |
447 | | - |
448 | | - match token.kind().to_token() { |
449 | | - LuaTokenKind::TkEndOfLine => { |
450 | | - lines.push(normalize_comment_line_with_spacing(¤t_line, plan)); |
451 | | - current_line.clear(); |
452 | | - } |
453 | | - _ => current_line.push(token), |
454 | | - } |
455 | | - } |
456 | | - |
457 | | - if !current_line.is_empty() { |
458 | | - lines.push(normalize_comment_line_with_spacing(¤t_line, plan)); |
459 | | - } |
460 | | - |
461 | | - lines |
462 | | -} |
463 | | - |
464 | | -fn normalize_comment_line_with_spacing( |
465 | | - tokens: &[LuaSyntaxToken], |
466 | | - plan: &FormatPlan, |
467 | | -) -> Option<String> { |
468 | | - let mut out = String::new(); |
469 | | - let mut previous_token: Option<&LuaSyntaxToken> = None; |
470 | | - let mut saw_whitespace = false; |
471 | | - |
472 | | - for token in tokens { |
473 | | - if token.kind().to_token() == LuaTokenKind::TkWhitespace { |
474 | | - saw_whitespace = !out.is_empty(); |
475 | | - continue; |
476 | | - } |
477 | | - |
478 | | - if !out.is_empty() { |
479 | | - let spacing = |
480 | | - comment_spacing_between_tokens(plan, previous_token, token, saw_whitespace); |
481 | | - out.extend(std::iter::repeat_n(' ', spacing)); |
482 | | - } |
483 | | - |
484 | | - out.push_str(comment_token_text(plan, token)); |
485 | | - previous_token = Some(token); |
486 | | - saw_whitespace = false; |
487 | | - } |
488 | | - |
489 | | - (!out.is_empty()).then_some(out) |
490 | | -} |
491 | | - |
492 | | -fn comment_spacing_between_tokens( |
493 | | - plan: &FormatPlan, |
494 | | - previous_token: Option<&LuaSyntaxToken>, |
495 | | - current_token: &LuaSyntaxToken, |
496 | | - had_source_whitespace: bool, |
497 | | -) -> usize { |
498 | | - if had_source_whitespace && previous_token.is_some_and(is_doc_tag_keyword_token) { |
499 | | - return 1; |
500 | | - } |
501 | | - |
502 | | - if had_source_whitespace |
503 | | - && current_token.kind().to_token() == LuaTokenKind::TkLeftBracket |
504 | | - && previous_token.is_some_and(|token| { |
505 | | - matches!( |
506 | | - token.kind().to_token(), |
507 | | - LuaTokenKind::TkDocVisibility | LuaTokenKind::TkTagVisibility |
508 | | - ) |
509 | | - }) |
510 | | - { |
511 | | - return 1; |
512 | | - } |
513 | | - |
514 | | - let current_id = LuaSyntaxId::from_token(current_token); |
515 | | - if let Some(expected) = plan.spacing.left_expected(current_id) { |
516 | | - return resolve_comment_spacing_expected(expected, had_source_whitespace); |
517 | | - } |
518 | | - |
519 | | - if let Some(previous_token) = previous_token { |
520 | | - let previous_id = LuaSyntaxId::from_token(previous_token); |
521 | | - if let Some(expected) = plan.spacing.right_expected(previous_id) { |
522 | | - return resolve_comment_spacing_expected(expected, had_source_whitespace); |
523 | | - } |
524 | | - } |
525 | | - |
526 | | - usize::from(had_source_whitespace) |
527 | | -} |
528 | | - |
529 | | -fn resolve_comment_spacing_expected( |
530 | | - expected: &TokenSpacingExpected, |
531 | | - had_source_whitespace: bool, |
532 | | -) -> usize { |
533 | | - match expected { |
534 | | - TokenSpacingExpected::Space(count) => *count, |
535 | | - TokenSpacingExpected::MaxSpace(count) => { |
536 | | - if had_source_whitespace { |
537 | | - (*count).min(1) |
538 | | - } else { |
539 | | - 0 |
540 | | - } |
541 | | - } |
542 | | - } |
543 | | -} |
544 | | - |
545 | | -fn comment_token_text<'a>(plan: &'a FormatPlan, token: &'a LuaSyntaxToken) -> &'a str { |
546 | | - plan.spacing |
547 | | - .token_replace(LuaSyntaxId::from_token(token)) |
548 | | - .unwrap_or(token.text()) |
549 | | -} |
550 | | - |
551 | | -fn is_doc_tag_keyword_token(token: &LuaSyntaxToken) -> bool { |
552 | | - matches!( |
553 | | - token.kind().to_token(), |
554 | | - LuaTokenKind::TkTagClass |
555 | | - | LuaTokenKind::TkTagAlias |
556 | | - | LuaTokenKind::TkTagField |
557 | | - | LuaTokenKind::TkTagType |
558 | | - | LuaTokenKind::TkTagParam |
559 | | - | LuaTokenKind::TkTagReturn |
560 | | - | LuaTokenKind::TkTagGeneric |
561 | | - | LuaTokenKind::TkTagOverload |
562 | | - | LuaTokenKind::TkTagVersion |
563 | | - ) |
564 | | -} |
565 | | - |
566 | | -pub(crate) fn preserved_dash_gap(text_after_dash: &str) -> Option<String> { |
567 | | - let gap_len = text_after_dash |
568 | | - .chars() |
569 | | - .take_while(|ch| matches!(ch, ' ' | '\t')) |
570 | | - .count(); |
571 | | - if gap_len > 1 { |
572 | | - Some(text_after_dash[..gap_len].to_string()) |
573 | | - } else { |
574 | | - None |
575 | | - } |
576 | | -} |
577 | | - |
578 | 232 | fn should_preserve_comment_raw(comment: &LuaComment) -> bool { |
579 | 233 | let raw = comment.syntax().text().to_string(); |
580 | 234 | if raw.starts_with("----") { |
581 | 235 | return true; |
582 | 236 | } |
583 | 237 |
|
584 | | - if raw_comment_starts_like_long_comment(raw.as_str()) { |
| 238 | + if raw |
| 239 | + .lines() |
| 240 | + .any(|line| raw_comment_starts_like_long_comment(line.trim_start())) |
| 241 | + { |
585 | 242 | return true; |
586 | 243 | } |
587 | 244 |
|
|
0 commit comments