Skip to content

Commit 8839f46

Browse files
committed
Prepare for doc comment support
Prior work: jannik4/shader_docs#1
1 parent 527206b commit 8839f46

File tree

17 files changed

+876
-31
lines changed

17 files changed

+876
-31
lines changed

Cargo.lock

Lines changed: 480 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/wesl_docs/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ license = "MIT OR Apache-2.0"
77
[dependencies]
88
semver = "1.0.20"
99
indexmap = "2.1.0"
10+
pulldown-cmark = "0.13.0"

crates/wesl_docs/src/lib.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::fmt;
22

33
pub use indexmap::{IndexMap, IndexSet};
4+
pub use pulldown_cmark as md;
45
pub use semver::Version;
56

67
#[derive(Debug, Clone)]
@@ -13,6 +14,7 @@ pub struct WeslDocs {
1314
pub struct Module {
1415
pub name: String,
1516
pub source: Option<String>,
17+
pub comment: Option<DocComment>,
1618
pub modules: Vec<Module>,
1719
pub constants: IndexMap<Ident, Item<Constant>>,
1820
pub global_variables: IndexMap<Ident, Item<GlobalVariable>>,
@@ -27,6 +29,7 @@ impl Module {
2729
Self {
2830
name,
2931
source: None,
32+
comment: None,
3033
modules: Vec::new(),
3134
constants: IndexMap::new(),
3235
global_variables: IndexMap::new(),
@@ -129,6 +132,7 @@ impl<T> Default for Item<T> {
129132
pub trait ItemInstance {
130133
const ITEM_KIND: ItemKind;
131134
fn conditional(&self) -> Option<&Conditional>;
135+
fn comment(&self) -> Option<&DocComment>;
132136
fn all_attributes(&self) -> impl Iterator<Item = &Attribute>;
133137
}
134138

@@ -139,6 +143,7 @@ pub struct Constant {
139143
pub init: Expression,
140144
pub attributes: Vec<Attribute>,
141145
pub conditional: Option<Conditional>,
146+
pub comment: Option<DocComment>,
142147
pub span: Option<Span>,
143148
}
144149

@@ -147,6 +152,9 @@ impl ItemInstance for Constant {
147152
fn conditional(&self) -> Option<&Conditional> {
148153
self.conditional.as_ref()
149154
}
155+
fn comment(&self) -> Option<&DocComment> {
156+
self.comment.as_ref()
157+
}
150158
fn all_attributes(&self) -> impl Iterator<Item = &Attribute> {
151159
self.attributes.iter()
152160
}
@@ -160,6 +168,7 @@ pub struct GlobalVariable {
160168
pub init: Option<Expression>,
161169
pub attributes: Vec<Attribute>,
162170
pub conditional: Option<Conditional>,
171+
pub comment: Option<DocComment>,
163172
pub span: Option<Span>,
164173
}
165174

@@ -168,6 +177,9 @@ impl ItemInstance for GlobalVariable {
168177
fn conditional(&self) -> Option<&Conditional> {
169178
self.conditional.as_ref()
170179
}
180+
fn comment(&self) -> Option<&DocComment> {
181+
self.comment.as_ref()
182+
}
171183
fn all_attributes(&self) -> impl Iterator<Item = &Attribute> {
172184
self.attributes.iter()
173185
}
@@ -243,6 +255,7 @@ pub struct Struct {
243255
pub members: Vec<StructMember>,
244256
pub attributes: Vec<Attribute>,
245257
pub conditional: Option<Conditional>,
258+
pub comment: Option<DocComment>,
246259
pub span: Option<Span>,
247260
}
248261

@@ -251,6 +264,9 @@ impl ItemInstance for Struct {
251264
fn conditional(&self) -> Option<&Conditional> {
252265
self.conditional.as_ref()
253266
}
267+
fn comment(&self) -> Option<&DocComment> {
268+
self.comment.as_ref()
269+
}
254270
fn all_attributes(&self) -> impl Iterator<Item = &Attribute> {
255271
self.attributes
256272
.iter()
@@ -264,6 +280,7 @@ pub struct StructMember {
264280
pub ty: TypeExpression,
265281
pub attributes: Vec<Attribute>,
266282
pub conditional: Option<Conditional>,
283+
pub comment: Option<DocComment>,
267284
}
268285

269286
#[derive(Debug, Clone)]
@@ -294,6 +311,7 @@ pub struct Function {
294311
pub attributes: Vec<Attribute>,
295312
pub return_attributes: Vec<Attribute>,
296313
pub conditional: Option<Conditional>,
314+
pub comment: Option<DocComment>,
297315
pub span: Option<Span>,
298316
}
299317

@@ -302,6 +320,9 @@ impl ItemInstance for Function {
302320
fn conditional(&self) -> Option<&Conditional> {
303321
self.conditional.as_ref()
304322
}
323+
fn comment(&self) -> Option<&DocComment> {
324+
self.comment.as_ref()
325+
}
305326
fn all_attributes(&self) -> impl Iterator<Item = &Attribute> {
306327
self.attributes
307328
.iter()
@@ -324,6 +345,7 @@ pub struct TypeAlias {
324345
pub ty: TypeExpression,
325346
pub attributes: Vec<Attribute>,
326347
pub conditional: Option<Conditional>,
348+
pub comment: Option<DocComment>,
327349
pub span: Option<Span>,
328350
}
329351

@@ -332,6 +354,9 @@ impl ItemInstance for TypeAlias {
332354
fn conditional(&self) -> Option<&Conditional> {
333355
self.conditional.as_ref()
334356
}
357+
fn comment(&self) -> Option<&DocComment> {
358+
self.comment.as_ref()
359+
}
335360
fn all_attributes(&self) -> impl Iterator<Item = &Attribute> {
336361
self.attributes.iter()
337362
}
@@ -346,6 +371,14 @@ impl fmt::Display for Ident {
346371
}
347372
}
348373

374+
#[derive(Debug, Clone)]
375+
pub struct DocComment {
376+
/// This is not escaped, e.g. it can contain `<script>` tags.
377+
pub unsafe_short: Vec<md::Event<'static>>,
378+
/// This is not escaped, e.g. it can contain `<script>` tags.
379+
pub unsafe_full: Vec<md::Event<'static>>,
380+
}
381+
349382
#[derive(Debug, Clone)]
350383
pub enum Attribute {
351384
Align(Expression),
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
use crate::source_map::SourceMap;
2+
use std::collections::HashMap;
3+
use wesl_docs::*;
4+
5+
pub fn build_inner_doc_comment(
6+
raw_comment: &str,
7+
source_map: &SourceMap,
8+
module_path: &[String],
9+
dependencies: &HashMap<String, Version>,
10+
) -> Option<DocComment> {
11+
build_doc_comment(raw_comment, "//!", source_map, module_path, dependencies)
12+
}
13+
14+
pub fn build_outer_doc_comment(
15+
raw_comment: &str,
16+
source_map: &SourceMap,
17+
module_path: &[String],
18+
dependencies: &HashMap<String, Version>,
19+
) -> Option<DocComment> {
20+
build_doc_comment(raw_comment, "///", source_map, module_path, dependencies)
21+
}
22+
23+
fn build_doc_comment(
24+
raw_comment: &str,
25+
comment_prefix: &str,
26+
_source_map: &SourceMap,
27+
_module_path: &[String],
28+
_dependencies: &HashMap<String, Version>,
29+
) -> Option<DocComment> {
30+
// Strip the comment prefix
31+
let comment = raw_comment
32+
.lines()
33+
.filter_map(|line| {
34+
let line = line.trim_start();
35+
line.starts_with(comment_prefix).then(|| line[3..].trim())
36+
})
37+
.enumerate()
38+
.fold(String::new(), |mut acc, (idx, line)| {
39+
if idx != 0 {
40+
acc.push('\n');
41+
}
42+
acc.push_str(line);
43+
acc
44+
});
45+
46+
// Parse
47+
let mut full = md::Parser::new(&comment)
48+
.map(|event| event.into_static())
49+
.collect::<Vec<_>>();
50+
if full.is_empty() {
51+
return None;
52+
}
53+
54+
// Raise heading levels
55+
raise_heading_levels(&mut full);
56+
57+
// TODO: Support intra-doc links
58+
59+
// Create the short variant
60+
let mut complete = false;
61+
let mut balance = 0;
62+
let short = full
63+
.iter()
64+
.filter_map(|event| match event {
65+
md::Event::Start(tag) => {
66+
balance += 1;
67+
Some(md::Event::Start(match tag {
68+
md::Tag::Heading { .. } => md::Tag::Paragraph,
69+
_ => tag.clone(),
70+
}))
71+
}
72+
md::Event::End(tag_end) => {
73+
balance -= 1;
74+
complete |= balance == 0;
75+
Some(md::Event::End(match tag_end {
76+
md::TagEnd::Heading(_) => md::TagEnd::Paragraph,
77+
_ => *tag_end,
78+
}))
79+
}
80+
e @ md::Event::Text(_) => (!complete).then(|| e.clone()),
81+
e @ md::Event::Code(_) => (!complete).then(|| e.clone()),
82+
e @ md::Event::InlineMath(_) => (!complete).then(|| e.clone()),
83+
e @ md::Event::DisplayMath(_) => (!complete).then(|| e.clone()),
84+
e @ md::Event::Html(_) => (!complete).then(|| e.clone()),
85+
e @ md::Event::InlineHtml(_) => (!complete).then(|| e.clone()),
86+
e @ md::Event::FootnoteReference(_) => Some(e.clone()),
87+
md::Event::SoftBreak => None,
88+
md::Event::HardBreak | md::Event::Rule | md::Event::TaskListMarker(_) => {
89+
complete |= balance == 0;
90+
None
91+
}
92+
})
93+
.collect::<Vec<_>>();
94+
95+
Some(DocComment {
96+
unsafe_short: short,
97+
unsafe_full: full,
98+
})
99+
}
100+
101+
fn raise_heading_levels(events: &mut [md::Event]) {
102+
for event in events {
103+
match event {
104+
md::Event::Start(md::Tag::Heading { level, .. }) => {
105+
*level = raise_heading_level(*level);
106+
}
107+
md::Event::End(md::TagEnd::Heading(level)) => {
108+
*level = raise_heading_level(*level);
109+
}
110+
_ => {}
111+
}
112+
}
113+
}
114+
115+
fn raise_heading_level(level: md::HeadingLevel) -> md::HeadingLevel {
116+
match level {
117+
md::HeadingLevel::H1 => md::HeadingLevel::H2,
118+
md::HeadingLevel::H2 => md::HeadingLevel::H3,
119+
md::HeadingLevel::H3 => md::HeadingLevel::H4,
120+
md::HeadingLevel::H4 => md::HeadingLevel::H5,
121+
md::HeadingLevel::H5 => md::HeadingLevel::H6,
122+
md::HeadingLevel::H6 => md::HeadingLevel::H6,
123+
}
124+
}

crates/wesl_docs_compiler/src/lib.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod build_attributes;
22
mod build_conditional;
3+
mod build_doc_comment;
34
mod build_expression;
45
mod build_type;
56
mod calculate_span;
@@ -11,6 +12,7 @@ mod source_map;
1112
use self::{
1213
build_attributes::build_attributes,
1314
build_conditional::{ConditionalScope, build_conditional},
15+
build_doc_comment::{build_inner_doc_comment, build_outer_doc_comment},
1416
build_expression::build_expression,
1517
build_type::build_type,
1618
calculate_span::calculate_span,
@@ -79,6 +81,14 @@ fn compile_module(
7981
module.source = Some(source.to_string());
8082
}
8183

84+
// Set comment
85+
module.comment = build_inner_doc_comment(
86+
"", // TODO: get doc comment from wesl-rs
87+
&source_map,
88+
&path,
89+
dependencies,
90+
);
91+
8292
// Collect translate time features
8393
module.translate_time_features = collect_features(compiled);
8494

@@ -151,6 +161,12 @@ fn compile_module(
151161
&mut conditional_scope,
152162
&declaration.attributes,
153163
),
164+
comment: build_outer_doc_comment(
165+
"", // TODO: get doc comment from wesl-rs
166+
&source_map,
167+
&path,
168+
dependencies,
169+
),
154170
span,
155171
});
156172
}
@@ -186,6 +202,12 @@ fn compile_module(
186202
&mut conditional_scope,
187203
&declaration.attributes,
188204
),
205+
comment: build_outer_doc_comment(
206+
"", // TODO: get doc comment from wesl-rs
207+
&source_map,
208+
&path,
209+
dependencies,
210+
),
189211
span,
190212
});
191213
}
@@ -210,6 +232,12 @@ fn compile_module(
210232
&mut conditional_scope,
211233
&type_alias.attributes,
212234
),
235+
comment: build_outer_doc_comment(
236+
"", // TODO: get doc comment from wesl-rs
237+
&source_map,
238+
&path,
239+
dependencies,
240+
),
213241
span,
214242
});
215243
}
@@ -240,6 +268,12 @@ fn compile_module(
240268
&mut conditional_scope,
241269
&member.attributes,
242270
),
271+
comment: build_outer_doc_comment(
272+
"", // TODO: get doc comment from wesl-rs
273+
&source_map,
274+
&path,
275+
dependencies,
276+
),
243277
})
244278
.collect()
245279
},
@@ -250,6 +284,12 @@ fn compile_module(
250284
dependencies,
251285
),
252286
conditional: build_conditional(&mut conditional_scope, &struct_.attributes),
287+
comment: build_outer_doc_comment(
288+
"", // TODO: get doc comment from wesl-rs
289+
&source_map,
290+
&path,
291+
dependencies,
292+
),
253293
span,
254294
});
255295
}
@@ -303,6 +343,12 @@ fn compile_module(
303343
&mut conditional_scope,
304344
&function.attributes,
305345
),
346+
comment: build_outer_doc_comment(
347+
"", // TODO: get doc comment from wesl-rs
348+
&source_map,
349+
&path,
350+
dependencies,
351+
),
306352
span,
307353
});
308354
}

crates/wesl_docs_generator/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ askama = "0.13.0"
1010
include_dir = "0.7.4"
1111
serde = { version = "1.0.193", features = ["derive"] }
1212
serde_json = "1.0.108"
13+
ammonia = "4.0.0"

0 commit comments

Comments
 (0)