2
1
Fork 0

Compare commits

..

2 commits

Author SHA1 Message Date
4b9a0e9ff2
typegen: add module and name fields to type files 2024-07-22 15:24:43 -07:00
db73ccf0d1
typegen: add module names 2024-07-22 15:00:09 -07:00
6 changed files with 128 additions and 199 deletions

8
flake.lock generated
View file

@ -22,11 +22,11 @@
]
},
"locked": {
"lastModified": 1725876916,
"narHash": "sha256-3SDm2pV1ZIQHqIhAe7BjuXhNdVyzcxPiwaFyqE8ODdc=",
"lastModified": 1720770600,
"narHash": "sha256-Ouic1xV4pJns1rbw6wnee9fO4VWkcv73hxSLQkPYabk=",
"ref": "refs/heads/master",
"rev": "85be3861ceadba6e3d877a64cade15015ea0c96f",
"revCount": 324,
"rev": "d630cc7f76b56ad3ebe084159df6beab48db9866",
"revCount": 247,
"type": "git",
"url": "https://git.outfoxxed.me/outfoxxed/quickshell"
},

View file

@ -1,18 +0,0 @@
{{ define "main" }}
<div class='mx-auto flex {{ partial "utils/page-width" . }}'>
{{ partial "sidebar.html" (dict "context" .) }}
{{ partial "toc.html" . }}
<article class="w-full break-words flex min-h-[calc(100vh-var(--navbar-height))] min-w-0 justify-center pb-8 pr-[calc(env(safe-area-inset-right)-1.5rem)]">
<main class="w-full min-w-0 max-w-6xl px-6 pt-4 md:px-12">
{{ partial "breadcrumb.html" . }}
<div class="content">
{{- if not (isset .Params "hidetitle") -}}<h1>{{ .Title }}</h1>{{- end -}}
{{ .Content }}
</div>
{{ partial "components/last-updated.html" . }}
{{ partial "components/pager.html" . }}
{{ partial "components/comments.html" . }}
</main>
</article>
</div>
{{ end }}

View file

@ -1,9 +0,0 @@
{{- $params := dict
"type" (.Get "type")
"module" (.Get "module")
"name" (.Get "name")
"mtype" (.Get "mtype")
"mname" (.Get "mname")
-}}
{{- partial "qmltype.html" $params -}}

View file

@ -4,7 +4,6 @@ use anyhow::{anyhow, Context};
mod outform;
mod parse;
mod reformat;
mod resolver;
mod typespec;

View file

@ -4,10 +4,7 @@ use anyhow::{anyhow, bail, Context};
use fancy_regex::Regex;
use serde::Deserialize;
use crate::{
reformat::{self, ReformatPass},
typespec,
};
use crate::typespec;
#[derive(Deserialize, Debug)]
pub struct ModuleInfoHeader {
@ -152,12 +149,12 @@ impl Parser {
class_regex: Regex::new(r#"(?<comment>(\s*\/\/\/.*\n)+)?\s*class\s+(?<name>\w+)(?:\s*:\s*public\s+((?<super>\w+)(<.+>)?)(\s*,(\s*\w+)*)*)?\s*\{(?<body>[\s\S]*?)(?!};\s*Q_ENUM)};"#).unwrap(),
macro_regex: Regex::new(r#"(?<comment>(\s*\/\/\/.*\n)+)?\s*(?<hide>QSDOC_HIDE\s)?(?<type>(Q|QML|QSDOC)_\w+)\s*(\(\s*(?<args>.*)\s*\))?;"#).unwrap(),
property_regex: Regex::new(r#"^\s*(?<type>(\w|::|, |<|>|\*)+)\*?\s+(?<name>\w+)(\s+(MEMBER\s+(?<member>\w+)|READ\s+(?<read>\w+)|WRITE\s+(?<write>\w+)|NOTIFY\s+(?<notify>\w+)|(?<const>CONSTANT)))+\s*$"#).unwrap(),
fn_regex: Regex::new(r#"(?<comment>(\s*\/\/\/.*\n)+)?\s*(\/\/@\s+)?Q_INVOKABLE\s+(\[\[.*\]\]\s+)?(static\s+)?(?<type>(\w|::|<|>)+\*?)\s+(?<name>\w+)\((?<params>[\s\S]*?)\)(\s*const)?;"#).unwrap(),
fn_regex: Regex::new(r#"(?<comment>(\s*\/\/\/.*\n)+)?\s*Q_INVOKABLE\s+(\[\[.*\]\]\s+)?(static\s+)?(?<type>(\w|::|<|>)+\*?)\s+(?<name>\w+)\((?<params>[\s\S]*?)\)(\s*const)?;"#).unwrap(),
signal_regex: Regex::new(r#"(?<comment>(\s*\/\/\/.*\n)+)?\s*void\s+(?<name>\w+)\((?<params>[\s\S]*?)\);"#).unwrap(),
fn_param_regex: Regex::new(r#"(const\s+)?(?<type>(\w|::|<|>)+\*?)&?\s+(?<name>\w+)(,|$)"#).unwrap(),
signals_regex: Regex::new(r#"signals:(?<signals>(\s*(\s*///.*\s*)*void .*;)*)"#).unwrap(),
defaultprop_classinfo_regex: Regex::new(r#"^\s*"DefaultProperty", "(?<prop>.+)"\s*$"#).unwrap(),
enum_ns_regex: Regex::new(r#"(?<comment>(\s*\/\/\/.*\n)+)?\s*namespace (?<namespace>\w+)\s*\{(?<nsbody>[\s\S]*?(QML_ELEMENT|QML_NAMED_ELEMENT\((?<qml_name>\w+)\));[\s\S]*?enum\s*(?<enum_name>\w+)\s*\{(?<body>[\s\S]*?)\};[\s\S]*?)\}"#).unwrap(),
enum_ns_regex: Regex::new(r#"(?<comment>(\s*\/\/\/.*\n)+)?\s*namespace (?<namespace>\w+)\s*\{[\s\S]*?(QML_ELEMENT|QML_NAMED_ELEMENT\((?<qml_name>\w+)\));[\s\S]*?enum\s*(?<enum_name>\w+)\s*\{(?<body>[\s\S]*?)\};[\s\S]*?\}"#).unwrap(),
enum_class_regex: Regex::new(r#"(?<comment>(\s*\/\/\/.*\n)+)?\s*enum\s*(?<enum_name>\w+)\s*\{(?<body>[\s\S]*?)\};\s+Q_ENUM\(.+\);"#).unwrap(),
enum_variant_regex: Regex::new(r#"(?<comment>(\s*\/\/\/.*\n)+)?\s*(?<name>\w+)\s*=\s*.+,"#).unwrap(),
}
@ -387,41 +384,14 @@ impl Parser {
let comment = enum_.name("comment").map(|m| m.as_str());
let namespace = enum_.name("namespace").unwrap().as_str();
let mut enum_name = enum_.name("enum_name").unwrap().as_str();
let enum_name = enum_.name("enum_name").unwrap().as_str();
let qml_name = enum_
.name("qml_name")
.map(|m| m.as_str())
.unwrap_or(namespace);
let nsbody = enum_.name("nsbody").unwrap().as_str();
let body = enum_.name("body").unwrap().as_str();
let variants = self.parse_enum_variants(body, ctx)?;
for macro_ in self.macro_regex.captures_iter(nsbody) {
let macro_ = macro_?;
let type_ = macro_.name("type").unwrap().as_str();
let args = macro_.name("args").map(|m| m.as_str());
(|| {
match type_ {
"Q_DECLARE_FLAGS" => {
enum_name = args
.expect("Q_DECLARE_FLAGS must have arguments")
.split_once(',')
.expect("Q_DECLARE_FLAGS must have two arguments")
.0
.trim();
},
_ => {},
}
Ok::<_, anyhow::Error>(())
})()
.with_context(|| {
format!("while parsing macro `{}`", macro_.get(0).unwrap().as_str())
})?;
}
ctx.enums.push(EnumInfo {
namespace,
enum_name,
@ -628,6 +598,7 @@ impl From<InvokableParam<'_>> for typespec::FnParam {
fn parse_details(comment: Comment) -> String {
let mut seen_content = false;
let mut callout = false;
let mut str = comment
.text
@ -644,14 +615,127 @@ fn parse_details(comment: Comment) -> String {
seen_content |= any;
filter
})
.map(|line| match callout {
true => {
if line.starts_with('>') {
Cow::Borrowed(line[1..].strip_prefix(' ').unwrap_or(&line[1..]))
} else {
callout = false;
Cow::Owned(format!("{{{{< /callout >}}}}\n{line}"))
}
},
false => {
if line.starts_with("> [!") {
let code = line[4..].split_once(']');
if let Some((code, line)) = code {
let code = code.to_lowercase();
callout = true;
return Cow::Owned(format!("{{{{< callout type=\"{code}\" >}}}}\n{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 separators = [
('$', true),
(' ', false),
(',', false),
(';', false),
(':', false),
];
let (mut end, mut ty) = src.chars().enumerate()
.find_map(|(i, char)| {
separators.iter()
.find(|(sc, _)| char == *sc)
.map(|(_, strip)| (i + if *strip { 1 } else { 0 }, &src[..i]))
})
.unwrap_or_else(|| (src.len(), src));
// special case for . as it is contained in valid types as well
if ty.ends_with('.') {
end -= 1;
ty = &ty[..ty.len() - 1];
}
let (ty, member) = match ty.chars().next() {
None => (None, None),
Some(c) if c.is_lowercase() => (None, Some(ty)),
Some(_) => {
let mut split = ty.rsplit_once('.').unwrap_or(("", ty));
let member = 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;
}
(Some((module, name)), Some(member))
},
};
let (membertype, membername) = match member {
None => ("", ""),
Some(name) if name.ends_with("()") => ("func", &name[..name.len() - 2]),
Some(name) if name.ends_with("(s)") => ("signal", &name[..name.len() - 3]),
Some(name) if name.is_empty() => ("", ""),
Some(name) => ("prop", name),
};
let ((linktype, module), name) = match ty {
Some((module, name)) => {
let module = match module {
module if module.starts_with("Quickshell") => ("local", module.to_string()),
module => ("qt", format!("qml.{module}")),
};
(module, name)
},
None => (("", String::new()), ""),
};
accum += &format!(
r#"{{{{< qmltypelink type="{linktype}" module="{module}" name="{name}" mtype="{membertype}" mname="{membername}" >}}}}"#
);
src = &src[end..];
}
accum += src;
return Cow::Owned(accum);
} else {
return line;
}
})
.fold(String::new(), |accum, line| accum + line.as_ref() + "\n");
let reformat_ctx = reformat::Context {
module: comment.module,
};
crate::reformat::GfmQuoteBlocks::reformat(&reformat_ctx, &mut str);
crate::reformat::TypeLinks::reformat(&reformat_ctx, &mut str);
if callout {
str += "\n{{< /callout >}}";
}
str
}

View file

@ -1,127 +0,0 @@
use std::borrow::Cow;
pub struct Context<'a> {
pub module: &'a str,
}
pub trait ReformatPass {
fn reformat(context: &Context, text: &mut String);
}
pub struct GfmQuoteBlocks;
impl ReformatPass for GfmQuoteBlocks {
fn reformat(_: &Context, text: &mut String) {
*text = text.replace("> [!INFO]", "> [!NOTE]");
}
}
pub struct TypeLinks;
impl ReformatPass for TypeLinks {
fn reformat(context: &Context, text: &mut String) {
let lines = text.lines().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 separators = [
('$', true),
(' ', false),
(',', false),
(';', false),
(':', false),
];
let (mut end, mut ty) = src
.chars()
.enumerate()
.find_map(|(i, char)| {
separators
.iter()
.find(|(sc, _)| char == *sc)
.map(|(_, strip)| (i + if *strip { 1 } else { 0 }, &src[..i]))
})
.unwrap_or_else(|| (src.len(), src));
// special case for . as it is contained in valid types as well
if ty.ends_with('.') {
end -= 1;
ty = &ty[..ty.len() - 1];
}
let (ty, member) = match ty.chars().next() {
None => (None, None),
Some(c) if c.is_lowercase() => (None, Some(ty)),
Some(_) => {
let mut split = ty.rsplit_once('.').unwrap_or(("", ty));
let member = 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 = context.module;
}
(Some((module, name)), Some(member))
},
};
let (membertype, membername) = match member {
None => ("", ""),
Some(name) if name.ends_with("()") => ("func", &name[..name.len() - 2]),
Some(name) if name.ends_with("(s)") => ("signal", &name[..name.len() - 3]),
Some(name) if name.is_empty() => ("", ""),
Some(name) => ("prop", name),
};
accum += "TYPE";
if let Some((module, name)) = ty {
if module.starts_with("Quickshell") {
accum += "99MQS";
} else {
accum += "99MQT_qml";
}
accum = module
.split('.')
.fold(accum, |accum, next| accum + "_" + next)
+ "99N" + name;
}
if !membername.is_empty() {
accum += &format!("99V{membername}99T{membertype}");
}
accum += "99TYPE";
src = &src[end..];
}
accum += src;
return Cow::Owned(accum);
} else {
return Cow::Borrowed(line);
}
});
*text = lines.fold(String::new(), |accum, line| accum + line.as_ref() + "\n");
}
}