Skip to content

Commit

Permalink
perf(html): initialize ammonia once, faster anchorizer, reduce iter c…
Browse files Browse the repository at this point in the history
…ollections & vecs (#616)

Co-authored-by: Luca Casonato <[email protected]>
  • Loading branch information
crowlKats and lucacasonato authored Jul 23, 2024
1 parent 80ca340 commit 4fbd014
Show file tree
Hide file tree
Showing 185 changed files with 1,409 additions and 1,288 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ tree-sitter-md = { version = "0.1.7", optional = true }
tree-sitter-rust = { version = "0.20.4", optional = true }
tree-sitter-html = { version = "0.20.0", optional = true }
tree-sitter-bash = { version = "0.20.5", optional = true }
itoa = "1.0.10"

[dev-dependencies]
anyhow = { version = "1.0.58" }
Expand Down
6 changes: 3 additions & 3 deletions examples/ddoc/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,9 @@ async fn run() -> anyhow::Result<()> {
doc_nodes.extend(nodes);
}

doc_nodes.retain(|doc_node| doc_node.kind != DocNodeKind::Import);
doc_nodes.retain(|doc_node| doc_node.kind() != DocNodeKind::Import);
if let Some(filter) = maybe_filter {
doc_nodes = find_nodes_by_name_recursively(doc_nodes, filter.to_string());
doc_nodes = find_nodes_by_name_recursively(doc_nodes, filter);
}

let result = DocPrinter::new(&doc_nodes, true, false);
Expand Down Expand Up @@ -201,7 +201,7 @@ impl HrefResolver for EmptyResolver {
}

fn resolve_source(&self, location: &deno_doc::Location) -> Option<String> {
Some(location.filename.clone())
Some(location.filename.to_string())
}
}

Expand Down
50 changes: 25 additions & 25 deletions src/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,14 @@ pub struct ClassPropertyDef {
pub ts_type: Option<TsTypeDef>,
pub readonly: bool,
pub accessibility: Option<deno_ast::swc::ast::Accessibility>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub decorators: Vec<DecoratorDef>,
#[serde(skip_serializing_if = "<[_]>::is_empty", default)]
pub decorators: Box<[DecoratorDef]>,
pub optional: bool,
pub is_abstract: bool,
pub is_static: bool,
#[serde(skip_serializing_if = "is_false", default)]
pub is_override: bool,
pub name: String,
pub name: Box<str>,
pub location: Location,
}

Expand Down Expand Up @@ -172,7 +172,7 @@ pub struct ClassMethodDef {
pub is_static: bool,
#[serde(skip_serializing_if = "is_false", default)]
pub is_override: bool,
pub name: String,
pub name: Box<str>,
pub kind: deno_ast::swc::ast::MethodKind,
pub function_def: FunctionDef,
pub location: Location,
Expand Down Expand Up @@ -220,24 +220,24 @@ impl Display for ClassMethodDef {
pub struct ClassDef {
#[serde(skip_serializing_if = "Option::is_none", default)]
/// set when the class is a default export and has a name in its declaration
pub def_name: Option<String>,
pub def_name: Option<Box<str>>,
pub is_abstract: bool,
pub constructors: Vec<ClassConstructorDef>,
pub properties: Vec<ClassPropertyDef>,
pub index_signatures: Vec<IndexSignatureDef>,
pub methods: Vec<ClassMethodDef>,
pub extends: Option<String>,
pub implements: Vec<TsTypeDef>,
pub type_params: Vec<TsTypeParamDef>,
pub super_type_params: Vec<TsTypeDef>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub decorators: Vec<DecoratorDef>,
pub constructors: Box<[ClassConstructorDef]>,
pub properties: Box<[ClassPropertyDef]>,
pub index_signatures: Box<[IndexSignatureDef]>,
pub methods: Box<[ClassMethodDef]>,
pub extends: Option<Box<str>>,
pub implements: Box<[TsTypeDef]>,
pub type_params: Box<[TsTypeParamDef]>,
pub super_type_params: Box<[TsTypeDef]>,
#[serde(skip_serializing_if = "<[_]>::is_empty", default)]
pub decorators: Box<[DecoratorDef]>,
}

pub fn class_to_class_def(
parsed_source: &ParsedSource,
class: &deno_ast::swc::ast::Class,
def_name: Option<String>,
def_name: Option<Box<str>>,
) -> (ClassDef, JsDoc) {
use deno_ast::swc::ast::Expr;

Expand All @@ -262,10 +262,10 @@ pub fn class_to_class_def(
}
}

let extends: Option<String> = match &class.super_class {
let extends: Option<Box<str>> = match &class.super_class {
Some(boxed) => {
let expr: &Expr = boxed;
walk_class_extends(expr)
walk_class_extends(expr).map(|s| s.into_boxed_str())
}
None => None,
};
Expand All @@ -274,7 +274,7 @@ pub fn class_to_class_def(
.implements
.iter()
.map(|expr| TsTypeDef::ts_expr_with_type_args(parsed_source, expr))
.collect::<Vec<_>>();
.collect::<Box<[_]>>();

for member in &class.body {
use deno_ast::swc::ast::ClassMember::*;
Expand Down Expand Up @@ -351,7 +351,7 @@ pub fn class_to_class_def(
is_abstract: class_method.is_abstract,
is_static: class_method.is_static,
is_override: class_method.is_override,
name: method_name,
name: method_name.into_boxed_str(),
kind: class_method.kind,
function_def: fn_def,
location: get_location(parsed_source, class_method.start()),
Expand Down Expand Up @@ -388,7 +388,7 @@ pub fn class_to_class_def(
is_static: class_prop.is_static,
is_override: class_prop.is_override,
accessibility: class_prop.accessibility,
name: prop_name,
name: prop_name.into_boxed_str(),
decorators,
location: get_location(parsed_source, class_prop.start()),
};
Expand Down Expand Up @@ -453,10 +453,10 @@ pub fn class_to_class_def(
is_abstract: class.is_abstract,
extends,
implements,
constructors,
properties,
index_signatures,
methods,
constructors: constructors.into_boxed_slice(),
properties: properties.into_boxed_slice(),
index_signatures: index_signatures.into_boxed_slice(),
methods: methods.into_boxed_slice(),
type_params,
super_type_params,
decorators,
Expand Down
2 changes: 1 addition & 1 deletion src/decorators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ impl DecoratorDef {
pub fn decorators_to_defs(
parsed_source: &ParsedSource,
decorators: &[Decorator],
) -> Vec<DecoratorDef> {
) -> Box<[DecoratorDef]> {
decorators
.iter()
.map(|d| DecoratorDef::from_ast_decorator(parsed_source, d))
Expand Down
30 changes: 17 additions & 13 deletions src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,10 @@ impl<'a> DiagnosticsCollector<'a> {
})
// should never happen, but just in case
.unwrap_or_else(|| Location {
filename: referenced_module.specifier().to_string(),
filename: referenced_module
.specifier()
.to_string()
.into_boxed_str(),
line: 1,
col: 0,
byte_index: 0,
Expand Down Expand Up @@ -364,8 +367,9 @@ impl<'a, 'b> DiagnosticDocNodeVisitor<'a, 'b> {
}

if let Some(last_node) = last_node {
if doc_node.name == last_node.name && last_node.function_def.is_some() {
if let Some(current_fn) = &doc_node.function_def {
if doc_node.name == last_node.name && last_node.function_def().is_some()
{
if let Some(current_fn) = &doc_node.function_def() {
if current_fn.has_body {
continue; // it's an overload. Ignore it
}
Expand Down Expand Up @@ -399,29 +403,29 @@ impl<'a, 'b> DiagnosticDocNodeVisitor<'a, 'b> {
return; // skip, we don't do these diagnostics above private nodes
}

if is_js_docable_kind(&doc_node.kind) {
if is_js_docable_kind(&doc_node.kind()) {
self
.diagnostics
.check_missing_js_doc(&doc_node.js_doc, &doc_node.location);
}

if let Some(def) = &doc_node.class_def {
if let Some(def) = &doc_node.class_def() {
self.visit_class_def(def);
}

if let Some(def) = &doc_node.function_def {
if let Some(def) = &doc_node.function_def() {
self.visit_function_def(doc_node, def);
}

if let Some(def) = &doc_node.interface_def {
if let Some(def) = &doc_node.interface_def() {
self.visit_interface_def(def);
}

if let Some(def) = &doc_node.namespace_def {
if let Some(def) = &doc_node.namespace_def() {
self.visit_namespace_def(def);
}

if let Some(def) = &doc_node.variable_def {
if let Some(def) = &doc_node.variable_def() {
self.visit_variable_def(doc_node, def);
}
}
Expand All @@ -439,7 +443,7 @@ impl<'a, 'b> DiagnosticDocNodeVisitor<'a, 'b> {
}

// properties
for prop in &def.properties {
for prop in def.properties.iter() {
if prop.accessibility == Some(Accessibility::Private) {
continue; // don't do diagnostics for private types
}
Expand All @@ -454,7 +458,7 @@ impl<'a, 'b> DiagnosticDocNodeVisitor<'a, 'b> {
}

// index signatures
for sig in &def.index_signatures {
for sig in def.index_signatures.iter() {
self
.diagnostics
.check_missing_js_doc(&sig.js_doc, &sig.location);
Expand All @@ -467,9 +471,9 @@ impl<'a, 'b> DiagnosticDocNodeVisitor<'a, 'b> {

// methods
let mut last_name: Option<&str> = None;
for method in &def.methods {
for method in def.methods.iter() {
if let Some(last_name) = last_name {
if method.name == last_name && method.function_def.has_body {
if &*method.name == last_name && method.function_def.has_body {
continue; // skip, it's the implementation signature
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ pub struct FunctionDef {
pub has_body: bool,
pub is_async: bool,
pub is_generator: bool,
pub type_params: Vec<TsTypeParamDef>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub decorators: Vec<DecoratorDef>,
pub type_params: Box<[TsTypeParamDef]>,
#[serde(skip_serializing_if = "<[_]>::is_empty", default)]
pub decorators: Box<[DecoratorDef]>,
}

pub fn function_to_function_def(
Expand Down Expand Up @@ -58,7 +58,7 @@ pub fn function_to_function_def(
repr: "Promise".to_string(),
kind: Some(crate::ts_type::TsTypeDefKind::TypeRef),
type_ref: Some(crate::ts_type::TsTypeRefDef {
type_params: Some(vec![TsTypeDef::keyword("void")]),
type_params: Some(Box::new([TsTypeDef::keyword("void")])),
type_name: "Promise".to_string(),
}),
..Default::default()
Expand Down
59 changes: 43 additions & 16 deletions src/html/comrak_adapters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ impl SyntaxHighlighterAdapter for HighlightAdapter {
let mut highlighter = syntect::easy::HighlightLines::new(syntax, theme);

match self.highlight_html(
syntect::util::LinesWithEndings::from(&code),
syntect::util::LinesWithEndings::from(code),
|lines, line| {
let regions = highlighter.highlight_line(line, &self.syntax_set)?;
syntect::html::append_highlighted_html_for_styled_line(
Expand All @@ -131,7 +131,7 @@ impl SyntaxHighlighterAdapter for HighlightAdapter {
Err(_) => output.write_all(code.as_bytes())?,
}

self.write_button(output, &code)
self.write_button(output, code)
}

#[cfg(all(feature = "tree-sitter", not(feature = "syntect")))]
Expand Down Expand Up @@ -232,11 +232,40 @@ pub struct ToCEntry {
pub anchor: String,
}

#[derive(Default)]
pub struct Anchorizer {
map: HashMap<String, i32>,
itoa_buffer: itoa::Buffer,
}

impl Anchorizer {
/// Returns a String that has been converted into an anchor using the GFM algorithm.
/// This replaces comrak's implementation to improve the performance.
/// @see https://docs.rs/comrak/latest/comrak/struct.Anchorizer.html#method.anchorize
pub fn anchorize(&mut self, s: &str) -> String {
let mut s = REJECTED_CHARS
.replace_all(&s.to_lowercase(), "")
.replace(' ', "-");

if let Some(count) = self.map.get_mut(&s) {
let a = self.itoa_buffer.format(*count);
s.push('-');
s.push_str(a);

*count += 1;
} else {
self.map.insert(s.clone(), 1);
}

s
}
}

#[derive(Clone)]
pub struct HeadingToCAdapter {
toc: Arc<Mutex<Vec<ToCEntry>>>,
anchorizer: Arc<Mutex<comrak::html::Anchorizer>>,
offset: Arc<Mutex<u8>>,
anchorizer: Arc<Mutex<Anchorizer>>,
}

impl Default for HeadingToCAdapter {
Expand All @@ -249,18 +278,18 @@ impl Default for HeadingToCAdapter {
}
}

lazy_static! {
static ref REJECTED_CHARS: regex::Regex =
regex::Regex::new(r"[^\p{L}\p{M}\p{N}\p{Pc} -]").unwrap();
}

impl HeadingToCAdapter {
pub fn anchorize(&self, content: String) -> String {
pub fn anchorize(&self, content: &str) -> String {
let mut anchorizer = self.anchorizer.lock().unwrap();
anchorizer.anchorize(content.clone())
anchorizer.anchorize(content)
}

pub fn add_entry(
&self,
level: u8,
content: String,
anchor: String,
) -> String {
pub fn add_entry(&self, level: u8, content: &str, anchor: &str) {
let mut toc = self.toc.lock().unwrap();
let mut offset = self.offset.lock().unwrap();

Expand All @@ -269,12 +298,10 @@ impl HeadingToCAdapter {
if toc.last().map_or(true, |toc| toc.content != content) {
toc.push(ToCEntry {
level,
content,
anchor: anchor.clone(),
content: content.to_owned(),
anchor: anchor.to_owned(),
});
}

anchor
}

pub fn render(self) -> Option<String> {
Expand Down Expand Up @@ -322,7 +349,7 @@ impl HeadingAdapter for HeadingToCAdapter {
let mut anchorizer = self.anchorizer.lock().unwrap();
let offset = self.offset.lock().unwrap();

let anchor = anchorizer.anchorize(heading.content.clone());
let anchor = anchorizer.anchorize(&heading.content);
writeln!(output, r#"<h{} id="{anchor}">"#, heading.level)?;

let mut toc = self.toc.lock().unwrap();
Expand Down
Loading

0 comments on commit 4fbd014

Please sign in to comment.