From ff5da84a8b258a9b2caaf978ddb6de23635d8903 Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Sun, 19 May 2024 02:20:32 -0700 Subject: [PATCH] typegen: Pipewire and related support changes - Added enum singleton support --- Justfile | 12 ++- layouts/shortcodes/qmltype.html | 60 +++++++-------- typegen/Cargo.lock | 40 ++++++---- typegen/Cargo.toml | 2 +- typegen/src/outform.rs | 2 + typegen/src/parse.rs | 126 ++++++++++++++++++++++++++------ typegen/src/resolver.rs | 30 ++++++-- typegen/src/typespec.rs | 1 + types/QtQuick.json | 10 +++ 9 files changed, 208 insertions(+), 75 deletions(-) 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

+ +{{- end -}} + {{- if $type.properties -}}

Properties

{{- end -}} -{{- if $type.variants -}} -

Variants

- -{{- end -}} - {{- if $type.details -}}

Detailed Description

{{- $type.details | $.Page.RenderString (dict "display" "block") -}} {{- end -}} +{{- if $type.variants -}} +

Variant Details

+ {{ range $name, $variant := $type.variants }} +
+

{{ $name -}}

+
+ +
+ {{- 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 }} -
-

{{ $name -}}

-
- -
- {{- 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",