diff --git a/Justfile b/Justfile
index f07f947..0cf08fd 100644
--- a/Justfile
+++ b/Justfile
@@ -14,8 +14,10 @@ clean:
rm -rf content/docs/types/Quickshell.Wayland
rm -rf data/modules/Quickshell.DBusMenu
rm -rf content/docs/types/Quickshell.DBusMenu
- rm -rf data/modules/Quickshell.Service.SystemTray
- rm -rf content/docs/types/Quickshell.Service.SystemTray
+ rm -rf data/modules/Quickshell.Services.SystemTray
+ rm -rf content/docs/types/Quickshell.Services.SystemTray
+ rm -rf data/modules/Quickshell.Services.PipeWire
+ rm -rf content/docs/types/Quickshell.Services.PipeWire
rm -rf data/modules/Quickshell.Hyprland
rm -rf content/docs/types/Quickshell.Hyprland
@@ -28,13 +30,15 @@ typedocs: clean buildtypegen
{{typegen_bin}} gentypes {{src_path}}/io/module.md build/types/types/Quickshell.Io.json
{{typegen_bin}} gentypes {{src_path}}/wayland/module.md build/types/types/Quickshell.Wayland.json
{{typegen_bin}} gentypes {{src_path}}/dbus/dbusmenu/module.md build/types/types/Quickshell.DBusMenu.json
- {{typegen_bin}} gentypes {{src_path}}/services/status_notifier/module.md build/types/types/Quickshell.Service.SystemTray.json
+ {{typegen_bin}} gentypes {{src_path}}/services/status_notifier/module.md build/types/types/Quickshell.Services.SystemTray.json
+ {{typegen_bin}} gentypes {{src_path}}/services/pipewire/module.md build/types/types/Quickshell.Services.PipeWire.json
{{typegen_bin}} gentypes {{src_path}}/wayland/hyprland/module.md build/types/types/Quickshell.Hyprland.json
sh -c '{{typegen_bin}} gendocs {{src_path}}/core/module.md data/modules/Quickshell content/docs/types/Quickshell types/* build/types/types/*'
sh -c '{{typegen_bin}} gendocs {{src_path}}/io/module.md data/modules/Quickshell.Io content/docs/types/Quickshell.Io types/* build/types/types/*'
sh -c '{{typegen_bin}} gendocs {{src_path}}/wayland/module.md data/modules/Quickshell.Wayland content/docs/types/Quickshell.Wayland types/* build/types/types/*'
sh -c '{{typegen_bin}} gendocs {{src_path}}/dbus/dbusmenu/module.md data/modules/Quickshell.DBusMenu content/docs/types/Quickshell.DBusMenu types/* build/types/types/*'
- sh -c '{{typegen_bin}} gendocs {{src_path}}/services/status_notifier/module.md data/modules/Quickshell.Service.SystemTray content/docs/types/Quickshell.Service.SystemTray types/* build/types/types/*'
+ sh -c '{{typegen_bin}} gendocs {{src_path}}/services/status_notifier/module.md data/modules/Quickshell.Services.SystemTray content/docs/types/Quickshell.Services.SystemTray types/* build/types/types/*'
+ sh -c '{{typegen_bin}} gendocs {{src_path}}/services/pipewire/module.md data/modules/Quickshell.Services.PipeWire content/docs/types/Quickshell.Services.PipeWire types/* build/types/types/*'
sh -c '{{typegen_bin}} gendocs {{src_path}}/wayland/hyprland/module.md data/modules/Quickshell.Hyprland content/docs/types/Quickshell.Hyprland types/* build/types/types/*'
serve: typedocs
diff --git a/layouts/shortcodes/qmltype.html b/layouts/shortcodes/qmltype.html
index 0def484..4b6cde7 100644
--- a/layouts/shortcodes/qmltype.html
+++ b/layouts/shortcodes/qmltype.html
@@ -53,6 +53,19 @@
{{ if $type.details -}} More {{- end -}}
{{- end -}}
+{{- if $type.variants -}}
+
Variants
+
+ {{- range $name, $variant := $type.variants -}}
+ -
+
+ {{ $name }}
+
+
+ {{- end -}}
+
+{{- end -}}
+
{{- if $type.properties -}}
Properties
{{- end -}}
-{{- if $type.variants -}}
-Variants
-
- {{- range $name, $variant := $type.variants -}}
- -
-
- {{ $name }}
-
-
- {{- end -}}
-
-{{- end -}}
-
{{- if $type.details -}}
Detailed Description
{{- $type.details | $.Page.RenderString (dict "display" "block") -}}
{{- end -}}
+{{- if $type.variants -}}
+ Variant Details
+ {{ range $name, $variant := $type.variants }}
+
+
+
+ {{- if $variant.details -}}
+ {{- $variant.details | $.Page.RenderString (dict "display" "block") -}}
+ {{- else -}}
+
No details provided.
+ {{- end -}}
+
+ {{- end -}}
+{{- end -}}
+
{{- if $type.properties -}}
Property Details
{{ range $propname, $prop := $type.properties }}
@@ -228,20 +245,3 @@
{{- end -}}
{{- end -}}
-
-{{- if $type.variants -}}
- Variant Details
- {{ range $name, $variant := $type.variants }}
-
-
-
- {{- if $variant.details -}}
- {{- $variant.details | $.Page.RenderString (dict "display" "block") -}}
- {{- else -}}
-
No details provided.
- {{- end -}}
-
- {{- end -}}
-{{- end -}}
diff --git a/typegen/Cargo.lock b/typegen/Cargo.lock
index 1e65307..f0bf391 100644
--- a/typegen/Cargo.lock
+++ b/typegen/Cargo.lock
@@ -17,12 +17,38 @@ version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
+[[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+[[package]]
+name = "fancy-regex"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2"
+dependencies = [
+ "bit-set",
+ "regex-automata",
+ "regex-syntax",
+]
+
[[package]]
name = "hashbrown"
version = "0.14.3"
@@ -69,18 +95,6 @@ dependencies = [
"proc-macro2",
]
-[[package]]
-name = "regex"
-version = "1.10.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-automata",
- "regex-syntax",
-]
-
[[package]]
name = "regex-automata"
version = "0.4.5"
@@ -194,7 +208,7 @@ name = "typegen"
version = "0.1.0"
dependencies = [
"anyhow",
- "regex",
+ "fancy-regex",
"serde",
"serde_json",
"toml",
diff --git a/typegen/Cargo.toml b/typegen/Cargo.toml
index c3a3e31..4062ddd 100644
--- a/typegen/Cargo.toml
+++ b/typegen/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2021"
[dependencies]
anyhow = "^1"
-regex = "^1.10"
+fancy-regex = "^0.13"
serde = { version = "^1", features = ["derive"] }
serde_json = "^1"
toml = "^0.8"
diff --git a/typegen/src/outform.rs b/typegen/src/outform.rs
index f893f56..185cc5b 100644
--- a/typegen/src/outform.rs
+++ b/typegen/src/outform.rs
@@ -27,6 +27,7 @@ pub struct ClassInfo {
pub properties: HashMap,
pub functions: Vec,
pub signals: HashMap,
+ pub variants: HashMap,
}
#[derive(Debug, Serialize)]
@@ -133,4 +134,5 @@ pub enum Flag {
Writeonly,
Singleton,
Uncreatable,
+ Enum,
}
diff --git a/typegen/src/parse.rs b/typegen/src/parse.rs
index 1abcacd..bc2d37f 100644
--- a/typegen/src/parse.rs
+++ b/typegen/src/parse.rs
@@ -1,7 +1,7 @@
use std::borrow::Cow;
use anyhow::{anyhow, bail, Context};
-use regex::Regex;
+use fancy_regex::Regex;
use serde::Deserialize;
use crate::typespec;
@@ -45,6 +45,7 @@ pub struct ClassInfo<'a> {
pub properties: Vec>,
pub invokables: Vec>,
pub signals: Vec>,
+ pub enums: Vec>,
}
#[derive(Debug)]
@@ -108,7 +109,8 @@ pub struct Parser {
pub signal_regex: Regex,
pub fn_param_regex: Regex,
pub defaultprop_classinfo_regex: Regex,
- pub enum_regex: Regex,
+ pub enum_ns_regex: Regex,
+ pub enum_class_regex: Regex,
pub enum_variant_regex: Regex,
}
@@ -130,16 +132,17 @@ impl Default for ParseContext<'_> {
impl Parser {
pub fn new() -> Self {
Self {
- class_regex: Regex::new(r#"(?(\s*\/\/\/.*\n)+)?\s*class\s+(?\w+)(?:\s*:\s*public\s+(?\w+)(\s*,(\s*\w+)*)*)?\s*\{(?[\s\S]*?)};"#).unwrap(),
+ class_regex: Regex::new(r#"(?(\s*\/\/\/.*\n)+)?\s*class\s+(?\w+)(?:\s*:\s*public\s+(?\w+)(\s*,(\s*\w+)*)*)?\s*\{(?[\s\S]*?)(?!};\s*Q_ENUM)};"#).unwrap(),
macro_regex: Regex::new(r#"(?(\s*\/\/\/.*\n)+)?\s*(?QSDOC_HIDE\s)?(?(Q|QML|QSDOC)_\w+)\s*(\(\s*(?.*)\s*\))?;"#).unwrap(),
property_regex: Regex::new(r#"^\s*(?(\w|::|, |<|>|\*)+)\*?\s+(?\w+)(\s+(MEMBER\s+(?\w+)|READ\s+(?\w+)|WRITE\s+(?\w+)|NOTIFY\s+(?\w+)|(?CONSTANT)))+\s*$"#).unwrap(),
- fn_regex: Regex::new(r#"(?(\s*\/\/\/.*\n)+)?\s*Q_INVOKABLE\s+(\[\[.*\]\]\s+)?(?(\w|::|<|>)+\*?)\s+(?\w+)\((?[\s\S]*?)\)(\s*const)?;"#).unwrap(),
+ fn_regex: Regex::new(r#"(?(\s*\/\/\/.*\n)+)?\s*Q_INVOKABLE\s+(\[\[.*\]\]\s+)?(static\s+)?(?(\w|::|<|>)+\*?)\s+(?\w+)\((?[\s\S]*?)\)(\s*const)?;"#).unwrap(),
signal_regex: Regex::new(r#"(?(\s*\/\/\/.*\n)+)?\s*void\s+(?\w+)\((?[\s\S]*?)\);"#).unwrap(),
fn_param_regex: Regex::new(r#"(const\s+)?(?(\w|::|<|>)+\*?)&?\s+(?\w+)(,|$)"#).unwrap(),
signals_regex: Regex::new(r#"signals:(?(\s*(\s*///.*\s*)*void .*;)*)"#).unwrap(),
defaultprop_classinfo_regex: Regex::new(r#"^\s*"DefaultProperty", "(?.+)"\s*$"#).unwrap(),
- enum_regex: Regex::new(r#"(?(\s*\/\/\/.*\n)+)?\s*namespace (?\w+)\s*\{[\s\S]*?(QML_ELEMENT|QML_NAMED_ELEMENT\((?\w+)\));[\s\S]*?enum\s*(?\w+)\s*\{(?[\s\S]*?)\};[\s\S]*?\}"#).unwrap(),
- enum_variant_regex: Regex::new(r#"(?(\s*\/\/\/.*\n)+)?\s*(?\w+)\s*=\s*\d+,"#).unwrap(),
+ enum_ns_regex: Regex::new(r#"(?(\s*\/\/\/.*\n)+)?\s*namespace (?\w+)\s*\{[\s\S]*?(QML_ELEMENT|QML_NAMED_ELEMENT\((?\w+)\));[\s\S]*?enum\s*(?\w+)\s*\{(?[\s\S]*?)\};[\s\S]*?\}"#).unwrap(),
+ enum_class_regex: Regex::new(r#"(?(\s*\/\/\/.*\n)+)?\s*enum\s*(?\w+)\s*\{(?[\s\S]*?)\};\s+Q_ENUM\(.+\);"#).unwrap(),
+ enum_variant_regex: Regex::new(r#"(?(\s*\/\/\/.*\n)+)?\s*(?\w+)\s*=\s*.+,"#).unwrap(),
}
}
@@ -149,6 +152,8 @@ impl Parser {
ctx: &mut ParseContext<'a>,
) -> anyhow::Result<()> {
for class in self.class_regex.captures_iter(text) {
+ let class = class?;
+
let comment = class.name("comment").map(|m| m.as_str());
let name = class.name("name").unwrap().as_str();
let mut superclass = class.name("super").map(|m| m.as_str());
@@ -163,9 +168,12 @@ impl Parser {
let mut invokables = Vec::new();
let mut notify_signals = Vec::new();
let mut signals = Vec::new();
+ let mut enums = Vec::new();
(|| {
for macro_ in self.macro_regex.captures_iter(body) {
+ let macro_ = macro_?;
+
if macro_.name("hide").is_some() {
continue
}
@@ -196,7 +204,7 @@ impl Parser {
self.property_regex
.captures(args.ok_or_else(|| {
anyhow!("expected args for Q_PROPERTY")
- })?)
+ })?)?
.ok_or_else(|| anyhow!("unable to parse Q_PROPERTY"))?;
let member = prop.name("member").is_some();
@@ -220,7 +228,7 @@ impl Parser {
"Q_CLASSINFO" => {
let classinfo = self.defaultprop_classinfo_regex.captures(
args.ok_or_else(|| anyhow!("expected args for Q_CLASSINFO"))?,
- );
+ )?;
if let Some(classinfo) = classinfo {
let prop = classinfo.name("prop").unwrap().as_str();
@@ -246,6 +254,8 @@ impl Parser {
}
for invokable in self.fn_regex.captures_iter(body) {
+ let invokable = invokable?;
+
let comment = invokable.name("comment").map(|m| m.as_str());
let type_ = invokable.name("type").unwrap().as_str();
let name = invokable.name("name").unwrap().as_str();
@@ -254,6 +264,8 @@ impl Parser {
let mut params = Vec::new();
for param in self.fn_param_regex.captures_iter(params_raw) {
+ let param = param?;
+
let type_ = param.name("type").unwrap().as_str();
let name = param.name("name").unwrap().as_str();
@@ -269,9 +281,13 @@ impl Parser {
}
for signal_set in self.signals_regex.captures_iter(body) {
+ let signal_set = signal_set?;
+
let signals_body = signal_set.name("signals").unwrap().as_str();
for signal in self.signal_regex.captures_iter(signals_body) {
+ let signal = signal?;
+
if signal.name("invokable").is_some() {
continue;
}
@@ -287,6 +303,8 @@ impl Parser {
let mut params = Vec::new();
for param in self.fn_param_regex.captures_iter(params_raw) {
+ let param = param?;
+
let type_ = param.name("type").unwrap().as_str();
let name = param.name("name").unwrap().as_str();
@@ -301,6 +319,23 @@ impl Parser {
}
}
+ for enum_ in self.enum_class_regex.captures_iter(body) {
+ let enum_ = enum_?;
+
+ 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)?;
+
+ enums.push(EnumInfo {
+ namespace: name,
+ enum_name,
+ qml_name: enum_name,
+ comment,
+ variants,
+ });
+ }
+
Ok::<_, anyhow::Error>(())
})()
.with_context(|| format!("while parsing class `{name}`"))?;
@@ -318,6 +353,7 @@ impl Parser {
properties,
invokables,
signals,
+ enums,
});
}
@@ -325,7 +361,9 @@ impl Parser {
}
pub fn parse_enums<'a>(&self, text: &'a str, ctx: &mut ParseContext<'a>) -> anyhow::Result<()> {
- for enum_ in self.enum_regex.captures_iter(text) {
+ for enum_ in self.enum_ns_regex.captures_iter(text) {
+ let enum_ = enum_?;
+
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();
@@ -334,15 +372,7 @@ impl Parser {
.map(|m| m.as_str())
.unwrap_or(namespace);
let body = enum_.name("body").unwrap().as_str();
-
- let mut variants = Vec::new();
-
- for variant in self.enum_variant_regex.captures_iter(body) {
- let comment = variant.name("comment").map(|m| m.as_str());
- let name = variant.name("name").unwrap().as_str();
-
- variants.push(Variant { name, comment });
- }
+ let variants = self.parse_enum_variants(body)?;
ctx.enums.push(EnumInfo {
namespace,
@@ -356,6 +386,21 @@ impl Parser {
Ok(())
}
+ pub fn parse_enum_variants<'a>(&self, body: &'a str) -> anyhow::Result>> {
+ let mut variants = Vec::new();
+
+ for variant in self.enum_variant_regex.captures_iter(body) {
+ let variant = variant?;
+
+ let comment = variant.name("comment").map(|m| m.as_str());
+ let name = variant.name("name").unwrap().as_str();
+
+ variants.push(Variant { name, comment });
+ }
+
+ Ok(variants)
+ }
+
pub fn parse<'a>(&self, text: &'a str, ctx: &mut ParseContext<'a>) -> anyhow::Result<()> {
self.parse_classes(text, ctx)?;
self.parse_enums(text, ctx)?;
@@ -370,13 +415,28 @@ impl ParseContext<'_> {
typemap: self
.classes
.iter()
- .filter_map(|class| {
- Some(typespec::QmlTypeMapping {
+ .flat_map(|class| {
+ let Some(qmlname) = class.qml_name else { return Vec::new() };
+
+ let mut classes = Vec::new();
+ classes.push(typespec::QmlTypeMapping {
// filters gadgets
- name: class.qml_name?.to_string(),
+ name: qmlname.to_string(),
cname: class.name.to_string(),
module: Some(module.to_string()),
- })
+ });
+
+ // dirty hack to fix unknowns in resolution
+ if let Some(e) = class.enums.iter().find(|e| e.enum_name == "Enum") {
+ classes.push(typespec::QmlTypeMapping {
+ // filters gadgets
+ name: qmlname.to_string(),
+ cname: format!("{}::{}", e.namespace, e.enum_name),
+ module: Some(module.to_string()),
+ });
+ }
+
+ classes
})
.collect(),
classes: self
@@ -400,6 +460,28 @@ impl ParseContext<'_> {
properties: class.properties.iter().map(|p| (*p).into()).collect(),
functions: class.invokables.iter().map(|f| f.as_typespec()).collect(),
signals: class.signals.iter().map(|s| s.as_typespec()).collect(),
+ enums: class
+ .enums
+ .iter()
+ .map(|enum_| {
+ let (description, details) = enum_
+ .comment
+ .map(parse_details_desc)
+ .unwrap_or((None, None));
+
+ typespec::Enum {
+ name: enum_.qml_name.to_string(),
+ module: Some(module.to_string()),
+ cname: Some(format!(
+ "{}::{}",
+ enum_.namespace, enum_.enum_name
+ )),
+ description,
+ details,
+ varaints: enum_.variants.iter().map(|v| (*v).into()).collect(),
+ }
+ })
+ .collect(),
})
})
.collect(),
diff --git a/typegen/src/resolver.rs b/typegen/src/resolver.rs
index af43653..e89ac95 100644
--- a/typegen/src/resolver.rs
+++ b/typegen/src/resolver.rs
@@ -119,11 +119,14 @@ pub fn resolve_types(
list = true;
} else if ctype.starts_with("QList<") {
ctype = &ctype[6..ctype.len() - 1];
- if ctype.ends_with('*') {
- ctype = &ctype[0..ctype.len() - 1];
- }
-
list = true;
+ } else if ctype.starts_with("QVector<") {
+ ctype = &ctype[8..ctype.len() - 1];
+ list = true;
+ }
+
+ if ctype.ends_with('*') {
+ ctype = &ctype[0..ctype.len() - 1];
}
let mut type_ = qmlparamtype(ctype, typespec);
@@ -218,6 +221,20 @@ pub fn resolve_types(
.map(|signal| (signal.name.clone(), solvesignal(signal, &typespec)))
.collect::>();
+ let coreenum = class.enums.iter().find(|e| e.name == "Enum");
+ let variants = match coreenum {
+ Some(e) => e
+ .varaints
+ .iter()
+ .map(|variant| {
+ (variant.name.clone(), outform::Variant {
+ details: variant.details.clone(),
+ })
+ })
+ .collect(),
+ None => HashMap::new(),
+ };
+
let type_ = outform::ClassInfo {
superclass,
description: class.description.clone(),
@@ -225,7 +242,9 @@ pub fn resolve_types(
flags: {
let mut flags = Vec::new();
- if class.singleton {
+ if coreenum.is_some() {
+ flags.push(Flag::Enum);
+ } else if class.singleton {
flags.push(Flag::Singleton);
} else if class.uncreatable {
flags.push(Flag::Uncreatable);
@@ -236,6 +255,7 @@ pub fn resolve_types(
properties,
functions,
signals,
+ variants,
};
outtypes.insert(mapping.name.clone(), outform::TypeInfo::Class(type_));
diff --git a/typegen/src/typespec.rs b/typegen/src/typespec.rs
index bbc27d3..481829e 100644
--- a/typegen/src/typespec.rs
+++ b/typegen/src/typespec.rs
@@ -38,6 +38,7 @@ pub struct Class {
pub properties: Vec,
pub functions: Vec,
pub signals: Vec,
+ pub enums: Vec,
}
#[derive(Debug, Serialize, Deserialize)]
diff --git a/types/QtQuick.json b/types/QtQuick.json
index 8282009..29df6a3 100644
--- a/types/QtQuick.json
+++ b/types/QtQuick.json
@@ -20,11 +20,21 @@
"cname": "qint32",
"module": null
},
+ {
+ "name": "int",
+ "cname": "quint32",
+ "module": null
+ },
{
"name": "real",
"cname": "qreal",
"module": null
},
+ {
+ "name": "real",
+ "cname": "float",
+ "module": null
+ },
{
"name": "point",
"cname": "QPointF",