Skip to content

Copy doc comments from Rust to JS #265

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jun 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ matrix:
(cd examples/char && sed -i 's/: "webpack-dev-server"/: "webpack"/' package.json && ./build.sh)
- |
(cd examples/closures && sed -i 's/: "webpack-dev-server"/: "webpack"/' package.json && ./build.sh)
- |
(cd examples/comments && sed -i 's/: "webpack-dev-server"/: "webpack"/' package.json && ./build.sh)


install:
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ members = [
"examples/asm.js",
"examples/char",
"examples/import_js",
"examples/comments"
]

[profile.release]
Expand Down
45 changes: 45 additions & 0 deletions crates/backend/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub struct Export {
pub mutable: bool,
pub constructor: Option<String>,
pub function: Function,
pub comments: Vec<String>,
}

#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
Expand Down Expand Up @@ -82,6 +83,7 @@ pub struct Function {
pub struct Struct {
pub name: Ident,
pub fields: Vec<StructField>,
pub comments: Vec<String>,
}

#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
Expand All @@ -92,12 +94,14 @@ pub struct StructField {
pub ty: syn::Type,
pub getter: Ident,
pub setter: Ident,
pub comments: Vec<String>,
}

#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
pub struct Enum {
pub name: Ident,
pub variants: Vec<Variant>,
pub comments: Vec<String>,
}

#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
Expand Down Expand Up @@ -150,13 +154,15 @@ impl Program {
}
_ => {}
}
let comments = extract_doc_comments(&f.attrs);
f.to_tokens(tokens);
self.exports.push(Export {
class: None,
method: false,
mutable: false,
constructor: None,
function: Function::from(f, opts),
comments,
});
}
syn::Item::Struct(mut s) => {
Expand Down Expand Up @@ -237,6 +243,7 @@ impl Program {
}

let opts = BindgenAttrs::find(&mut method.attrs);
let comments = extract_doc_comments(&method.attrs);
let is_constructor = opts.constructor();
let constructor = if is_constructor {
Some(method.sig.ident.to_string())
Expand All @@ -259,6 +266,7 @@ impl Program {
mutable: mutable.unwrap_or(false),
constructor,
function,
comments,
});
}

Expand Down Expand Up @@ -299,9 +307,11 @@ impl Program {
}
})
.collect();
let comments = extract_doc_comments(&item.attrs);
self.enums.push(Enum {
name: item.ident,
variants,
comments,
});
}

Expand Down Expand Up @@ -585,6 +595,7 @@ impl Export {
method: self.method,
constructor: self.constructor.clone(),
function: self.function.shared(),
comments: self.comments.clone(),
}
}
}
Expand All @@ -594,6 +605,7 @@ impl Enum {
shared::Enum {
name: self.name.to_string(),
variants: self.variants.iter().map(|v| v.shared()).collect(),
comments: self.comments.clone(),
}
}
}
Expand Down Expand Up @@ -750,26 +762,31 @@ impl Struct {
let getter = shared::struct_field_get(&ident, &name_str);
let setter = shared::struct_field_set(&ident, &name_str);
let opts = BindgenAttrs::find(&mut field.attrs);
let comments = extract_doc_comments(&field.attrs);
fields.push(StructField {
opts,
name: name.clone(),
struct_name: s.ident.clone(),
ty: field.ty.clone(),
getter: Ident::new(&getter, Span::call_site()),
setter: Ident::new(&setter, Span::call_site()),
comments
});
}
}
let comments: Vec<String> = extract_doc_comments(&s.attrs);
Struct {
name: s.ident.clone(),
fields,
comments,
}
}

fn shared(&self) -> shared::Struct {
shared::Struct {
name: self.name.to_string(),
fields: self.fields.iter().map(|s| s.shared()).collect(),
comments: self.comments.clone(),
}
}
}
Expand All @@ -779,6 +796,7 @@ impl StructField {
shared::StructField {
name: self.name.to_string(),
readonly: self.opts.readonly(),
comments: self.comments.clone(),
}
}
}
Expand Down Expand Up @@ -1072,3 +1090,30 @@ fn replace_self(name: &Ident, item: &mut syn::ImplItem) {

syn::visit_mut::VisitMut::visit_impl_item_mut(&mut Walk(name), item);
}

/// Extract the documentation comments from a Vec of attributes
fn extract_doc_comments(attrs: &[syn::Attribute]) -> Vec<String> {
attrs
.iter()
.filter_map(|a| {
// if the path segments include an ident of "doc" we know this
// this is a doc comment
if a.path.segments.iter().any(|s| s.ident.to_string() == "doc") {
Some(
// We want to filter out any Puncts so just grab the Literals
a.tts.clone().into_iter().filter_map(|t| match t {
TokenTree::Literal(lit) => {
// this will always return the quoted string, we deal with
// that in the cli when we read in the comments
Some(lit.to_string())
},
_ => None,
})
)
} else {
None
}
})
//Fold up the [[String]] iter we created into Vec<String>
.fold(vec![], |mut acc, a| {acc.extend(a); acc})
}
57 changes: 36 additions & 21 deletions crates/cli-support/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@ pub struct Context<'a> {

#[derive(Default)]
pub struct ExportedClass {
comments: String,
contents: String,
typescript: String,
constructor: Option<String>,
fields: Vec<ClassField>,
}

struct ClassField {
comments: String,
name: String,
readonly: bool,
}
Expand All @@ -52,9 +54,12 @@ pub struct SubContext<'a, 'b: 'a> {
}

impl<'a> Context<'a> {
fn export(&mut self, name: &str, contents: &str) {
fn export(&mut self, name: &str, contents: &str, comments: Option<String>) {
let contents = deindent(contents);
let contents = contents.trim();
if let Some(ref c) = comments {
self.globals.push_str(c);
}
let global = if self.config.nodejs {
if contents.starts_with("class") {
format!("{1}\nmodule.exports.{0} = {0};\n", name, contents)
Expand Down Expand Up @@ -396,7 +401,7 @@ impl<'a> Context<'a> {
.with_context(|_| {
format!("failed to generate internal JS function `{}`", name)
})?;
self.export(name, &contents);
self.export(name, &contents, None);
Ok(())
}

Expand Down Expand Up @@ -461,7 +466,7 @@ impl<'a> Context<'a> {
function(ptr) {{
return addHeapObject({}.__construct(ptr));
}}
", name));
", name), None);
}

for field in class.fields.iter() {
Expand All @@ -484,7 +489,10 @@ impl<'a> Context<'a> {
.method(true)
.ret(&Some(descriptor))?
.finish("", &format!("wasm.{}", wasm_getter));

if !dst.ends_with("\n") {
dst.push_str("\n");
}
dst.push_str(&field.comments);
dst.push_str("get ");
dst.push_str(&field.name);
dst.push_str(&get);
Expand All @@ -504,13 +512,12 @@ impl<'a> Context<'a> {
}}
", shared::free_function(&name)));
ts_dst.push_str("free(): void;\n");

dst.push_str(&class.contents);
ts_dst.push_str(&class.typescript);
dst.push_str("}\n");
ts_dst.push_str("}\n");

self.export(&name, &dst);
self.export(&name, &dst, Some(class.comments.clone()));
self.typescript.push_str(&ts_dst);

Ok(())
Expand All @@ -534,7 +541,7 @@ impl<'a> Context<'a> {

fn rewrite_imports(&mut self, module_name: &str) {
for (name, contents) in self._rewrite_imports(module_name) {
self.export(&name, &contents);
self.export(&name, &contents, None);
}
}

Expand Down Expand Up @@ -691,7 +698,7 @@ impl<'a> Context<'a> {
return;
throw new Error('stack is not currently empty');
}
");
", None);
}
}

Expand All @@ -715,7 +722,7 @@ impl<'a> Context<'a> {
throw new Error('slab is not currently empty');
}}
}}
", initial_values.len()));
", initial_values.len()), None);
}
}

Expand Down Expand Up @@ -1406,7 +1413,7 @@ impl<'a> Context<'a> {

// Ensure a blank line between adjacent items, and ensure everything is
// terminated with a newline.
while !self.globals.ends_with("\n\n\n") {
while !self.globals.ends_with("\n\n\n") && !self.globals.ends_with("*/\n") {
self.globals.push_str("\n");
}
self.globals.push_str(s);
Expand Down Expand Up @@ -1452,14 +1459,16 @@ impl<'a, 'b> SubContext<'a, 'b> {
self.generate_enum(e);
}
for s in self.program.structs.iter() {
self.cx.exported_classes
let mut class = self.cx.exported_classes
.entry(s.name.clone())
.or_insert_with(Default::default)
.fields
.extend(s.fields.iter().map(|s| {
.or_insert_with(Default::default);
class.comments = format_doc_comments(&s.comments);
class.fields
.extend(s.fields.iter().map(|f| {
ClassField {
name: s.name.clone(),
readonly: s.readonly,
name: f.name.clone(),
readonly: f.readonly,
comments: format_doc_comments(&f.comments),
}
}));
}
Expand All @@ -1477,7 +1486,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
let (js, ts) = Js2Rust::new(&export.function.name, self.cx)
.process(descriptor.unwrap_function())?
.finish("function", &format!("wasm.{}", export.function.name));
self.cx.export(&export.function.name, &js);
self.cx.export(&export.function.name, &js, Some(format_doc_comments(&export.comments)));
self.cx.globals.push_str("\n");
self.cx.typescript.push_str("export ");
self.cx.typescript.push_str(&ts);
Expand All @@ -1498,6 +1507,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
.finish("", &format!("wasm.{}", wasm_name));
let class = self.cx.exported_classes.entry(class_name.to_string())
.or_insert(ExportedClass::default());
class.contents.push_str(&format_doc_comments(&export.comments));
if !export.method {
class.contents.push_str("static ");
class.typescript.push_str("static ");
Expand All @@ -1514,7 +1524,6 @@ impl<'a, 'b> SubContext<'a, 'b> {
1 => Some(constructors[0].clone()),
x @ _ => bail!("there must be only one constructor, not {}", x),
};

class.contents.push_str(&export.function.name);
class.contents.push_str(&js);
class.contents.push_str("\n");
Expand Down Expand Up @@ -1593,7 +1602,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
function() {{
return addHeapObject({});
}}
", obj));
", obj), None);
Ok(())
}

Expand Down Expand Up @@ -1688,7 +1697,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
.catch(import.catch)
.process(descriptor.unwrap_function())?
.finish(&target);
self.cx.export(&import.shim, &js);
self.cx.export(&import.shim, &js, None);
Ok(())
}

Expand All @@ -1698,7 +1707,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
for variant in enum_.variants.iter() {
variants.push_str(&format!("{}:{},", variant.name, variant.value));
}
self.cx.export(&enum_.name, &format!("Object.freeze({{ {} }})", variants));
self.cx.export(&enum_.name, &format!("Object.freeze({{ {} }})", variants), Some(format_doc_comments(&enum_.comments)));
self.cx.typescript.push_str(&format!("export enum {} {{", enum_.name));

variants.clear();
Expand Down Expand Up @@ -1764,3 +1773,9 @@ fn deindent(s: &str) -> String {
}
ret
}


fn format_doc_comments(comments: &Vec<String>) -> String {
let body: String = comments.iter().map(|c| format!("*{}\n", c.trim_matches('"'))).collect();
format!("/**\n{}*/\n", body)
}
Loading