Compare commits
11 commits
4b9a0e9ff2
...
0c55789923
Author | SHA1 | Date | |
---|---|---|---|
0c55789923 | |||
997eb9e876 | |||
b78b75f006 | |||
43ccb1ea87 | |||
fcc4533b2b | |||
e574e37bfc | |||
63c4583bd5 | |||
08cad1f0e8 | |||
419ebe0cd3 | |||
18debdc9db | |||
89a533173b |
8 changed files with 239 additions and 153 deletions
8
flake.lock
generated
8
flake.lock
generated
|
@ -22,11 +22,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1720770600,
|
||||
"narHash": "sha256-Ouic1xV4pJns1rbw6wnee9fO4VWkcv73hxSLQkPYabk=",
|
||||
"lastModified": 1725876916,
|
||||
"narHash": "sha256-3SDm2pV1ZIQHqIhAe7BjuXhNdVyzcxPiwaFyqE8ODdc=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "d630cc7f76b56ad3ebe084159df6beab48db9866",
|
||||
"revCount": 247,
|
||||
"rev": "85be3861ceadba6e3d877a64cade15015ea0c96f",
|
||||
"revCount": 324,
|
||||
"type": "git",
|
||||
"url": "https://git.outfoxxed.me/outfoxxed/quickshell"
|
||||
},
|
||||
|
|
18
layouts/docs/list.html
Normal file
18
layouts/docs/list.html
Normal file
|
@ -0,0 +1,18 @@
|
|||
{{ 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 }}
|
9
layouts/shortcodes/qmltypelink.html
Normal file
9
layouts/shortcodes/qmltypelink.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
{{- $params := dict
|
||||
"type" (.Get "type")
|
||||
"module" (.Get "module")
|
||||
"name" (.Get "name")
|
||||
"mtype" (.Get "mtype")
|
||||
"mname" (.Get "mname")
|
||||
-}}
|
||||
|
||||
{{- partial "qmltype.html" $params -}}
|
|
@ -4,6 +4,7 @@ use anyhow::{anyhow, Context};
|
|||
|
||||
mod outform;
|
||||
mod parse;
|
||||
mod reformat;
|
||||
mod resolver;
|
||||
mod typespec;
|
||||
|
||||
|
@ -107,6 +108,7 @@ hidetitle = true
|
|||
}
|
||||
|
||||
let index = outform::ModuleIndex {
|
||||
name: module.header.name.to_string(),
|
||||
description: module.header.description,
|
||||
details: module.details.to_string(),
|
||||
};
|
||||
|
|
|
@ -4,14 +4,23 @@ use serde::Serialize;
|
|||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct ModuleIndex {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub details: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct TypeInfo {
|
||||
pub name: String,
|
||||
pub module: String,
|
||||
#[serde(flatten)]
|
||||
pub details: TypeDetails,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[serde(tag = "type")]
|
||||
pub enum TypeInfo {
|
||||
pub enum TypeDetails {
|
||||
Class(ClassInfo),
|
||||
Enum(EnumInfo),
|
||||
}
|
||||
|
|
|
@ -4,7 +4,10 @@ use anyhow::{anyhow, bail, Context};
|
|||
use fancy_regex::Regex;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::typespec;
|
||||
use crate::{
|
||||
reformat::{self, ReformatPass},
|
||||
typespec,
|
||||
};
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct ModuleInfoHeader {
|
||||
|
@ -149,12 +152,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*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*(\/\/@\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*\{[\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*\{(?<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_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(),
|
||||
}
|
||||
|
@ -384,14 +387,41 @@ impl Parser {
|
|||
|
||||
let comment = enum_.name("comment").map(|m| m.as_str());
|
||||
let namespace = enum_.name("namespace").unwrap().as_str();
|
||||
let enum_name = enum_.name("enum_name").unwrap().as_str();
|
||||
let mut 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,
|
||||
|
@ -598,7 +628,6 @@ 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
|
||||
|
@ -615,127 +644,14 @@ 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");
|
||||
|
||||
if callout {
|
||||
str += "\n{{< /callout >}}";
|
||||
}
|
||||
let reformat_ctx = reformat::Context {
|
||||
module: comment.module,
|
||||
};
|
||||
|
||||
crate::reformat::GfmQuoteBlocks::reformat(&reformat_ctx, &mut str);
|
||||
crate::reformat::TypeLinks::reformat(&reformat_ctx, &mut str);
|
||||
|
||||
str
|
||||
}
|
||||
|
|
127
typegen/src/reformat.rs
Normal file
127
typegen/src/reformat.rs
Normal file
|
@ -0,0 +1,127 @@
|
|||
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");
|
||||
}
|
||||
}
|
|
@ -211,37 +211,42 @@ pub fn resolve_types(
|
|||
None => HashMap::new(),
|
||||
};
|
||||
|
||||
let type_ = outform::ClassInfo {
|
||||
superclass,
|
||||
description: class.description.clone(),
|
||||
details: class.details.clone(),
|
||||
flags: {
|
||||
let mut flags = Vec::new();
|
||||
let type_ = outform::TypeInfo {
|
||||
name: mapping.name.clone(),
|
||||
module: mapping.module.clone().unwrap(),
|
||||
details: outform::TypeDetails::Class(outform::ClassInfo {
|
||||
superclass,
|
||||
description: class.description.clone(),
|
||||
details: class.details.clone(),
|
||||
flags: {
|
||||
let mut flags = Vec::new();
|
||||
|
||||
if coreenum.is_some() {
|
||||
flags.push(Flag::Enum);
|
||||
} else if class.singleton {
|
||||
flags.push(Flag::Singleton);
|
||||
} else if class.uncreatable {
|
||||
flags.push(Flag::Uncreatable);
|
||||
}
|
||||
if coreenum.is_some() {
|
||||
flags.push(Flag::Enum);
|
||||
} else if class.singleton {
|
||||
flags.push(Flag::Singleton);
|
||||
} else if class.uncreatable {
|
||||
flags.push(Flag::Uncreatable);
|
||||
}
|
||||
|
||||
flags
|
||||
},
|
||||
properties,
|
||||
functions,
|
||||
signals,
|
||||
variants,
|
||||
flags
|
||||
},
|
||||
properties,
|
||||
functions,
|
||||
signals,
|
||||
variants,
|
||||
}),
|
||||
};
|
||||
|
||||
outtypes.insert(mapping.name.clone(), outform::TypeInfo::Class(type_));
|
||||
outtypes.insert(mapping.name.clone(), type_);
|
||||
}
|
||||
|
||||
for enum_ in typespec.enums {
|
||||
if enum_.module.as_ref().map(|v| v as &str) == Some(module) {
|
||||
outtypes.insert(
|
||||
enum_.name,
|
||||
outform::TypeInfo::Enum(outform::EnumInfo {
|
||||
outtypes.insert(enum_.name.clone(), outform::TypeInfo {
|
||||
name: enum_.name,
|
||||
module: enum_.module.unwrap(),
|
||||
details: outform::TypeDetails::Enum(outform::EnumInfo {
|
||||
description: enum_.description,
|
||||
details: enum_.details,
|
||||
variants: enum_
|
||||
|
@ -254,7 +259,7 @@ pub fn resolve_types(
|
|||
})
|
||||
.collect(),
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue