perf(linter/react/jsx-curly-brace-presence): avoid newline position allocation (#26499)

Track line offsets while splitting multiline JSX text instead of collecting newline positions in a separate vector. Offsets remain byte-based, preserving fixes for Unicode text, blank lines, and trailing newlines.
This commit is contained in:
camc314
2026-09-09 15:09:04 +00:00
parent ed4357c620
commit c068fd2f44
@@ -664,21 +664,25 @@ fn report_missing_curly_for_string_attribute_value(
fn report_missing_curly_for_text_node(ctx: &LintContext, span: Span, string_value: &str) {
ctx.diagnostic_with_fix(jsx_curly_brace_presence_necessary_diagnostic(span), |fixer| {
let fixer = fixer.for_multifix();
let line_matches = string_value.match_indices('\n').map(|(i, _)| i).collect::<Vec<_>>();
let fix_contexts = if line_matches.is_empty() {
let fix_contexts = if string_value.contains('\n') {
string_value
.split('\n')
.scan(0, |line_start, line| {
let contexts = build_missing_curly_fix_context_for_line(
span,
line,
u32::try_from(*line_start).unwrap(),
);
*line_start += line.len() + 1;
Some(contexts)
})
.flatten()
.collect::<Vec<_>>()
} else {
build_missing_curly_fix_context_for_part(span, string_value, 0)
.iter()
.copied()
.collect::<Vec<_>>()
} else {
string_value
.split('\n')
.enumerate()
.flat_map(|(index, line)| {
let line_start = calculate_line_start(line_matches.as_slice(), index);
build_missing_curly_fix_context_for_line(span, line, line_start)
})
.collect::<Vec<_>>()
};
if fix_contexts.is_empty() {
return fixer.noop();
@@ -724,17 +728,6 @@ fn build_missing_curly_fix_context_for_part(
})
}
fn calculate_line_start(line_matches: &[usize], index: usize) -> u32 {
if index == 0 {
0u32
} else {
u32::try_from(
line_matches.get(index - 1).map_or(1usize, |new_line_index| *new_line_index + 1),
)
.unwrap()
}
}
fn calculate_part_start(line_matches: &[usize], index: usize) -> u32 {
if index == 0 {
0u32
@@ -1547,3 +1540,19 @@ fn test() {
.expect_fix(fix)
.test_and_snapshot();
}
#[test]
fn test_multiline_byte_offsets() {
use crate::tester::Tester;
let source = "<App>é\n\n\n</App>";
let config = Some(serde_json::json!([{ "children": "always" }]));
Tester::new(
JsxCurlyBracePresence::NAME,
JsxCurlyBracePresence::PLUGIN,
Vec::<(&str, Option<serde_json::Value>)>::new(),
vec![(source, config.clone())],
)
.expect_fix(vec![(source, "<App>{\"é\"}\n\n{\"\"}\n</App>", config)])
.test();
}