use oxc_span::Span;
use crate::format::extended_data::LIST_METADATA_SIZE;
use crate::format::kind::Kind;
use crate::format::string_field::{STRING_FIELD_SIZE, StringField};
use super::super::{BinaryWriter, ExtOffset, LeafStringPayload};
use super::NodeIndex;
const JSDOC_BLOCK_LIST_COUNT: usize = 3;
pub(crate) const JSDOC_BLOCK_LISTS_BASE: usize = 1 + 1 + 8 * STRING_FIELD_SIZE;
pub const JSDOC_BLOCK_DESC_LINES_SLOT: usize = JSDOC_BLOCK_LISTS_BASE;
pub const JSDOC_BLOCK_TAGS_SLOT: usize = JSDOC_BLOCK_LISTS_BASE + LIST_METADATA_SIZE;
pub const JSDOC_BLOCK_INLINE_TAGS_SLOT: usize = JSDOC_BLOCK_LISTS_BASE + 2 * LIST_METADATA_SIZE;
pub(crate) const JSDOC_BLOCK_BASIC_SIZE: usize =
JSDOC_BLOCK_LISTS_BASE + JSDOC_BLOCK_LIST_COUNT * LIST_METADATA_SIZE;
pub(crate) const JSDOC_BLOCK_COMPAT_SIZE: usize = JSDOC_BLOCK_BASIC_SIZE + 22;
const JSDOC_BLOCK_COMPAT_TAIL_BASE: usize = JSDOC_BLOCK_BASIC_SIZE;
pub(crate) const DESCRIPTION_RAW_SPAN_SIZE: usize = 8;
pub(crate) const JSDOC_BLOCK_HAS_DESCRIPTION_RAW_SPAN_BIT: u8 = 1 << 0;
#[allow(clippy::too_many_arguments)]
pub fn write_jsdoc_block(
writer: &mut BinaryWriter<'_>,
span: Span,
parent_index: u32,
description: Option<StringField>,
delimiter: StringField,
post_delimiter: StringField,
terminal: StringField,
line_end: StringField,
initial: StringField,
delimiter_line_break: StringField,
preterminal_line_break: StringField,
children_bitmask: u8,
description_raw_span: Option<(u32, u32)>,
) -> (NodeIndex, ExtOffset) {
let base_size = if writer.compat_mode() {
JSDOC_BLOCK_COMPAT_SIZE
} else {
JSDOC_BLOCK_BASIC_SIZE
};
let total_size = if description_raw_span.is_some() {
base_size + DESCRIPTION_RAW_SPAN_SIZE
} else {
base_size
};
let (off, dst) = writer.extended.reserve_mut(total_size);
dst[0] = children_bitmask;
dst[1] = 0;
write_opt_string_field(dst, 2, description);
write_string_field(dst, 8, delimiter);
write_string_field(dst, 14, post_delimiter);
write_string_field(dst, 20, terminal);
write_string_field(dst, 26, line_end);
write_string_field(dst, 32, initial);
write_string_field(dst, 38, delimiter_line_break);
write_string_field(dst, 44, preterminal_line_break);
let common_data = if let Some((raw_start, raw_end)) = description_raw_span {
let span_off = total_size - DESCRIPTION_RAW_SPAN_SIZE;
dst[span_off..span_off + 4].copy_from_slice(&raw_start.to_le_bytes());
dst[span_off + 4..span_off + 8].copy_from_slice(&raw_end.to_le_bytes());
JSDOC_BLOCK_HAS_DESCRIPTION_RAW_SPAN_BIT
} else {
0
};
let idx = writer.emit_extended_node(parent_index, Kind::JsdocBlock, common_data, span, off);
(idx, off)
}
#[allow(clippy::too_many_arguments)]
pub fn write_jsdoc_block_compat_tail(
writer: &mut BinaryWriter<'_>,
ext_offset: ExtOffset,
end_line: u32,
description_start_line: Option<u32>,
description_end_line: Option<u32>,
last_description_line: Option<u32>,
has_preterminal_description: u8,
has_preterminal_tag_description: Option<u8>,
) {
debug_assert!(
writer.compat_mode(),
"write_jsdoc_block_compat_tail called but compat_mode is off"
);
let dst = writer
.extended
.slice_mut(ext_offset, JSDOC_BLOCK_COMPAT_SIZE);
let base = JSDOC_BLOCK_COMPAT_TAIL_BASE;
dst[base + 2..base + 6].copy_from_slice(&end_line.to_le_bytes());
dst[base + 6..base + 10]
.copy_from_slice(&opt_u32_sentinel(description_start_line).to_le_bytes());
dst[base + 10..base + 14]
.copy_from_slice(&opt_u32_sentinel(description_end_line).to_le_bytes());
dst[base + 14..base + 18]
.copy_from_slice(&opt_u32_sentinel(last_description_line).to_le_bytes());
dst[base + 18] = has_preterminal_description;
dst[base + 19] = has_preterminal_tag_description.unwrap_or(0xFF);
}
const JSDOC_DESCRIPTION_LINE_COMPAT_SIZE: usize = 4 * STRING_FIELD_SIZE;
pub fn write_jsdoc_description_line(
writer: &mut BinaryWriter<'_>,
span: Span,
parent_index: u32,
description: LeafStringPayload,
) -> NodeIndex {
writer.emit_string_node(
parent_index,
Kind::JsdocDescriptionLine,
0,
span,
description,
)
}
pub fn write_jsdoc_description_line_compat(
writer: &mut BinaryWriter<'_>,
span: Span,
parent_index: u32,
description: StringField,
delimiter: Option<StringField>,
post_delimiter: Option<StringField>,
initial: Option<StringField>,
) -> NodeIndex {
debug_assert!(writer.compat_mode());
let (off, dst) = writer
.extended
.reserve_mut(JSDOC_DESCRIPTION_LINE_COMPAT_SIZE);
write_string_field(dst, 0, description);
write_opt_string_field(dst, 6, delimiter);
write_opt_string_field(dst, 12, post_delimiter);
write_opt_string_field(dst, 18, initial);
writer.emit_extended_node(parent_index, Kind::JsdocDescriptionLine, 0, span, off)
}
const JSDOC_TAG_LIST_COUNT: usize = 3;
pub(crate) const JSDOC_TAG_LISTS_BASE: usize = 1 + 1 + 3 * STRING_FIELD_SIZE;
pub const JSDOC_TAG_TYPE_LINES_SLOT: usize = JSDOC_TAG_LISTS_BASE;
pub const JSDOC_TAG_DESC_LINES_SLOT: usize = JSDOC_TAG_LISTS_BASE + LIST_METADATA_SIZE;
pub const JSDOC_TAG_INLINE_TAGS_SLOT: usize = JSDOC_TAG_LISTS_BASE + 2 * LIST_METADATA_SIZE;
pub(crate) const JSDOC_TAG_BASIC_SIZE: usize =
JSDOC_TAG_LISTS_BASE + JSDOC_TAG_LIST_COUNT * LIST_METADATA_SIZE;
pub(crate) const JSDOC_TAG_COMPAT_SIZE: usize = JSDOC_TAG_BASIC_SIZE + 7 * STRING_FIELD_SIZE;
const JSDOC_TAG_COMPAT_TAIL_BASE: usize = JSDOC_TAG_BASIC_SIZE;
pub(crate) const JSDOC_TAG_HAS_DESCRIPTION_RAW_SPAN_BIT: u8 = 1 << 1;
#[allow(clippy::too_many_arguments)]
pub fn write_jsdoc_tag(
writer: &mut BinaryWriter<'_>,
span: Span,
parent_index: u32,
optional: bool,
default_value: Option<StringField>,
description: Option<StringField>,
raw_body: Option<StringField>,
children_bitmask: u8,
description_raw_span: Option<(u32, u32)>,
) -> (NodeIndex, ExtOffset) {
let base_size = if writer.compat_mode() {
JSDOC_TAG_COMPAT_SIZE
} else {
JSDOC_TAG_BASIC_SIZE
};
let total_size = if description_raw_span.is_some() {
base_size + DESCRIPTION_RAW_SPAN_SIZE
} else {
base_size
};
let (off, dst) = writer.extended.reserve_mut(total_size);
dst[0] = children_bitmask;
dst[1] = 0;
write_opt_string_field(dst, 2, default_value);
write_opt_string_field(dst, 8, description);
write_opt_string_field(dst, 14, raw_body);
let common_data = {
let mut cd = optional as u8;
if let Some((raw_start, raw_end)) = description_raw_span {
let span_off = total_size - DESCRIPTION_RAW_SPAN_SIZE;
dst[span_off..span_off + 4].copy_from_slice(&raw_start.to_le_bytes());
dst[span_off + 4..span_off + 8].copy_from_slice(&raw_end.to_le_bytes());
cd |= JSDOC_TAG_HAS_DESCRIPTION_RAW_SPAN_BIT;
}
cd
};
let idx = writer.emit_extended_node(parent_index, Kind::JsdocTag, common_data, span, off);
(idx, off)
}
#[allow(clippy::too_many_arguments)]
pub fn write_jsdoc_tag_compat_tail(
writer: &mut BinaryWriter<'_>,
ext_offset: ExtOffset,
delimiter: StringField,
post_delimiter: StringField,
post_tag: StringField,
post_type: StringField,
post_name: StringField,
initial: StringField,
line_end: StringField,
) {
debug_assert!(writer.compat_mode());
let dst = writer.extended.slice_mut(ext_offset, JSDOC_TAG_COMPAT_SIZE);
let base = JSDOC_TAG_COMPAT_TAIL_BASE;
write_string_field(dst, base, delimiter);
write_string_field(dst, base + STRING_FIELD_SIZE, post_delimiter);
write_string_field(dst, base + 2 * STRING_FIELD_SIZE, post_tag);
write_string_field(dst, base + 3 * STRING_FIELD_SIZE, post_type);
write_string_field(dst, base + 4 * STRING_FIELD_SIZE, post_name);
write_string_field(dst, base + 5 * STRING_FIELD_SIZE, initial);
write_string_field(dst, base + 6 * STRING_FIELD_SIZE, line_end);
}
pub fn write_jsdoc_tag_name(
writer: &mut BinaryWriter<'_>,
span: Span,
parent_index: u32,
value: LeafStringPayload,
) -> NodeIndex {
writer.emit_string_node(parent_index, Kind::JsdocTagName, 0, span, value)
}
pub fn write_jsdoc_tag_name_value(
writer: &mut BinaryWriter<'_>,
span: Span,
parent_index: u32,
raw: LeafStringPayload,
) -> NodeIndex {
writer.emit_string_node(parent_index, Kind::JsdocTagNameValue, 0, span, raw)
}
pub fn write_jsdoc_type_source(
writer: &mut BinaryWriter<'_>,
span: Span,
parent_index: u32,
raw: LeafStringPayload,
) -> NodeIndex {
writer.emit_string_node(parent_index, Kind::JsdocTypeSource, 0, span, raw)
}
const JSDOC_TYPE_LINE_COMPAT_SIZE: usize = 4 * STRING_FIELD_SIZE;
pub fn write_jsdoc_type_line(
writer: &mut BinaryWriter<'_>,
span: Span,
parent_index: u32,
raw_type: LeafStringPayload,
) -> NodeIndex {
writer.emit_string_node(parent_index, Kind::JsdocTypeLine, 0, span, raw_type)
}
pub fn write_jsdoc_type_line_compat(
writer: &mut BinaryWriter<'_>,
span: Span,
parent_index: u32,
raw_type: StringField,
delimiter: Option<StringField>,
post_delimiter: Option<StringField>,
initial: Option<StringField>,
) -> NodeIndex {
debug_assert!(writer.compat_mode());
let (off, dst) = writer.extended.reserve_mut(JSDOC_TYPE_LINE_COMPAT_SIZE);
write_string_field(dst, 0, raw_type);
write_opt_string_field(dst, 6, delimiter);
write_opt_string_field(dst, 12, post_delimiter);
write_opt_string_field(dst, 18, initial);
writer.emit_extended_node(parent_index, Kind::JsdocTypeLine, 0, span, off)
}
const JSDOC_INLINE_TAG_SIZE: usize = 3 * STRING_FIELD_SIZE;
pub fn write_jsdoc_inline_tag(
writer: &mut BinaryWriter<'_>,
span: Span,
parent_index: u32,
format: u8,
namepath_or_url: Option<StringField>,
text: Option<StringField>,
raw_body: Option<StringField>,
) -> NodeIndex {
let (off, dst) = writer.extended.reserve_mut(JSDOC_INLINE_TAG_SIZE);
write_opt_string_field(dst, 0, namepath_or_url);
write_opt_string_field(dst, 6, text);
write_opt_string_field(dst, 12, raw_body);
writer.emit_extended_node(
parent_index,
Kind::JsdocInlineTag,
format & 0b111,
span,
off,
)
}
const JSDOC_GENERIC_TAG_BODY_SIZE: usize = 1 + 1 + STRING_FIELD_SIZE;
pub fn write_jsdoc_generic_tag_body(
writer: &mut BinaryWriter<'_>,
span: Span,
parent_index: u32,
has_dash_separator: bool,
description: Option<StringField>,
children_bitmask: u8,
) -> NodeIndex {
let (off, dst) = writer.extended.reserve_mut(JSDOC_GENERIC_TAG_BODY_SIZE);
dst[0] = children_bitmask;
dst[1] = 0;
write_opt_string_field(dst, 2, description);
writer.emit_extended_node(
parent_index,
Kind::JsdocGenericTagBody,
has_dash_separator as u8,
span,
off,
)
}
pub fn write_jsdoc_borrows_tag_body(
writer: &mut BinaryWriter<'_>,
span: Span,
parent_index: u32,
children_bitmask: u32,
) -> NodeIndex {
writer.emit_children_node(
parent_index,
Kind::JsdocBorrowsTagBody,
0,
span,
children_bitmask,
)
}
pub fn write_jsdoc_raw_tag_body(
writer: &mut BinaryWriter<'_>,
span: Span,
parent_index: u32,
raw: LeafStringPayload,
) -> NodeIndex {
writer.emit_string_node(parent_index, Kind::JsdocRawTagBody, 0, span, raw)
}
const JSDOC_PARAMETER_NAME_SIZE: usize = 2 * STRING_FIELD_SIZE;
pub fn write_jsdoc_parameter_name(
writer: &mut BinaryWriter<'_>,
span: Span,
parent_index: u32,
optional: bool,
path: StringField,
default_value: Option<StringField>,
) -> NodeIndex {
let (off, dst) = writer.extended.reserve_mut(JSDOC_PARAMETER_NAME_SIZE);
write_string_field(dst, 0, path);
write_opt_string_field(dst, 6, default_value);
writer.emit_extended_node(
parent_index,
Kind::JsdocParameterName,
optional as u8,
span,
off,
)
}
pub fn write_jsdoc_namepath_source(
writer: &mut BinaryWriter<'_>,
span: Span,
parent_index: u32,
raw: LeafStringPayload,
) -> NodeIndex {
writer.emit_string_node(parent_index, Kind::JsdocNamepathSource, 0, span, raw)
}
pub fn write_jsdoc_identifier(
writer: &mut BinaryWriter<'_>,
span: Span,
parent_index: u32,
name: LeafStringPayload,
) -> NodeIndex {
writer.emit_string_node(parent_index, Kind::JsdocIdentifier, 0, span, name)
}
pub fn write_jsdoc_text(
writer: &mut BinaryWriter<'_>,
span: Span,
parent_index: u32,
value: LeafStringPayload,
) -> NodeIndex {
writer.emit_string_node(parent_index, Kind::JsdocText, 0, span, value)
}
#[inline]
fn write_string_field(dst: &mut [u8], offset: usize, field: StringField) {
field.write_le(&mut dst[offset..offset + STRING_FIELD_SIZE]);
}
#[inline]
fn write_opt_string_field(dst: &mut [u8], offset: usize, opt: Option<StringField>) {
let field = opt.unwrap_or(StringField::NONE);
field.write_le(&mut dst[offset..offset + STRING_FIELD_SIZE]);
}
#[inline]
fn opt_u32_sentinel(opt: Option<u32>) -> u32 {
opt.unwrap_or(0xFFFF_FFFF)
}