Skip to content

Commit c822bbd

Browse files
committed
Merge remote-tracking branch 'upstream/main' into update
2 parents 19f649a + a2b3531 commit c822bbd

6 files changed

Lines changed: 882 additions & 1544 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
### ✨ Added
88

9-
- **Support LuaJIT-Ext**: Added support for LuaJIT‑Ext syntax, including compound assignment operators, null‑safe navigation, the null‑coalescing operator, constant variables, and the continue statement.
9+
- **Support LuaJIT-Ext**: Added support for LuaJIT‑Ext syntax, including compound assignment operators, null‑safe navigation, the null‑coalescing operator, constant variables statement, the continue statement, the underscore number, and the short function syntax.
1010

1111
- **Support LuaJIT3**: Besides LuaJIT‑Ext syntax, named variadic arguments and integer division are also supported.
1212

crates/emmylua_formatter/src/formatter/render/comments.rs

Lines changed: 7 additions & 350 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1+
use crate::formatter::render::comments_ast::render_comment_via_ast;
12
use rowan::TextRange;
23

3-
use super::doc_comments::{
4-
normalize_doc_comment_block, normalize_mixed_comment_block,
5-
should_preserve_doc_comment_block_raw,
6-
};
74
use super::*;
85

96
pub(crate) type RenderedTrailingComment = (Vec<DocIR>, TextRange, bool);
@@ -191,212 +188,11 @@ pub(crate) fn render_comment_with_spacing(
191188
comment: &LuaComment,
192189
plan: &FormatPlan,
193190
) -> Vec<DocIR> {
194-
if should_preserve_comment_raw(comment) || should_preserve_doc_comment_block_raw(comment) {
191+
if should_preserve_comment_raw(comment) {
195192
return vec![ir::source_node_trimmed(comment.syntax().clone())];
196193
}
197194

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)
400196
}
401197

402198
pub(crate) fn render_direct_body_comment(
@@ -433,155 +229,16 @@ pub(crate) fn comment_is_inline_after_anchor(
433229
!text[start..end].chars().any(|ch| matches!(ch, '\n' | '\r'))
434230
}
435231

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(&current_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(&current_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-
578232
fn should_preserve_comment_raw(comment: &LuaComment) -> bool {
579233
let raw = comment.syntax().text().to_string();
580234
if raw.starts_with("----") {
581235
return true;
582236
}
583237

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+
{
585242
return true;
586243
}
587244

0 commit comments

Comments
 (0)