2
1
Fork 0

typegen: add shorthand for type/property links

This commit is contained in:
outfoxxed 2024-07-14 16:16:37 -07:00
parent 11ff70f1a8
commit db5c3aa3f4
Signed by: outfoxxed
GPG key ID: 4C88A185FB89301E
3 changed files with 115 additions and 24 deletions

View file

@ -13,5 +13,16 @@
{{- $of = printf "<%s>" (partial "qmltype.html" .of) }} {{- $of = printf "<%s>" (partial "qmltype.html" .of) }}
{{- end -}} {{- end -}}
<a href="{{ $link }}">{{ .name }}</a>{{ $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 -}}
<a href="{{ $link }}">{{ .name }}{{ $member }}</a>{{ $of | safeHTML -}}
{{- end -}} {{- end -}}

View file

@ -36,7 +36,8 @@ fn main() -> anyhow::Result<()> {
.collect::<Result<HashMap<_, _>, _>>()?; .collect::<Result<HashMap<_, _>, _>>()?;
let parser = parse::Parser::new(); let parser = parse::Parser::new();
let mut ctx = parse::ParseContext::default(); let mut ctx = parse::ParseContext::new(&module.header.name);
texts texts
.iter() .iter()
.map(|(header, text)| { .map(|(header, text)| {

View file

@ -33,6 +33,18 @@ pub fn parse_module(text: &str) -> anyhow::Result<ModuleInfo> {
Ok(ModuleInfo { header, details }) 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)] #[derive(Debug)]
pub struct ClassInfo<'a> { pub struct ClassInfo<'a> {
pub type_: ClassType, pub type_: ClassType,
@ -41,7 +53,7 @@ pub struct ClassInfo<'a> {
pub superclass: Option<&'a str>, pub superclass: Option<&'a str>,
pub singleton: bool, pub singleton: bool,
pub uncreatable: bool, pub uncreatable: bool,
pub comment: Option<&'a str>, pub comment: Option<Comment<'a>>,
pub properties: Vec<Property<'a>>, pub properties: Vec<Property<'a>>,
pub invokables: Vec<Invokable<'a>>, pub invokables: Vec<Invokable<'a>>,
pub signals: Vec<Signal<'a>>, pub signals: Vec<Signal<'a>>,
@ -58,7 +70,7 @@ pub enum ClassType {
pub struct Property<'a> { pub struct Property<'a> {
pub type_: &'a str, pub type_: &'a str,
pub name: &'a str, pub name: &'a str,
pub comment: Option<&'a str>, pub comment: Option<Comment<'a>>,
pub readable: bool, pub readable: bool,
pub writable: bool, pub writable: bool,
pub default: bool, pub default: bool,
@ -68,14 +80,14 @@ pub struct Property<'a> {
pub struct Invokable<'a> { pub struct Invokable<'a> {
pub name: &'a str, pub name: &'a str,
pub ret: &'a str, pub ret: &'a str,
pub comment: Option<&'a str>, pub comment: Option<Comment<'a>>,
pub params: Vec<InvokableParam<'a>>, pub params: Vec<InvokableParam<'a>>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Signal<'a> { pub struct Signal<'a> {
pub name: &'a str, pub name: &'a str,
pub comment: Option<&'a str>, pub comment: Option<Comment<'a>>,
pub params: Vec<InvokableParam<'a>>, pub params: Vec<InvokableParam<'a>>,
} }
@ -90,14 +102,14 @@ pub struct EnumInfo<'a> {
pub namespace: &'a str, pub namespace: &'a str,
pub enum_name: &'a str, pub enum_name: &'a str,
pub qml_name: &'a str, pub qml_name: &'a str,
pub comment: Option<&'a str>, pub comment: Option<Comment<'a>>,
pub variants: Vec<Variant<'a>>, pub variants: Vec<Variant<'a>>,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Variant<'a> { pub struct Variant<'a> {
pub name: &'a str, pub name: &'a str,
pub comment: Option<&'a str>, pub comment: Option<Comment<'a>>,
} }
pub struct Parser { pub struct Parser {
@ -116,13 +128,15 @@ pub struct Parser {
#[derive(Debug)] #[derive(Debug)]
pub struct ParseContext<'a> { pub struct ParseContext<'a> {
pub module: &'a str,
pub classes: Vec<ClassInfo<'a>>, pub classes: Vec<ClassInfo<'a>>,
pub enums: Vec<EnumInfo<'a>>, pub enums: Vec<EnumInfo<'a>>,
} }
impl Default for ParseContext<'_> { impl<'a> ParseContext<'a> {
fn default() -> Self { pub fn new(module: &'a str) -> Self {
Self { Self {
module,
classes: Vec::new(), classes: Vec::new(),
enums: Vec::new(), enums: Vec::new(),
} }
@ -223,7 +237,7 @@ impl Parser {
properties.push(Property { properties.push(Property {
type_: prop.name("type").unwrap().as_str(), type_: prop.name("type").unwrap().as_str(),
name: prop.name("name").unwrap().as_str(), name: prop.name("name").unwrap().as_str(),
comment, comment: comment.map(|v| Comment::new(v, ctx.module)),
readable: read || member, readable: read || member,
writable: !constant && (write || member), writable: !constant && (write || member),
default: false, default: false,
@ -279,7 +293,7 @@ impl Parser {
invokables.push(Invokable { invokables.push(Invokable {
name, name,
ret: type_, ret: type_,
comment, comment: comment.map(|v| Comment::new(v, ctx.module)),
params, params,
}); });
} }
@ -317,7 +331,7 @@ impl Parser {
signals.push(Signal { signals.push(Signal {
name, name,
comment, comment: comment.map(|v| Comment::new(v, ctx.module)),
params, params,
}); });
} }
@ -329,13 +343,13 @@ impl Parser {
let comment = enum_.name("comment").map(|m| m.as_str()); let comment = enum_.name("comment").map(|m| m.as_str());
let enum_name = enum_.name("enum_name").unwrap().as_str(); let enum_name = enum_.name("enum_name").unwrap().as_str();
let body = enum_.name("body").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 { enums.push(EnumInfo {
namespace: name, namespace: name,
enum_name, enum_name,
qml_name: enum_name, qml_name: enum_name,
comment, comment: comment.map(|v| Comment::new(v, ctx.module)),
variants, variants,
}); });
} }
@ -353,7 +367,7 @@ impl Parser {
superclass, superclass,
singleton, singleton,
uncreatable, uncreatable,
comment, comment: comment.map(|v| Comment::new(v, ctx.module)),
properties, properties,
invokables, invokables,
signals, signals,
@ -376,13 +390,13 @@ impl Parser {
.map(|m| m.as_str()) .map(|m| m.as_str())
.unwrap_or(namespace); .unwrap_or(namespace);
let body = enum_.name("body").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)?;
ctx.enums.push(EnumInfo { ctx.enums.push(EnumInfo {
namespace, namespace,
enum_name, enum_name,
qml_name, qml_name,
comment, comment: comment.map(|v| Comment::new(v, ctx.module)),
variants, variants,
}); });
} }
@ -390,7 +404,11 @@ impl Parser {
Ok(()) Ok(())
} }
pub fn parse_enum_variants<'a>(&self, body: &'a str) -> anyhow::Result<Vec<Variant<'a>>> { pub fn parse_enum_variants<'a>(
&self,
body: &'a str,
ctx: &ParseContext<'a>,
) -> anyhow::Result<Vec<Variant<'a>>> {
let mut variants = Vec::new(); let mut variants = Vec::new();
for variant in self.enum_variant_regex.captures_iter(body) { 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 comment = variant.name("comment").map(|m| m.as_str());
let name = variant.name("name").unwrap().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) Ok(variants)
@ -575,11 +596,12 @@ impl From<InvokableParam<'_>> for typespec::FnParam {
} }
} }
fn parse_details(text: &str) -> String { fn parse_details(comment: Comment) -> String {
let mut seen_content = false; let mut seen_content = false;
let mut callout = false; let mut callout = false;
let mut str = text let mut str = comment
.text
.lines() .lines()
.map(|line| { .map(|line| {
line.trim() line.trim()
@ -616,6 +638,63 @@ fn parse_details(text: &str) -> String {
return Cow::Borrowed(line); 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"); .fold(String::new(), |accum, line| accum + line.as_ref() + "\n");
if callout { if callout {
@ -625,8 +704,8 @@ fn parse_details(text: &str) -> String {
str str
} }
fn parse_details_desc(text: &str) -> (Option<String>, Option<String>) { fn parse_details_desc(comment: Comment) -> (Option<String>, Option<String>) {
let details = parse_details(text); let details = parse_details(comment);
if details.starts_with('!') { if details.starts_with('!') {
match details[1..].split_once('\n') { match details[1..].split_once('\n') {
Some((desc, details)) => ( Some((desc, details)) => (