diff --git a/layouts/partials/qmltype.html b/layouts/partials/qmltype.html
index a674bf3..2686d78 100644
--- a/layouts/partials/qmltype.html
+++ b/layouts/partials/qmltype.html
@@ -13,5 +13,16 @@
{{- $of = printf "<%s>" (partial "qmltype.html" .of) }}
{{- end -}}
- {{ .name }}{{ $of | safeHTML -}}
+ {{- $member := "" -}}
+
+ {{- if .prop -}}
+ {{- $member = printf ".%s" .prop -}}
+ {{- if eq .type "qt" -}}
+ {{- $link = printf "%s#%s-prop" $link .prop -}}
+ {{- else if eq .type "local" -}}
+ {{- $link = printf "%s#prop.%s" $link .prop -}}
+ {{- end -}}
+ {{- end -}}
+
+ {{ .name }}{{ $member }}{{ $of | safeHTML -}}
{{- end -}}
diff --git a/typegen/src/main.rs b/typegen/src/main.rs
index 3e02e94..4906d88 100644
--- a/typegen/src/main.rs
+++ b/typegen/src/main.rs
@@ -36,7 +36,8 @@ fn main() -> anyhow::Result<()> {
.collect::, _>>()?;
let parser = parse::Parser::new();
- let mut ctx = parse::ParseContext::default();
+ let mut ctx = parse::ParseContext::new(&module.header.name);
+
texts
.iter()
.map(|(header, text)| {
diff --git a/typegen/src/parse.rs b/typegen/src/parse.rs
index b2b4f01..9f4e586 100644
--- a/typegen/src/parse.rs
+++ b/typegen/src/parse.rs
@@ -33,6 +33,18 @@ pub fn parse_module(text: &str) -> anyhow::Result {
Ok(ModuleInfo { header, details })
}
+#[derive(Debug, Clone, Copy)]
+pub struct Comment<'a> {
+ pub text: &'a str,
+ pub module: &'a str,
+}
+
+impl<'a> Comment<'a> {
+ fn new(text: &'a str, module: &'a str) -> Self {
+ Self { text, module }
+ }
+}
+
#[derive(Debug)]
pub struct ClassInfo<'a> {
pub type_: ClassType,
@@ -41,7 +53,7 @@ pub struct ClassInfo<'a> {
pub superclass: Option<&'a str>,
pub singleton: bool,
pub uncreatable: bool,
- pub comment: Option<&'a str>,
+ pub comment: Option>,
pub properties: Vec>,
pub invokables: Vec>,
pub signals: Vec>,
@@ -58,7 +70,7 @@ pub enum ClassType {
pub struct Property<'a> {
pub type_: &'a str,
pub name: &'a str,
- pub comment: Option<&'a str>,
+ pub comment: Option>,
pub readable: bool,
pub writable: bool,
pub default: bool,
@@ -68,14 +80,14 @@ pub struct Property<'a> {
pub struct Invokable<'a> {
pub name: &'a str,
pub ret: &'a str,
- pub comment: Option<&'a str>,
+ pub comment: Option>,
pub params: Vec>,
}
#[derive(Debug, Clone)]
pub struct Signal<'a> {
pub name: &'a str,
- pub comment: Option<&'a str>,
+ pub comment: Option>,
pub params: Vec>,
}
@@ -90,14 +102,14 @@ pub struct EnumInfo<'a> {
pub namespace: &'a str,
pub enum_name: &'a str,
pub qml_name: &'a str,
- pub comment: Option<&'a str>,
+ pub comment: Option>,
pub variants: Vec>,
}
#[derive(Debug, Clone, Copy)]
pub struct Variant<'a> {
pub name: &'a str,
- pub comment: Option<&'a str>,
+ pub comment: Option>,
}
pub struct Parser {
@@ -116,13 +128,15 @@ pub struct Parser {
#[derive(Debug)]
pub struct ParseContext<'a> {
+ pub module: &'a str,
pub classes: Vec>,
pub enums: Vec>,
}
-impl Default for ParseContext<'_> {
- fn default() -> Self {
+impl<'a> ParseContext<'a> {
+ pub fn new(module: &'a str) -> Self {
Self {
+ module,
classes: Vec::new(),
enums: Vec::new(),
}
@@ -223,7 +237,7 @@ impl Parser {
properties.push(Property {
type_: prop.name("type").unwrap().as_str(),
name: prop.name("name").unwrap().as_str(),
- comment,
+ comment: comment.map(|v| Comment::new(v, ctx.module)),
readable: read || member,
writable: !constant && (write || member),
default: false,
@@ -279,7 +293,7 @@ impl Parser {
invokables.push(Invokable {
name,
ret: type_,
- comment,
+ comment: comment.map(|v| Comment::new(v, ctx.module)),
params,
});
}
@@ -317,7 +331,7 @@ impl Parser {
signals.push(Signal {
name,
- comment,
+ comment: comment.map(|v| Comment::new(v, ctx.module)),
params,
});
}
@@ -329,13 +343,13 @@ impl Parser {
let comment = enum_.name("comment").map(|m| m.as_str());
let enum_name = enum_.name("enum_name").unwrap().as_str();
let body = enum_.name("body").unwrap().as_str();
- let variants = self.parse_enum_variants(body)?;
+ let variants = self.parse_enum_variants(body, ctx)?;
enums.push(EnumInfo {
namespace: name,
enum_name,
qml_name: enum_name,
- comment,
+ comment: comment.map(|v| Comment::new(v, ctx.module)),
variants,
});
}
@@ -353,7 +367,7 @@ impl Parser {
superclass,
singleton,
uncreatable,
- comment,
+ comment: comment.map(|v| Comment::new(v, ctx.module)),
properties,
invokables,
signals,
@@ -376,13 +390,13 @@ impl Parser {
.map(|m| m.as_str())
.unwrap_or(namespace);
let body = enum_.name("body").unwrap().as_str();
- let variants = self.parse_enum_variants(body)?;
+ let variants = self.parse_enum_variants(body, ctx)?;
ctx.enums.push(EnumInfo {
namespace,
enum_name,
qml_name,
- comment,
+ comment: comment.map(|v| Comment::new(v, ctx.module)),
variants,
});
}
@@ -390,7 +404,11 @@ impl Parser {
Ok(())
}
- pub fn parse_enum_variants<'a>(&self, body: &'a str) -> anyhow::Result>> {
+ pub fn parse_enum_variants<'a>(
+ &self,
+ body: &'a str,
+ ctx: &ParseContext<'a>,
+ ) -> anyhow::Result>> {
let mut variants = Vec::new();
for variant in self.enum_variant_regex.captures_iter(body) {
@@ -399,7 +417,10 @@ impl Parser {
let comment = variant.name("comment").map(|m| m.as_str());
let name = variant.name("name").unwrap().as_str();
- variants.push(Variant { name, comment });
+ variants.push(Variant {
+ name,
+ comment: comment.map(|v| Comment::new(v, ctx.module)),
+ });
}
Ok(variants)
@@ -575,11 +596,12 @@ impl From> for typespec::FnParam {
}
}
-fn parse_details(text: &str) -> String {
+fn parse_details(comment: Comment) -> String {
let mut seen_content = false;
let mut callout = false;
- let mut str = text
+ let mut str = comment
+ .text
.lines()
.map(|line| {
line.trim()
@@ -616,6 +638,63 @@ fn parse_details(text: &str) -> String {
return Cow::Borrowed(line);
},
})
+ .map(|line| {
+ if line.contains("@@") {
+ let mut src: &str = &*line;
+ let mut accum = String::new();
+
+ while let Some(i) = src.find("@@") {
+ accum += &src[..i];
+ src = &src[i + 2..];
+
+ let endmk = src.find('$');
+ let endsp = src.find(' ');
+
+ let (end, ty) = match (endmk, endsp) {
+ (Some(i), _) if i < endsp.unwrap_or(usize::MAX) => (i + 1, &src[..i]),
+ (_, Some(i)) => (i, &src[..i]),
+ _ => (src.len(), src),
+ };
+
+ let mut split = ty.rsplit_once('.').unwrap_or(("", ty));
+
+ let prop = split
+ .1
+ .chars()
+ .next()
+ .unwrap()
+ .is_lowercase()
+ .then(|| {
+ let prop = split.1;
+ split = split.0.rsplit_once('.').unwrap_or(("", split.0));
+ prop
+ })
+ .unwrap_or("");
+
+ let (mut module, name) = split;
+
+ if module.is_empty() {
+ module = comment.module;
+ }
+
+ let (linktype, module) = match module.starts_with("Quickshell") {
+ true => ("local", module.to_string()),
+ false => ("qt", format!("qml.{module}")),
+ };
+
+ accum += &format!(
+ r#"{{{{< qmltypelink type="{linktype}" module="{module}" name="{name}" prop="{prop}" >}}}}"#
+ );
+ src = &src[end..];
+ }
+
+ accum += src;
+
+ return Cow::Owned(accum);
+ } else {
+ return line;
+ }
+ })
.fold(String::new(), |accum, line| accum + line.as_ref() + "\n");
if callout {
@@ -625,8 +704,8 @@ fn parse_details(text: &str) -> String {
str
}
-fn parse_details_desc(text: &str) -> (Option, Option) {
- let details = parse_details(text);
+fn parse_details_desc(comment: Comment) -> (Option, Option) {
+ let details = parse_details(comment);
if details.starts_with('!') {
match details[1..].split_once('\n') {
Some((desc, details)) => (