mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 20:32:10 +00:00
Avoid the `insert!` macro in AST codegen. Use doc comments starting with special symbol `@` instead.
* Before: `insert!("// plain comment");`
* After: `///@ plain comment`
* Or: `//!@ plain comment`
Either `///@` or `//!@` is converted to plain `//` in output.
`//!@` is legal in top-of-file position, which allows us to inline `#![allow(...)]` attributes, which in my opinion makes the generators a bit easier to read.
121 lines
3.5 KiB
Rust
121 lines
3.5 KiB
Rust
use std::process::Command;
|
|
|
|
use lazy_static::lazy_static;
|
|
use proc_macro2::TokenStream;
|
|
use regex::{Captures, Regex, Replacer};
|
|
use syn::parse_file;
|
|
|
|
static INSERT_MACRO_IDENT: &str = "insert";
|
|
static ENDL_MACRO_IDENT: &str = "endl";
|
|
static WHITE_SPACES: &str = " \t";
|
|
|
|
/// Replace `insert!` macro calls with the contents of the `insert!`.
|
|
///
|
|
/// e.g. `insert!("#![allow(dead_code)]")` is replaced by `#![allow(dead_code)]`.
|
|
///
|
|
/// We use this when inserting outer attributes (`#![allow(unused)]`) or plain comments (`//` not `///`).
|
|
/// `quote!` macro ignores plain comments, so it's not possible to produce them otherwise.
|
|
#[allow(dead_code)] // `insert!` macro is not currently used
|
|
struct InsertReplacer;
|
|
|
|
impl Replacer for InsertReplacer {
|
|
fn replace_append(&mut self, caps: &Captures, dst: &mut String) {
|
|
assert_eq!(caps.len(), 2);
|
|
let arg = caps.get(1);
|
|
if let Some(arg) = arg {
|
|
dst.push_str(arg.as_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
lazy_static! {
|
|
static ref INSERT_REGEX: Regex = Regex::new(
|
|
format!(
|
|
r#"(?m)^[{WHITE_SPACES}]*{INSERT_MACRO_IDENT}!\([\n\s\S]*?\"([\n\s\S]*?)\"[\n\s\S]*?\);$"#
|
|
)
|
|
.as_str()
|
|
)
|
|
.unwrap();
|
|
}
|
|
|
|
/// Remove `endl!();`, so it produces a line break.
|
|
///
|
|
/// e.g.:
|
|
/// ```
|
|
/// use oxc_allocator::Allocator;
|
|
/// endl!();
|
|
/// use oxc_ast::*;
|
|
/// ```
|
|
/// becomes:
|
|
/// ```
|
|
/// use oxc_allocator::Allocator;
|
|
///
|
|
/// use oxc_ast::*;
|
|
/// ```
|
|
///
|
|
/// We use `endl!();` because `quote!` macro ignores whitespace,
|
|
/// so we have to use another means to generate line breaks.
|
|
struct EndlReplacer;
|
|
|
|
impl Replacer for EndlReplacer {
|
|
fn replace_append(&mut self, _: &Captures, _: &mut String) {}
|
|
}
|
|
|
|
lazy_static! {
|
|
static ref ENDL_REGEX: Regex =
|
|
Regex::new(format!(r"[{WHITE_SPACES}]*{ENDL_MACRO_IDENT}!\(\);").as_str()).unwrap();
|
|
}
|
|
|
|
/// Replace doc comments which start with `@` with plain comments or line breaks.
|
|
///
|
|
/// Original comment can be either `///@` or `//!@`.
|
|
///
|
|
/// * `///@ foo` becomes `// foo`.
|
|
/// * `//!@ foo` becomes `// foo`.
|
|
/// * `///@@` is removed - i.e. line break.
|
|
/// * `//!@@` is removed - i.e. line break.
|
|
///
|
|
/// `quote!` macro ignores plain comments, but we can use these to generate plain comments
|
|
/// in generated code.
|
|
///
|
|
/// To dynamically generate a comment:
|
|
/// ```
|
|
/// let comment = format!("@ NOTE: {} doesn't exist!", name);
|
|
/// quote!(#[doc = #comment])
|
|
/// // or `quote!(#![doc = #comment])`
|
|
/// ```
|
|
///
|
|
/// `//!@@` is particularly useful for inserting a line break in a position where `endl!();`
|
|
/// is not valid syntax e.g. before an `#![allow(...)]`.
|
|
struct CommentReplacer;
|
|
|
|
impl Replacer for CommentReplacer {
|
|
fn replace_append(&mut self, caps: &Captures, dst: &mut String) {
|
|
assert_eq!(caps.len(), 2);
|
|
let body = caps.get(1).unwrap().as_str();
|
|
if body != "@" {
|
|
dst.push_str("//");
|
|
dst.push_str(body);
|
|
}
|
|
}
|
|
}
|
|
|
|
lazy_static! {
|
|
static ref COMMENT_REGEX: Regex =
|
|
Regex::new(format!(r"[{WHITE_SPACES}]*//[/!]@(.*)").as_str()).unwrap();
|
|
}
|
|
|
|
/// Pretty print
|
|
pub fn pprint(input: &TokenStream) -> String {
|
|
let result = prettyplease::unparse(&parse_file(input.to_string().as_str()).unwrap());
|
|
let result = ENDL_REGEX.replace_all(&result, EndlReplacer);
|
|
// `insert!` macro is not currently used
|
|
// let result = INSERT_REGEX.replace_all(&result, InsertReplacer).to_string();
|
|
let result = COMMENT_REGEX.replace_all(&result, CommentReplacer).to_string();
|
|
result
|
|
}
|
|
|
|
/// Run `cargo fmt`
|
|
pub fn cargo_fmt() {
|
|
Command::new("cargo").arg("fmt").status().unwrap();
|
|
}
|