macro_rules! impl_js_family_doc_binding {
($lang:ident, $hidden:expr, $standalone:path, $target:ident, $after_code:expr) => {
const DOC_BINDING: Option<$crate::lang_helpers::DocBinding> =
Some($crate::lang_helpers::DocBinding {
skips: |node, _| node.kind_id() == $lang::Decorator,
forward: |comment, code, ancestors| {
$crate::lang_helpers::doc_comment::outside_function_bodies::<Self>(
comment, ancestors,
) && !$standalone($crate::lang_helpers::doc_comment::node_bytes(comment, code))
},
..$crate::lang_helpers::DocBinding::new($crate::lang_helpers::KeptDocs::Leading {
hidden: $hidden,
joins: $crate::lang_helpers::doc_comment::never_joins,
after_code: $after_code,
})
});
fn is_doc_target<'a>(node: &Node<'a>, _code: &[u8], ancestors: Ancestors<'a, '_>) -> bool {
$target!($lang, node, ancestors)
}
fn holds_doc_comment_in_body<'a>(
ancestor: &Node<'a>,
ancestors: Ancestors<'a, '_>,
below: &Node<'a>,
) -> bool {
if !Self::is_func(ancestor, ancestors) && !Self::is_closure(ancestor, ancestors) {
return false;
}
if ancestor.kind_id() == $lang::ArrowFunction {
return below.end_byte() == ancestor.end_byte()
&& below.start_byte() != ancestor.start_byte()
&& !Self::is_comment(below);
}
below.kind_id() == $lang::StatementBlock
}
};
}
macro_rules! jsdoc_target {
($lang:ident, $node:ident, $ancestors:ident) => {{
use $lang::*;
let _ = $ancestors;
match $node.kind_id().into() {
FunctionDeclaration
| GeneratorFunctionDeclaration
| ClassDeclaration
| MethodDefinition
| FieldDefinition
| Pair
| ShorthandPropertyIdentifier => true,
VariableDeclaration | LexicalDeclaration | UsingDeclaration => $node
.children()
.find(|c| c.kind_id() == VariableDeclarator)
.and_then(|d| d.child_by_field_name("name"))
.is_some_and(|name| name.kind_id() == Identifier),
ExportStatement => !$node.is_child(ExportClause as u16),
ExpressionStatement => {
let mut expr = $node.child(0);
while let Some(e) = expr.filter(|e| e.kind_id() == ParenthesizedExpression) {
expr = e
.children()
.find(|c| c.is_named() && !c.as_tree_sitter().is_extra());
}
expr.filter(|e| {
matches!(
e.kind_id().into(),
AssignmentExpression | AugmentedAssignmentExpression
)
})
.and_then(|e| e.child_by_field_name("left"))
.is_some_and(|left| !matches!(left.kind_id().into(), ObjectPattern | ArrayPattern))
}
_ => false,
}
}};
}
macro_rules! tsdoc_target {
($lang:ident, $node:ident, $ancestors:ident) => {{
use $lang::*;
let declaration = |node: &Node| match node.kind_id().into() {
FunctionDeclaration
| GeneratorFunctionDeclaration
| ClassDeclaration
| AbstractClassDeclaration
| InterfaceDeclaration
| TypeAliasDeclaration
| EnumDeclaration
| Module
| AmbientDeclaration
| FunctionSignature
| MethodDefinition
| MethodSignature
| AbstractMethodSignature
| PublicFieldDefinition
| PropertySignature
| IndexSignature
| CallSignature
| ConstructSignature
| Pair
| ShorthandPropertyIdentifier
| EnumAssignment
| RequiredParameter
| RequiredParameter2
| OptionalParameter
| OptionalParameter2 => true,
VariableDeclaration | LexicalDeclaration => node
.children()
.find(|c| c.kind_id() == VariableDeclarator)
.and_then(|d| d.child_by_field_name("name"))
.is_some_and(|name| name.kind_id() == Identifier),
ExpressionStatement => node.child(0).is_some_and(|e| {
if e.kind_id() == InternalModule {
return true;
}
let assignment = if e.kind_id() == AwaitExpression {
e.children()
.find(|c| c.is_named() && !c.as_tree_sitter().is_extra())
} else {
Some(e)
};
assignment.is_some_and(|a| {
a.kind_id() == AssignmentExpression
&& a.child(0).is_some_and(|u| u.kind_id() == Using)
&& a.child_by_field_name("left")
.is_some_and(|left| left.kind_id() == Identifier)
})
}),
_ => false,
};
let parent = $ancestors.parent($node).map(|p| p.kind_id().into());
match ($node.kind_id().into(), parent) {
(_, Some(ExportStatement | AmbientDeclaration)) => false,
(ExportStatement, _) => match $node.child_by_field_name("declaration") {
Some(inner) => declaration(&inner) || inner.kind_id() == InternalModule,
None => {
$node.is_child(ExportClause as u16)
|| $node.child_by_field_name("value").is_some_and(|value| {
matches!(
value.kind_id().into(),
FunctionExpression | GeneratorFunction | Class
)
})
}
},
(PropertyIdentifier | ComputedPropertyName, Some(EnumBody)) => true,
(_, Some(EnumBody)) => $node.kind() == "string" || declaration($node),
_ => declaration($node),
}
}};
}