mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
refactor(semantic): use Comment::attached_to for jsdoc attachment (#5876)
I can't believe tests passed on my first try 🤔
This commit is contained in:
parent
db4f16a98f
commit
9115dd947a
3 changed files with 33 additions and 69 deletions
|
|
@ -145,7 +145,7 @@ impl<'a> SemanticBuilder<'a> {
|
||||||
module_record: Arc::new(ModuleRecord::default()),
|
module_record: Arc::new(ModuleRecord::default()),
|
||||||
unused_labels: UnusedLabels::default(),
|
unused_labels: UnusedLabels::default(),
|
||||||
build_jsdoc: false,
|
build_jsdoc: false,
|
||||||
jsdoc: JSDocBuilder::new(source_text, trivias),
|
jsdoc: JSDocBuilder::new(source_text, &trivias),
|
||||||
stats: None,
|
stats: None,
|
||||||
excess_capacity: 0.0,
|
excess_capacity: 0.0,
|
||||||
check_syntax_error: false,
|
check_syntax_error: false,
|
||||||
|
|
@ -178,7 +178,7 @@ impl<'a> SemanticBuilder<'a> {
|
||||||
/// `with_trivias` must be called prior to this call.
|
/// `with_trivias` must be called prior to this call.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_build_jsdoc(mut self, yes: bool) -> Self {
|
pub fn with_build_jsdoc(mut self, yes: bool) -> Self {
|
||||||
self.jsdoc = JSDocBuilder::new(self.source_text, self.trivias.clone());
|
self.jsdoc = JSDocBuilder::new(self.source_text, &self.trivias);
|
||||||
self.build_jsdoc = yes;
|
self.build_jsdoc = yes;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
use std::collections::BTreeMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use rustc_hash::FxHashSet;
|
|
||||||
|
|
||||||
use oxc_ast::{AstKind, Comment, Trivias};
|
use oxc_ast::{AstKind, Comment, Trivias};
|
||||||
use oxc_span::{GetSpan, Span};
|
use oxc_span::{GetSpan, Span};
|
||||||
|
|
@ -10,34 +8,30 @@ use crate::jsdoc::JSDocFinder;
|
||||||
use super::parser::JSDoc;
|
use super::parser::JSDoc;
|
||||||
|
|
||||||
pub struct JSDocBuilder<'a> {
|
pub struct JSDocBuilder<'a> {
|
||||||
source_text: &'a str,
|
not_attached_docs: FxHashMap<u32, Vec<JSDoc<'a>>>,
|
||||||
trivias: Trivias,
|
attached_docs: FxHashMap<u32, Vec<JSDoc<'a>>>,
|
||||||
attached_docs: BTreeMap<Span, Vec<JSDoc<'a>>>,
|
|
||||||
leading_comments_seen: FxHashSet<u32>,
|
|
||||||
/// End span of the previous successful comment search.
|
|
||||||
previous_span_end: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> JSDocBuilder<'a> {
|
impl<'a> JSDocBuilder<'a> {
|
||||||
pub fn new(source_text: &'a str, trivias: Trivias) -> Self {
|
pub fn new(source_text: &'a str, trivias: &Trivias) -> Self {
|
||||||
Self {
|
let mut not_attached_docs: FxHashMap<u32, Vec<_>> = FxHashMap::default();
|
||||||
source_text,
|
for comment in trivias
|
||||||
trivias,
|
.comments()
|
||||||
attached_docs: BTreeMap::default(),
|
.filter(|comment| comment.is_leading() && comment.is_jsdoc(source_text))
|
||||||
leading_comments_seen: FxHashSet::default(),
|
{
|
||||||
previous_span_end: 0,
|
not_attached_docs
|
||||||
|
.entry(comment.attached_to)
|
||||||
|
.or_default()
|
||||||
|
.push(Self::parse_jsdoc_comment(comment, source_text));
|
||||||
}
|
}
|
||||||
|
Self { not_attached_docs, attached_docs: FxHashMap::default() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> JSDocFinder<'a> {
|
pub fn build(self) -> JSDocFinder<'a> {
|
||||||
let not_attached_docs = self
|
JSDocFinder::new(
|
||||||
.trivias
|
self.attached_docs,
|
||||||
.comments()
|
self.not_attached_docs.into_iter().flat_map(|(_, v)| v).collect::<Vec<_>>(),
|
||||||
.filter(|comment| !self.leading_comments_seen.contains(&comment.span.start))
|
)
|
||||||
.filter_map(|comment| self.parse_if_jsdoc_comment(comment))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
JSDocFinder::new(self.attached_docs, not_attached_docs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ## Current architecture
|
// ## Current architecture
|
||||||
|
|
@ -116,51 +110,21 @@ impl<'a> JSDocBuilder<'a> {
|
||||||
// If one day we want to add a performance-affecting kind,
|
// If one day we want to add a performance-affecting kind,
|
||||||
// we might as well give up pre-flagging architecture itself?
|
// we might as well give up pre-flagging architecture itself?
|
||||||
pub fn retrieve_attached_jsdoc(&mut self, kind: &AstKind<'a>) -> bool {
|
pub fn retrieve_attached_jsdoc(&mut self, kind: &AstKind<'a>) -> bool {
|
||||||
if !should_attach_jsdoc(kind) {
|
if should_attach_jsdoc(kind) {
|
||||||
return false;
|
let start = kind.span().start;
|
||||||
}
|
if let Some(docs) = self.not_attached_docs.remove(&start) {
|
||||||
|
self.attached_docs.insert(start, docs);
|
||||||
let span = kind.span();
|
return true;
|
||||||
let comments_range = self.trivias.comments_range(self.previous_span_end..span.start);
|
|
||||||
let comments_len = comments_range.size_hint().1;
|
|
||||||
let mut leading_jsdoc_comments = Vec::with_capacity(comments_len.unwrap_or(0));
|
|
||||||
|
|
||||||
for comment in comments_range {
|
|
||||||
if self.leading_comments_seen.contains(&comment.span.start) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.leading_comments_seen.insert(comment.span.start);
|
|
||||||
if let Some(jsdoc) = self.parse_if_jsdoc_comment(comment) {
|
|
||||||
leading_jsdoc_comments.push(jsdoc);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
false
|
||||||
if leading_jsdoc_comments.is_empty() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
leading_jsdoc_comments.shrink_to_fit();
|
|
||||||
self.attached_docs.insert(span, leading_jsdoc_comments);
|
|
||||||
self.previous_span_end = span.end;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_if_jsdoc_comment(&self, comment: &Comment) -> Option<JSDoc<'a>> {
|
|
||||||
if !comment.is_block() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inside of marker: /*CONTENT*/ => CONTENT
|
|
||||||
let comment_content = comment.span.source_text(self.source_text);
|
|
||||||
// Should start with "*"
|
|
||||||
if !comment_content.starts_with('*') {
|
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_jsdoc_comment(comment: &Comment, source_text: &'a str) -> JSDoc<'a> {
|
||||||
// Remove the very first `*`
|
// Remove the very first `*`
|
||||||
let jsdoc_span = Span::new(comment.span.start + 1, comment.span.end);
|
let jsdoc_span = Span::new(comment.span.start + 1, comment.span.end);
|
||||||
Some(JSDoc::new(&comment_content[1..], jsdoc_span))
|
let comment_content = jsdoc_span.source_text(source_text);
|
||||||
|
JSDoc::new(comment_content, jsdoc_span)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::collections::BTreeMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use oxc_span::{GetSpan, Span};
|
use oxc_span::{GetSpan, Span};
|
||||||
|
|
||||||
|
|
@ -9,12 +9,12 @@ use super::parser::JSDoc;
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct JSDocFinder<'a> {
|
pub struct JSDocFinder<'a> {
|
||||||
/// JSDocs by Span
|
/// JSDocs by Span
|
||||||
attached: BTreeMap<Span, Vec<JSDoc<'a>>>,
|
attached: FxHashMap<u32, Vec<JSDoc<'a>>>,
|
||||||
not_attached: Vec<JSDoc<'a>>,
|
not_attached: Vec<JSDoc<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> JSDocFinder<'a> {
|
impl<'a> JSDocFinder<'a> {
|
||||||
pub fn new(attached: BTreeMap<Span, Vec<JSDoc<'a>>>, not_attached: Vec<JSDoc<'a>>) -> Self {
|
pub fn new(attached: FxHashMap<u32, Vec<JSDoc<'a>>>, not_attached: Vec<JSDoc<'a>>) -> Self {
|
||||||
Self { attached, not_attached }
|
Self { attached, not_attached }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,7 +36,7 @@ impl<'a> JSDocFinder<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all_by_span<'b>(&'b self, span: Span) -> Option<Vec<JSDoc<'a>>> {
|
pub fn get_all_by_span<'b>(&'b self, span: Span) -> Option<Vec<JSDoc<'a>>> {
|
||||||
self.attached.get(&span).cloned()
|
self.attached.get(&span.start).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_all<'b>(&'b self) -> impl Iterator<Item = &JSDoc<'a>> + 'b {
|
pub fn iter_all<'b>(&'b self) -> impl Iterator<Item = &JSDoc<'a>> + 'b {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue