use tree_sitter::Node;
use crate::cop::layout::report;
use crate::cop::shared;
use crate::cop::Cop;
use crate::correction::Correction;
use crate::diagnostic::Diagnostic;
use crate::parse::source::SourceFile;
pub fn check_first_break(
cop: &dyn Cop,
source: &SourceFile,
node: Node<'_>,
message: &str,
diagnostics: &mut Vec<Diagnostic>,
corrections: &mut Option<&mut Vec<Correction>>,
) {
check_first_break_cfg(cop, source, node, 1, message, diagnostics, corrections, false);
}
pub fn check_first_break_min(
cop: &dyn Cop,
source: &SourceFile,
node: Node<'_>,
min_elems: usize,
message: &str,
diagnostics: &mut Vec<Diagnostic>,
corrections: &mut Option<&mut Vec<Correction>>,
) {
check_first_break_cfg(
cop,
source,
node,
min_elems,
message,
diagnostics,
corrections,
false,
);
}
fn uses_parens(source: &SourceFile, node: Node<'_>, first: Node<'_>) -> bool {
let start = node.start_byte();
let limit = first.start_byte();
limit >= start && source.as_bytes()[start..limit].contains(&b'(')
}
fn span_end_line(source: &SourceFile, elems: &[Node<'_>], node: Node<'_>, allow_final: bool) -> usize {
if allow_final {
elems
.iter()
.map(|e| shared::node_line(source, *e))
.max()
.unwrap_or_else(|| shared::node_line(source, node))
} else {
source.offset_to_line_col(node.end_byte().saturating_sub(1)).0
}
}
fn non_comment_kids<'a>(node: Node<'a>) -> Vec<Node<'a>> {
let mut cur = node.walk();
node.named_children(&mut cur)
.filter(|n| n.kind() != "comment")
.collect()
}
fn first_break_target<'a>(
source: &SourceFile,
node: Node<'a>,
min_elems: usize,
allow_multiline_final: bool,
) -> Option<Node<'a>> {
let elems = non_comment_kids(node);
let first = *elems.first()?;
if elems.len() < min_elems || !uses_parens(source, node, first) {
return None;
}
if !first_on_call_start_line(source, node, first) {
return None;
}
if call_start_line(source, node) == span_end_line(source, &elems, node, allow_multiline_final) {
return None;
}
Some(first)
}
fn call_start_line(source: &SourceFile, node: Node<'_>) -> usize {
node.parent()
.map(|p| shared::node_line(source, p))
.unwrap_or_else(|| shared::node_line(source, node))
}
fn first_on_call_start_line(source: &SourceFile, node: Node<'_>, first: Node<'_>) -> bool {
call_start_line(source, node) == shared::node_line(source, first)
}
pub fn check_first_break_cfg(
cop: &dyn Cop,
source: &SourceFile,
node: Node<'_>,
min_elems: usize,
message: &str,
diagnostics: &mut Vec<Diagnostic>,
corrections: &mut Option<&mut Vec<Correction>>,
allow_multiline_final: bool,
) {
let Some(first) = first_break_target(source, node, min_elems, allow_multiline_final) else {
return;
};
report::report_fix(
cop,
source,
first.start_byte(),
message.into(),
diagnostics,
corrections,
first.start_byte(),
first.start_byte(),
"\n".into(),
);
}