initial commit
This commit is contained in:
		
						commit
						27b3274027
					
				
					 25 changed files with 1696 additions and 0 deletions
				
			
		
							
								
								
									
										12
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,12 @@
 | 
				
			||||||
 | 
					# direnv
 | 
				
			||||||
 | 
					/.envrc
 | 
				
			||||||
 | 
					/.direnv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# typegen
 | 
				
			||||||
 | 
					/typegen/target
 | 
				
			||||||
 | 
					/build
 | 
				
			||||||
 | 
					/content/docs/types/QuickShell
 | 
				
			||||||
 | 
					/data/modules
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# hugo
 | 
				
			||||||
 | 
					/.hugo_build.lock
 | 
				
			||||||
							
								
								
									
										3
									
								
								.gitmodules
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					[submodule "themes/hextra"]
 | 
				
			||||||
 | 
						path = themes/hextra
 | 
				
			||||||
 | 
						url = https://github.com/imfing/hextra.git
 | 
				
			||||||
							
								
								
									
										13
									
								
								Justfile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								Justfile
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					clean:
 | 
				
			||||||
 | 
						rm -rf build
 | 
				
			||||||
 | 
						rm -rf data/modules/QuickShell
 | 
				
			||||||
 | 
						rm -rf content/docs/types/QuickShell
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedocs: clean
 | 
				
			||||||
 | 
						cd typegen && cargo build
 | 
				
			||||||
 | 
						mkdir -p build/types/types
 | 
				
			||||||
 | 
						./typegen/target/debug/typegen gentypes ../src/cpp/module.md build/types/types/QuickShell.json
 | 
				
			||||||
 | 
						sh -c './typegen/target/debug/typegen gendocs ../src/cpp/module.md data/modules/QuickShell content/docs/types/QuickShell types/* build/types/types/*'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					serve: typedocs
 | 
				
			||||||
 | 
						hugo server --buildDrafts --disableFastRender
 | 
				
			||||||
							
								
								
									
										47
									
								
								assets/css/custom.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								assets/css/custom.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,47 @@
 | 
				
			||||||
 | 
					[id] {
 | 
				
			||||||
 | 
						scroll-margin-top: var(--navbar-height);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.nav-container-blur {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.content a:not(:hover) {
 | 
				
			||||||
 | 
						text-decoration-line: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.hextra-scrollbar .font-semibold {
 | 
				
			||||||
 | 
						font-weight: normal;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.content ul {
 | 
				
			||||||
 | 
						margin-top: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.typegray {
 | 
				
			||||||
 | 
						color: #999999;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.qmlpropdef {
 | 
				
			||||||
 | 
						background-color: rgb(0 0 0 / .05);
 | 
				
			||||||
 | 
						font-weight: normal;
 | 
				
			||||||
 | 
						font-size: 1.2rem;
 | 
				
			||||||
 | 
						border-radius: 5px;
 | 
				
			||||||
 | 
						margin: 5px 0;
 | 
				
			||||||
 | 
						padding: 5px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					:is(html[class~="dark"] .qmlpropdef) {
 | 
				
			||||||
 | 
						background-color: #55555530;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.qmlpropdef > p {
 | 
				
			||||||
 | 
						padding: 0;
 | 
				
			||||||
 | 
						margin: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.qmlpropdetails {
 | 
				
			||||||
 | 
						margin-left: 8px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.qmlprops {
 | 
				
			||||||
 | 
						float: right;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										9
									
								
								content/_index.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								content/_index.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					+++
 | 
				
			||||||
 | 
					+++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# QuickShell
 | 
				
			||||||
 | 
					QuickShell is a fully user customizable desktop shell based on QtQuick.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{{< cards >}}
 | 
				
			||||||
 | 
					  {{< card link="/docs/types" title="Type Reference" >}}
 | 
				
			||||||
 | 
					{{< /cards >}}
 | 
				
			||||||
							
								
								
									
										3
									
								
								content/docs/_index.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								content/docs/_index.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					+++
 | 
				
			||||||
 | 
					title = 'Docs'
 | 
				
			||||||
 | 
					+++
 | 
				
			||||||
							
								
								
									
										7
									
								
								content/docs/types/_index.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								content/docs/types/_index.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					+++
 | 
				
			||||||
 | 
					title = "Type Reference"
 | 
				
			||||||
 | 
					+++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Index of all QuickShell types. See [the QtQuick type reference](https://doc.qt.io/qt-6/qtquick-qmlmodule.html) for builtin Qt types.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{{< qmlmodulelisting >}}
 | 
				
			||||||
							
								
								
									
										36
									
								
								hugo.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								hugo.toml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,36 @@
 | 
				
			||||||
 | 
					baseURL = "https://example.org/"
 | 
				
			||||||
 | 
					languageCode = "en-us"
 | 
				
			||||||
 | 
					title = "QuickShell"
 | 
				
			||||||
 | 
					theme = "hextra"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[params.theme]
 | 
				
			||||||
 | 
					default = "dark"
 | 
				
			||||||
 | 
					displayToggle = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[params.search]
 | 
				
			||||||
 | 
					enable = true
 | 
				
			||||||
 | 
					type = "flexsearch"
 | 
				
			||||||
 | 
					flexsearch.index = "content"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[markup] # required by theme to render properly
 | 
				
			||||||
 | 
					goldmark.renderer.unsafe = true
 | 
				
			||||||
 | 
					highlight.noClasses = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[menu.sidebar]]
 | 
				
			||||||
 | 
					name = "QtQuick Type Reference ↗"
 | 
				
			||||||
 | 
					url = "https://doc.qt.io/qt-6/qtquick-qmlmodule.html"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[menu.main]]
 | 
				
			||||||
 | 
					name = "Docs"
 | 
				
			||||||
 | 
					pageRef = "/docs"
 | 
				
			||||||
 | 
					weight = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[menu.main]]
 | 
				
			||||||
 | 
					name = "Types"
 | 
				
			||||||
 | 
					pageRef = "/docs/types"
 | 
				
			||||||
 | 
					weight = 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[menu.main]]
 | 
				
			||||||
 | 
					name = "Search"
 | 
				
			||||||
 | 
					weight = 3
 | 
				
			||||||
 | 
					params.type = "search"
 | 
				
			||||||
							
								
								
									
										18
									
								
								layouts/docs/single.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								layouts/docs/single.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 }}
 | 
				
			||||||
							
								
								
									
										6
									
								
								layouts/partials/qmlparams.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								layouts/partials/qmlparams.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					{{- $first := true -}}
 | 
				
			||||||
 | 
					{{- range $param, $type := . -}}
 | 
				
			||||||
 | 
						{{- if ne $first true -}}, {{ end -}}
 | 
				
			||||||
 | 
						{{- $first = false -}}
 | 
				
			||||||
 | 
						{{ $param }}: {{ partial "qmltype.html" $type }}
 | 
				
			||||||
 | 
					{{- end -}}
 | 
				
			||||||
							
								
								
									
										17
									
								
								layouts/partials/qmltype.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								layouts/partials/qmltype.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					{{- if eq .type "unknown" -}}
 | 
				
			||||||
 | 
						unknown
 | 
				
			||||||
 | 
					{{- else -}}
 | 
				
			||||||
 | 
						{{- $link := "#ERROR" -}}
 | 
				
			||||||
 | 
						{{- if eq .type "qt" -}}
 | 
				
			||||||
 | 
							{{- $link = printf "https://doc.qt.io/qt-6/%s-%s.html" (lower (replace .module "." "-")) (lower .name) -}}
 | 
				
			||||||
 | 
						{{- else if eq .type "local" -}}
 | 
				
			||||||
 | 
							{{- $link = printf "/docs/types/%s/%s" (lower .module) (lower .name) -}}
 | 
				
			||||||
 | 
						{{- end -}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{{- $of := "" -}}
 | 
				
			||||||
 | 
						{{- if .of -}}
 | 
				
			||||||
 | 
							{{- $of = printf "<%s>" (partial "qmltype.html" .of) }}
 | 
				
			||||||
 | 
						{{- end -}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<a href="{{ $link }}">{{ .name }}</a>{{ $of | safeHTML -}}
 | 
				
			||||||
 | 
					{{- end -}}
 | 
				
			||||||
							
								
								
									
										14
									
								
								layouts/shortcodes/qmlmodule.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								layouts/shortcodes/qmlmodule.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					{{- $modulename := .Get "module" -}}
 | 
				
			||||||
 | 
					{{- $module := index .Site.Data.modules $modulename -}}
 | 
				
			||||||
 | 
					{{- $module.index.description | $.Page.RenderString (dict "display" "block") -}}
 | 
				
			||||||
 | 
					<h3>Types</h3>
 | 
				
			||||||
 | 
					<table>
 | 
				
			||||||
 | 
						{{- range $name, $type := $module -}}
 | 
				
			||||||
 | 
							{{- if ne $name "index" -}}
 | 
				
			||||||
 | 
								<tr>
 | 
				
			||||||
 | 
									<td><a href="/docs/types/{{ lower $modulename }}/{{ lower $name }}">{{ $name }}</a></td>
 | 
				
			||||||
 | 
									<td>{{ $type.description }}</td>
 | 
				
			||||||
 | 
								</tr>
 | 
				
			||||||
 | 
							{{- end -}}
 | 
				
			||||||
 | 
						{{- end -}}
 | 
				
			||||||
 | 
					</table>
 | 
				
			||||||
							
								
								
									
										8
									
								
								layouts/shortcodes/qmlmodulelisting.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								layouts/shortcodes/qmlmodulelisting.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					<table>
 | 
				
			||||||
 | 
						{{- range $name, $module := .Site.Data.modules -}}
 | 
				
			||||||
 | 
							<tr>
 | 
				
			||||||
 | 
								<td><a href="/docs/types/{{ lower $name }}">{{ $name }}</a></td>
 | 
				
			||||||
 | 
								<td>{{ $module.index.shortDescription }}</td>
 | 
				
			||||||
 | 
							</tr>
 | 
				
			||||||
 | 
						{{- end -}}
 | 
				
			||||||
 | 
					</table>
 | 
				
			||||||
							
								
								
									
										204
									
								
								layouts/shortcodes/qmltype.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								layouts/shortcodes/qmltype.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,204 @@
 | 
				
			||||||
 | 
					{{ $modulename := .Get "module" }}
 | 
				
			||||||
 | 
					{{ $typename := .Get "type" }}
 | 
				
			||||||
 | 
					{{ $type := index .Site.Data.modules $modulename $typename }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{{- define "partials/qmltypeflag.html" -}}
 | 
				
			||||||
 | 
						{{- if eq . "default" -}}
 | 
				
			||||||
 | 
							<span title="Components written directly into an object are collected in its default property.">{{ . }}</span>
 | 
				
			||||||
 | 
						{{- else if eq . "singleton" -}}
 | 
				
			||||||
 | 
							<span title="Only one instance of this type exists, accessible by its name.">{{ . }}</span>
 | 
				
			||||||
 | 
						{{- else if eq . "uncreatable" -}}
 | 
				
			||||||
 | 
							<span title="This type cannot be manually created.">{{ . }}</span>
 | 
				
			||||||
 | 
						{{- else if eq . "readonly" -}}
 | 
				
			||||||
 | 
							<span title="This property cannot be assigned to, only read from.">{{ . }}</span>
 | 
				
			||||||
 | 
						{{- else if eq . "writeonly" -}}
 | 
				
			||||||
 | 
							<span title="This property cannot be read, only assigned.">{{ . }}</span>
 | 
				
			||||||
 | 
						{{- else -}}
 | 
				
			||||||
 | 
							<span>{{ . }}</span>
 | 
				
			||||||
 | 
						{{- end -}}
 | 
				
			||||||
 | 
					{{- end -}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{{- define "partials/qmltypeflags.html" -}}
 | 
				
			||||||
 | 
							{{- $first := true -}}
 | 
				
			||||||
 | 
							[
 | 
				
			||||||
 | 
							{{- range $flag := . }}
 | 
				
			||||||
 | 
								{{- if not $first -}}, {{ end -}}
 | 
				
			||||||
 | 
								{{- $first = false -}}
 | 
				
			||||||
 | 
								{{ partial "qmltypeflag.html" $flag }}
 | 
				
			||||||
 | 
							{{- end -}}
 | 
				
			||||||
 | 
							]
 | 
				
			||||||
 | 
					{{- end -}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<h1>
 | 
				
			||||||
 | 
						{{ $typename -}}
 | 
				
			||||||
 | 
						<span class="typegray" style="font-weight: normal">
 | 
				
			||||||
 | 
							{{- if eq $type.type "class" -}}
 | 
				
			||||||
 | 
								: {{ partial "qmltype.html" $type.super }}
 | 
				
			||||||
 | 
								{{ if $type.flags -}}
 | 
				
			||||||
 | 
									<span class="qmlprops">{{ partial "qmltypeflags.html" $type.flags }}</span>
 | 
				
			||||||
 | 
								{{- end -}}
 | 
				
			||||||
 | 
							{{- else if eq $type.type "enum" -}}
 | 
				
			||||||
 | 
								<span class="qmlprops">[enum]</span>
 | 
				
			||||||
 | 
							{{- end -}}
 | 
				
			||||||
 | 
						</span>
 | 
				
			||||||
 | 
					</h1>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<code>
 | 
				
			||||||
 | 
						<i>import {{ $modulename }}</i>
 | 
				
			||||||
 | 
					</code>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{{- if $type.description -}}
 | 
				
			||||||
 | 
						<br><br>
 | 
				
			||||||
 | 
						{{- $type.description | $.Page.RenderString (dict "display" "inline") }}
 | 
				
			||||||
 | 
						{{ if $type.details -}} <a href="#details">More</a> {{- end -}}
 | 
				
			||||||
 | 
					{{- end -}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{{- if $type.properties -}}
 | 
				
			||||||
 | 
					<h4>Properties</h4>
 | 
				
			||||||
 | 
					<ul>
 | 
				
			||||||
 | 
						{{- range $propname, $prop := $type.properties -}}
 | 
				
			||||||
 | 
						<li>
 | 
				
			||||||
 | 
							<span class="typegray">
 | 
				
			||||||
 | 
								<a href="#prop.{{ $propname }}">{{ $propname }}</a>
 | 
				
			||||||
 | 
								{{- if not $prop.type.gadget -}}
 | 
				
			||||||
 | 
									: {{ partial "qmltype.html" $prop.type }}
 | 
				
			||||||
 | 
								{{- end -}}
 | 
				
			||||||
 | 
								{{ if in $prop.flags "default" }}
 | 
				
			||||||
 | 
									[{{ partial "qmltypeflag.html" "default" }}]
 | 
				
			||||||
 | 
								{{ end }}
 | 
				
			||||||
 | 
							</span>
 | 
				
			||||||
 | 
							{{- if $prop.type.gadget -}}
 | 
				
			||||||
 | 
								<ul>
 | 
				
			||||||
 | 
									{{- range $gadgetname, $gadgettype := $prop.type.gadget -}}
 | 
				
			||||||
 | 
										<li>
 | 
				
			||||||
 | 
											<a href="#prop.{{ $propname }}">{{ $propname }}.{{ $gadgetname }}</a>:
 | 
				
			||||||
 | 
											{{ partial "qmltype.html" $gadgettype }}
 | 
				
			||||||
 | 
										</li>
 | 
				
			||||||
 | 
									{{- end -}}
 | 
				
			||||||
 | 
								</ul>
 | 
				
			||||||
 | 
							{{- end -}}
 | 
				
			||||||
 | 
						</li>
 | 
				
			||||||
 | 
						{{- end -}}
 | 
				
			||||||
 | 
					</ul>
 | 
				
			||||||
 | 
					{{- end -}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{{- if $type.functions -}}
 | 
				
			||||||
 | 
					<h4>Functions</h4>
 | 
				
			||||||
 | 
					<ul>
 | 
				
			||||||
 | 
						{{- range $funcname, $func := $type.functions -}}
 | 
				
			||||||
 | 
						<li>
 | 
				
			||||||
 | 
							<span class="typegray">
 | 
				
			||||||
 | 
								{{ partial "qmltype.html" $func.ret }}
 | 
				
			||||||
 | 
								<a href="#func.{{ $funcname }}">{{ $funcname }}</a>(
 | 
				
			||||||
 | 
									{{- partial "qmlparams.html" $func.params -}}
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							</span>
 | 
				
			||||||
 | 
						</li>
 | 
				
			||||||
 | 
						{{- end -}}
 | 
				
			||||||
 | 
					</ul>
 | 
				
			||||||
 | 
					{{- end -}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{{- if $type.variants -}}
 | 
				
			||||||
 | 
					<h4>Variants</h4>
 | 
				
			||||||
 | 
					<ul>
 | 
				
			||||||
 | 
						{{- range $name, $variant := $type.variants -}}
 | 
				
			||||||
 | 
						<li>
 | 
				
			||||||
 | 
							<span class="typegray">
 | 
				
			||||||
 | 
								<a href="#variant.{{ $name }}">{{ $name }}</a>
 | 
				
			||||||
 | 
							</span>
 | 
				
			||||||
 | 
						</li>
 | 
				
			||||||
 | 
						{{- end -}}
 | 
				
			||||||
 | 
					</ul>
 | 
				
			||||||
 | 
					{{- end -}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{{- if $type.details -}}
 | 
				
			||||||
 | 
						<h3 id="details">Detailed Description</h3>
 | 
				
			||||||
 | 
						{{- $type.details | $.Page.RenderString (dict "display" "block") -}}
 | 
				
			||||||
 | 
					{{- end -}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{{- if $type.properties -}}
 | 
				
			||||||
 | 
						<h3>Property Details</h3>
 | 
				
			||||||
 | 
						{{ range $propname, $prop := $type.properties }}
 | 
				
			||||||
 | 
							<div id="prop.{{ $propname }}" class = "qmlpropdef">
 | 
				
			||||||
 | 
								{{- if $prop.flags -}}
 | 
				
			||||||
 | 
									<span class="qmlprops typegray">
 | 
				
			||||||
 | 
										{{ partial "qmltypeflags.html" $prop.flags }}
 | 
				
			||||||
 | 
									</span>
 | 
				
			||||||
 | 
								{{- end -}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								{{- if $prop.type.gadget -}}
 | 
				
			||||||
 | 
									{{- range $gadgetname, $gadgettype := $prop.type.gadget -}}
 | 
				
			||||||
 | 
										<p id="prop.{{ $propname }}.{{ $gadgetname }}">
 | 
				
			||||||
 | 
										{{ $propname }}.{{ $gadgetname -}}
 | 
				
			||||||
 | 
										<span class="typegray">:
 | 
				
			||||||
 | 
											{{ partial "qmltype.html" $gadgettype -}}
 | 
				
			||||||
 | 
										<span>
 | 
				
			||||||
 | 
										</p>
 | 
				
			||||||
 | 
									{{- end -}}
 | 
				
			||||||
 | 
								{{- else -}}
 | 
				
			||||||
 | 
									<p>
 | 
				
			||||||
 | 
									{{ $propname -}}
 | 
				
			||||||
 | 
									<span class="typegray">:
 | 
				
			||||||
 | 
										{{ partial "qmltype.html" $prop.type -}}
 | 
				
			||||||
 | 
									<span>
 | 
				
			||||||
 | 
									</p>
 | 
				
			||||||
 | 
								{{- end -}}
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<div class="qmlpropdetails">
 | 
				
			||||||
 | 
								{{- if $prop.details -}}
 | 
				
			||||||
 | 
									{{- $prop.details | $.Page.RenderString (dict "display" "block") -}}
 | 
				
			||||||
 | 
								{{- else -}}
 | 
				
			||||||
 | 
									<p style="color: #999999"><i>No details provided.</i></p>
 | 
				
			||||||
 | 
								{{- end -}}
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
						{{- end -}}
 | 
				
			||||||
 | 
					{{- end -}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{{- if $type.functions -}}
 | 
				
			||||||
 | 
						<h3>Function Details</h3>
 | 
				
			||||||
 | 
						{{ range $funcname, $func := $type.functions }}
 | 
				
			||||||
 | 
							<div id="func.{{ $funcname }}" class = "qmlpropdef">
 | 
				
			||||||
 | 
								{{- if $func.flags -}}
 | 
				
			||||||
 | 
									<span class="qmlprops typegray">
 | 
				
			||||||
 | 
										{{ partial "qmltypeflags.html" $func.flags }}
 | 
				
			||||||
 | 
									</span>
 | 
				
			||||||
 | 
								{{- end -}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<p>
 | 
				
			||||||
 | 
									<span class="typegray">
 | 
				
			||||||
 | 
										{{ partial "qmltype.html" $func.ret -}}
 | 
				
			||||||
 | 
									</span>
 | 
				
			||||||
 | 
									{{ $funcname -}}
 | 
				
			||||||
 | 
									<span class="typegray">(
 | 
				
			||||||
 | 
										{{- partial "qmlparams.html" $func.params -}}
 | 
				
			||||||
 | 
									)</span>
 | 
				
			||||||
 | 
								</p>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<div class="qmlpropdetails">
 | 
				
			||||||
 | 
								{{- if $func.details -}}
 | 
				
			||||||
 | 
									{{- $func.details | $.Page.RenderString (dict "display" "block") -}}
 | 
				
			||||||
 | 
								{{- else -}}
 | 
				
			||||||
 | 
									<p style="color: #999999"><i>No details provided.</i></p>
 | 
				
			||||||
 | 
								{{- end -}}
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
						{{- end -}}
 | 
				
			||||||
 | 
					{{- end -}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{{- if $type.variants -}}
 | 
				
			||||||
 | 
						<h3>Variant Details</h3>
 | 
				
			||||||
 | 
						{{ range $name, $variant := $type.variants }}
 | 
				
			||||||
 | 
							<div id="variant.{{ $name }}" class = "qmlpropdef">
 | 
				
			||||||
 | 
								<p>{{ $name -}}</p>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<div class="qmlpropdetails">
 | 
				
			||||||
 | 
								{{- if $variant.details -}}
 | 
				
			||||||
 | 
									{{- $variant.details | $.Page.RenderString (dict "display" "block") -}}
 | 
				
			||||||
 | 
								{{- else -}}
 | 
				
			||||||
 | 
									<p style="color: #999999"><i>No details provided.</i></p>
 | 
				
			||||||
 | 
								{{- end -}}
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
						{{- end -}}
 | 
				
			||||||
 | 
					{{- end -}}
 | 
				
			||||||
							
								
								
									
										22
									
								
								shell.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								shell.nix
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					{ pkgs ? import <nixpkgs> {} }: let
 | 
				
			||||||
 | 
					  # rustfmt unstable
 | 
				
			||||||
 | 
					  fenix = import (pkgs.fetchFromGitHub {
 | 
				
			||||||
 | 
					    owner = "nix-community";
 | 
				
			||||||
 | 
					    repo = "fenix";
 | 
				
			||||||
 | 
					    rev = "3776d0e2a30184cc6a0ba20fb86dc6df5b41fccd";
 | 
				
			||||||
 | 
					    sha256 = "K8QDx8UgbvGdENuvPvcsCXcd8brd55OkRDFLBT7xUVY=";
 | 
				
			||||||
 | 
					  }) {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  rust-toolchain = fenix.complete.withComponents [
 | 
				
			||||||
 | 
					    "cargo"
 | 
				
			||||||
 | 
					    "rustc"
 | 
				
			||||||
 | 
					    "clippy"
 | 
				
			||||||
 | 
					    "rustfmt"
 | 
				
			||||||
 | 
					  ];
 | 
				
			||||||
 | 
					in pkgs.mkShell {
 | 
				
			||||||
 | 
					  buildInputs = with pkgs; [
 | 
				
			||||||
 | 
					    just
 | 
				
			||||||
 | 
					    hugo
 | 
				
			||||||
 | 
					    rust-toolchain
 | 
				
			||||||
 | 
					  ];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										1
									
								
								themes/hextra
									
										
									
									
									
										Submodule
									
								
							
							
						
						
									
										1
									
								
								themes/hextra
									
										
									
									
									
										Submodule
									
								
							| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					Subproject commit 7191e259584e339b499a63e2e60cb6d818222e18
 | 
				
			||||||
							
								
								
									
										216
									
								
								typegen/Cargo.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								typegen/Cargo.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,216 @@
 | 
				
			||||||
 | 
					# This file is automatically @generated by Cargo.
 | 
				
			||||||
 | 
					# It is not intended for manual editing.
 | 
				
			||||||
 | 
					version = 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "aho-corasick"
 | 
				
			||||||
 | 
					version = "1.1.2"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "memchr",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "anyhow"
 | 
				
			||||||
 | 
					version = "1.0.79"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "equivalent"
 | 
				
			||||||
 | 
					version = "1.0.1"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "hashbrown"
 | 
				
			||||||
 | 
					version = "0.14.3"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "indexmap"
 | 
				
			||||||
 | 
					version = "2.2.2"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "equivalent",
 | 
				
			||||||
 | 
					 "hashbrown",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "itoa"
 | 
				
			||||||
 | 
					version = "1.0.10"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "memchr"
 | 
				
			||||||
 | 
					version = "2.7.1"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "proc-macro2"
 | 
				
			||||||
 | 
					version = "1.0.78"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "unicode-ident",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "quote"
 | 
				
			||||||
 | 
					version = "1.0.35"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
 | 
				
			||||||
 | 
					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"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "aho-corasick",
 | 
				
			||||||
 | 
					 "memchr",
 | 
				
			||||||
 | 
					 "regex-syntax",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "regex-syntax"
 | 
				
			||||||
 | 
					version = "0.8.2"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "ryu"
 | 
				
			||||||
 | 
					version = "1.0.16"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "serde"
 | 
				
			||||||
 | 
					version = "1.0.196"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "serde_derive",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "serde_derive"
 | 
				
			||||||
 | 
					version = "1.0.196"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "proc-macro2",
 | 
				
			||||||
 | 
					 "quote",
 | 
				
			||||||
 | 
					 "syn",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "serde_json"
 | 
				
			||||||
 | 
					version = "1.0.113"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "itoa",
 | 
				
			||||||
 | 
					 "ryu",
 | 
				
			||||||
 | 
					 "serde",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "serde_spanned"
 | 
				
			||||||
 | 
					version = "0.6.5"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "serde",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "syn"
 | 
				
			||||||
 | 
					version = "2.0.48"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "proc-macro2",
 | 
				
			||||||
 | 
					 "quote",
 | 
				
			||||||
 | 
					 "unicode-ident",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "toml"
 | 
				
			||||||
 | 
					version = "0.8.10"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "serde",
 | 
				
			||||||
 | 
					 "serde_spanned",
 | 
				
			||||||
 | 
					 "toml_datetime",
 | 
				
			||||||
 | 
					 "toml_edit",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "toml_datetime"
 | 
				
			||||||
 | 
					version = "0.6.5"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "serde",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "toml_edit"
 | 
				
			||||||
 | 
					version = "0.22.4"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "0c9ffdf896f8daaabf9b66ba8e77ea1ed5ed0f72821b398aba62352e95062951"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "indexmap",
 | 
				
			||||||
 | 
					 "serde",
 | 
				
			||||||
 | 
					 "serde_spanned",
 | 
				
			||||||
 | 
					 "toml_datetime",
 | 
				
			||||||
 | 
					 "winnow",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "typegen"
 | 
				
			||||||
 | 
					version = "0.1.0"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "anyhow",
 | 
				
			||||||
 | 
					 "regex",
 | 
				
			||||||
 | 
					 "serde",
 | 
				
			||||||
 | 
					 "serde_json",
 | 
				
			||||||
 | 
					 "toml",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "unicode-ident"
 | 
				
			||||||
 | 
					version = "1.0.12"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "winnow"
 | 
				
			||||||
 | 
					version = "0.5.37"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "a7cad8365489051ae9f054164e459304af2e7e9bb407c958076c8bf4aef52da5"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "memchr",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
							
								
								
									
										11
									
								
								typegen/Cargo.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								typegen/Cargo.toml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					[package]
 | 
				
			||||||
 | 
					name = "typegen"
 | 
				
			||||||
 | 
					version = "0.1.0"
 | 
				
			||||||
 | 
					edition = "2021"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dependencies]
 | 
				
			||||||
 | 
					anyhow = "^1"
 | 
				
			||||||
 | 
					regex = "^1.10"
 | 
				
			||||||
 | 
					serde = { version = "^1", features = ["derive"] }
 | 
				
			||||||
 | 
					serde_json = "^1"
 | 
				
			||||||
 | 
					toml = "^0.8"
 | 
				
			||||||
							
								
								
									
										21
									
								
								typegen/rustfmt.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								typegen/rustfmt.toml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,21 @@
 | 
				
			||||||
 | 
					unstable_features = true
 | 
				
			||||||
 | 
					error_on_line_overflow = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					hard_tabs = true
 | 
				
			||||||
 | 
					newline_style = "Unix"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					max_width = 100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					imports_layout = "HorizontalVertical"
 | 
				
			||||||
 | 
					imports_granularity = "Crate"
 | 
				
			||||||
 | 
					group_imports = "StdExternalCrate"
 | 
				
			||||||
 | 
					hex_literal_case = "Lower"
 | 
				
			||||||
 | 
					match_block_trailing_comma = true
 | 
				
			||||||
 | 
					overflow_delimited_expr = true
 | 
				
			||||||
 | 
					reorder_impl_items = true
 | 
				
			||||||
 | 
					trailing_semicolon = false
 | 
				
			||||||
 | 
					use_field_init_shorthand = true
 | 
				
			||||||
 | 
					use_try_shorthand = true
 | 
				
			||||||
 | 
					condense_wildcard_suffixes = true
 | 
				
			||||||
 | 
					single_line_if_else_max_width = 80
 | 
				
			||||||
 | 
					single_line_let_else_max_width = 80
 | 
				
			||||||
							
								
								
									
										119
									
								
								typegen/src/main.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								typegen/src/main.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,119 @@
 | 
				
			||||||
 | 
					use std::{collections::HashMap, path::Path};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use anyhow::{anyhow, Context};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mod parse;
 | 
				
			||||||
 | 
					mod typespec;
 | 
				
			||||||
 | 
					mod outform;
 | 
				
			||||||
 | 
					mod resolver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() -> anyhow::Result<()> {
 | 
				
			||||||
 | 
						let args = std::env::args().collect::<Vec<_>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						match args.get(1).map(|v| v as &str) {
 | 
				
			||||||
 | 
							Some("gentypes") => {
 | 
				
			||||||
 | 
								let modinfo = args.get(2).expect("expected module file");
 | 
				
			||||||
 | 
								let outpath = args.get(3).expect("expected output path");
 | 
				
			||||||
 | 
								let path = Path::new(modinfo);
 | 
				
			||||||
 | 
								let dir = path.parent().unwrap();
 | 
				
			||||||
 | 
								let text = std::fs::read_to_string(path).expect("failed to read module file");
 | 
				
			||||||
 | 
								let module = parse::parse_module(&text)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let texts = module.header.headers.iter()
 | 
				
			||||||
 | 
									.map(|header| {
 | 
				
			||||||
 | 
										let text = std::fs::read_to_string(dir.join(header))
 | 
				
			||||||
 | 
											.with_context(|| format!("failed to read module header `{header}` at {:?}", dir.join(header)))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										Ok::<_, anyhow::Error>((header, text))
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
									.collect::<Result<HashMap<_, _>, _>>()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let parser = parse::Parser::new();
 | 
				
			||||||
 | 
								let mut ctx = parse::ParseContext::default();
 | 
				
			||||||
 | 
								texts.iter()
 | 
				
			||||||
 | 
									.map(|(header, text)| {
 | 
				
			||||||
 | 
										parser.parse(&text, &mut ctx)
 | 
				
			||||||
 | 
											.with_context(|| format!("while parsing module header `{header}`"))
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
									.collect::<Result<_, _>>()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let typespec = ctx.gen_typespec(&module.header.name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let text = serde_json::to_string_pretty(&typespec).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								std::fs::write(outpath, text)
 | 
				
			||||||
 | 
									.context("saving typespec")?;
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Some("gendocs") => {
 | 
				
			||||||
 | 
								let modinfo = args.get(2).expect("expected module file");
 | 
				
			||||||
 | 
								let datapath = args.get(3).expect("expected datapath");
 | 
				
			||||||
 | 
								let templatepath = args.get(4).expect("expected templatepath");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let text = std::fs::read_to_string(modinfo).expect("failed to read module file");
 | 
				
			||||||
 | 
								let module = parse::parse_module(&text)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let mut typespec = typespec::TypeSpec::default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for path in &args[5..] {
 | 
				
			||||||
 | 
									let text = std::fs::read_to_string(&path)
 | 
				
			||||||
 | 
										.with_context(|| anyhow!("attempting to read {path}"))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									let ts = serde_json::from_str::<typespec::TypeSpec>(&text)
 | 
				
			||||||
 | 
										.with_context(|| anyhow!("attempting to parse {path}"))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									typespec.typemap.extend(ts.typemap);
 | 
				
			||||||
 | 
									typespec.classes.extend(ts.classes);
 | 
				
			||||||
 | 
									typespec.gadgets.extend(ts.gadgets);
 | 
				
			||||||
 | 
									typespec.enums.extend(ts.enums);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let types = resolver::resolve_types(&module.header.name, typespec)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let datapath = Path::new(datapath);
 | 
				
			||||||
 | 
								let templatepath = Path::new(templatepath);
 | 
				
			||||||
 | 
								std::fs::create_dir_all(datapath)?;
 | 
				
			||||||
 | 
								std::fs::create_dir_all(templatepath)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for (name, info) in types {
 | 
				
			||||||
 | 
									let json = serde_json::to_string_pretty(&info).unwrap();
 | 
				
			||||||
 | 
									let datapath = datapath.join(format!("{name}.json"));
 | 
				
			||||||
 | 
									std::fs::write(&datapath, json).with_context(|| format!("while writing {datapath:?}"))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									let template = format!("+++
 | 
				
			||||||
 | 
					title = \"{name}\"
 | 
				
			||||||
 | 
					hidetitle = true
 | 
				
			||||||
 | 
					+++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{{{{< qmltype module=\"{module}\" type=\"{name}\" >}}}}
 | 
				
			||||||
 | 
					", name = name, module = module.header.name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									let templatepath = templatepath.join(format!("{name}.md"));
 | 
				
			||||||
 | 
									std::fs::write(&templatepath, template).with_context(|| format!("while writing {templatepath:?}"))?;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let index = outform::ModuleIndex {
 | 
				
			||||||
 | 
									description: module.header.description,
 | 
				
			||||||
 | 
									details: module.details.to_string(),
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let datapath = datapath.join("index.json");
 | 
				
			||||||
 | 
								let json = serde_json::to_string_pretty(&index).unwrap();
 | 
				
			||||||
 | 
								std::fs::write(&datapath, json).with_context(|| format!("while writing {datapath:?}"))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let template = format!("+++
 | 
				
			||||||
 | 
					title = \"{name}\"
 | 
				
			||||||
 | 
					+++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{{{{< qmlmodule module=\"{name}\" >}}}}
 | 
				
			||||||
 | 
					", name = module.header.name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									let templatepath = templatepath.join(format!("_index.md"));
 | 
				
			||||||
 | 
									std::fs::write(&templatepath, template).with_context(|| format!("while writing {templatepath:?}"))?;
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							_ => {
 | 
				
			||||||
 | 
								panic!("typegen invoked without mode");
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										120
									
								
								typegen/src/outform.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								typegen/src/outform.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,120 @@
 | 
				
			||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use serde::Serialize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize)]
 | 
				
			||||||
 | 
					pub struct ModuleIndex {
 | 
				
			||||||
 | 
						pub description: String,
 | 
				
			||||||
 | 
						pub details: String,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize)]
 | 
				
			||||||
 | 
					#[serde(rename_all = "lowercase")]
 | 
				
			||||||
 | 
					#[serde(tag = "type")]
 | 
				
			||||||
 | 
					pub enum TypeInfo {
 | 
				
			||||||
 | 
						Class(ClassInfo),
 | 
				
			||||||
 | 
						Enum(EnumInfo),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize)]
 | 
				
			||||||
 | 
					pub struct ClassInfo {
 | 
				
			||||||
 | 
						#[serde(rename = "super")]
 | 
				
			||||||
 | 
						pub superclass: Type,
 | 
				
			||||||
 | 
						pub description: Option<String>,
 | 
				
			||||||
 | 
						pub details: Option<String>,
 | 
				
			||||||
 | 
						#[serde(skip_serializing_if = "Vec::is_empty")]
 | 
				
			||||||
 | 
						pub flags: Vec<Flag>,
 | 
				
			||||||
 | 
						pub properties: HashMap<String, Property>,
 | 
				
			||||||
 | 
						pub functions: HashMap<String, Function>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize)]
 | 
				
			||||||
 | 
					pub struct Property {
 | 
				
			||||||
 | 
						#[serde(rename = "type")]
 | 
				
			||||||
 | 
						pub type_: PropertyType,
 | 
				
			||||||
 | 
						pub details: Option<String>,
 | 
				
			||||||
 | 
						#[serde(skip_serializing_if = "Vec::is_empty")]
 | 
				
			||||||
 | 
						pub flags: Vec<Flag>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize)]
 | 
				
			||||||
 | 
					pub enum PropertyType {
 | 
				
			||||||
 | 
						#[serde(rename = "gadget")]
 | 
				
			||||||
 | 
						Gadget(HashMap<String, PropertyType>),
 | 
				
			||||||
 | 
						#[serde(untagged)]
 | 
				
			||||||
 | 
						Type(Type),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize)]
 | 
				
			||||||
 | 
					pub struct Function {
 | 
				
			||||||
 | 
						pub ret: Type,
 | 
				
			||||||
 | 
						pub name: String,
 | 
				
			||||||
 | 
						pub details: Option<String>,
 | 
				
			||||||
 | 
						pub params: HashMap<String, Type>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize)]
 | 
				
			||||||
 | 
					pub struct EnumInfo {
 | 
				
			||||||
 | 
						pub description: Option<String>,
 | 
				
			||||||
 | 
						pub details: Option<String>,
 | 
				
			||||||
 | 
						pub variants: HashMap<String, Variant>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize)]
 | 
				
			||||||
 | 
					pub struct Variant {
 | 
				
			||||||
 | 
						pub details: Option<String>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize)]
 | 
				
			||||||
 | 
					pub struct Type {
 | 
				
			||||||
 | 
						#[serde(rename = "type")]
 | 
				
			||||||
 | 
						pub type_: TypeSource,
 | 
				
			||||||
 | 
						pub module: String,
 | 
				
			||||||
 | 
						pub name: String,
 | 
				
			||||||
 | 
						#[serde(skip_serializing_if = "Option::is_none")]
 | 
				
			||||||
 | 
						pub of: Option<Box<Type>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Type {
 | 
				
			||||||
 | 
						pub fn resolve(module: Option<&str>, name: &str) -> Self {
 | 
				
			||||||
 | 
							let (src, module) = match module {
 | 
				
			||||||
 | 
								None => (TypeSource::Qt, "qml".to_string()),
 | 
				
			||||||
 | 
								Some(name) if name.starts_with("qml.") => (TypeSource::Qt, name.to_string()),
 | 
				
			||||||
 | 
								Some(name) => (TypeSource::Local, name.to_string()),
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Type {
 | 
				
			||||||
 | 
								type_: src,
 | 
				
			||||||
 | 
								module,
 | 
				
			||||||
 | 
								name: name.to_string(),
 | 
				
			||||||
 | 
								of: None,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pub fn unknown() -> Type {
 | 
				
			||||||
 | 
							Type {
 | 
				
			||||||
 | 
								type_: TypeSource::Unknown,
 | 
				
			||||||
 | 
								module: "".to_string(),
 | 
				
			||||||
 | 
								name: "".to_string(),
 | 
				
			||||||
 | 
								of: None,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize)]
 | 
				
			||||||
 | 
					#[serde(rename_all = "lowercase")]
 | 
				
			||||||
 | 
					pub enum TypeSource {
 | 
				
			||||||
 | 
						Qt,
 | 
				
			||||||
 | 
						Local,
 | 
				
			||||||
 | 
						Unknown,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize)]
 | 
				
			||||||
 | 
					#[serde(rename_all = "lowercase")]
 | 
				
			||||||
 | 
					pub enum Flag {
 | 
				
			||||||
 | 
						Default,
 | 
				
			||||||
 | 
						Readonly,
 | 
				
			||||||
 | 
						Writeonly,
 | 
				
			||||||
 | 
						Singleton,
 | 
				
			||||||
 | 
						Uncreatable,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										452
									
								
								typegen/src/parse.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										452
									
								
								typegen/src/parse.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,452 @@
 | 
				
			||||||
 | 
					use std::borrow::Cow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use anyhow::{anyhow, bail, Context};
 | 
				
			||||||
 | 
					use regex::Regex;
 | 
				
			||||||
 | 
					use serde::Deserialize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::typespec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Deserialize, Debug)]
 | 
				
			||||||
 | 
					pub struct ModuleInfoHeader {
 | 
				
			||||||
 | 
						pub name: String,
 | 
				
			||||||
 | 
						pub description: String,
 | 
				
			||||||
 | 
						pub headers: Vec<String>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					pub struct ModuleInfo<'a> {
 | 
				
			||||||
 | 
						pub header: ModuleInfoHeader,
 | 
				
			||||||
 | 
						pub details: &'a str,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn parse_module(text: &str) -> anyhow::Result<ModuleInfo> {
 | 
				
			||||||
 | 
						let Some((mut header, mut details)) = text.split_once("-----") else {
 | 
				
			||||||
 | 
							bail!("could not split module header");
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						header = header.trim();
 | 
				
			||||||
 | 
						details = details.trim();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let header = toml::from_str::<ModuleInfoHeader>(header)
 | 
				
			||||||
 | 
					    .context("parsing module info header")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Ok(ModuleInfo {
 | 
				
			||||||
 | 
							header,
 | 
				
			||||||
 | 
							details,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					pub struct ClassInfo<'a> {
 | 
				
			||||||
 | 
						pub type_: ClassType,
 | 
				
			||||||
 | 
						pub name: &'a str,
 | 
				
			||||||
 | 
						pub qml_name: Option<&'a str>,
 | 
				
			||||||
 | 
						pub superclass: Option<&'a str>,
 | 
				
			||||||
 | 
						pub singleton: bool,
 | 
				
			||||||
 | 
						pub uncreatable: bool,
 | 
				
			||||||
 | 
						pub comment: Option<&'a str>,
 | 
				
			||||||
 | 
						pub properties: Vec<Property<'a>>,
 | 
				
			||||||
 | 
						pub invokables: Vec<Invokable<'a>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					pub enum ClassType {
 | 
				
			||||||
 | 
						Object,
 | 
				
			||||||
 | 
						Gadget,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Copy)]
 | 
				
			||||||
 | 
					pub struct Property<'a> {
 | 
				
			||||||
 | 
						pub type_: &'a str,
 | 
				
			||||||
 | 
						pub name: &'a str,
 | 
				
			||||||
 | 
						pub comment: Option<&'a str>,
 | 
				
			||||||
 | 
						pub readable: bool,
 | 
				
			||||||
 | 
						pub writable: bool,
 | 
				
			||||||
 | 
						pub default: bool,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
 | 
					pub struct Invokable<'a> {
 | 
				
			||||||
 | 
						pub name: &'a str,
 | 
				
			||||||
 | 
						pub ret: &'a str,
 | 
				
			||||||
 | 
						pub comment: Option<&'a str>,
 | 
				
			||||||
 | 
						pub params: Vec<InvokableParam<'a>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Copy)]
 | 
				
			||||||
 | 
					pub struct InvokableParam<'a> {
 | 
				
			||||||
 | 
						pub name: &'a str,
 | 
				
			||||||
 | 
						pub type_: &'a str,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					pub struct EnumInfo<'a> {
 | 
				
			||||||
 | 
						pub namespace: &'a str,
 | 
				
			||||||
 | 
						pub enum_name: &'a str,
 | 
				
			||||||
 | 
						pub qml_name: &'a str,
 | 
				
			||||||
 | 
						pub comment: Option<&'a str>,
 | 
				
			||||||
 | 
						pub variants: Vec<Variant<'a>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Copy)]
 | 
				
			||||||
 | 
					pub struct Variant<'a> {
 | 
				
			||||||
 | 
						pub name: &'a str,
 | 
				
			||||||
 | 
						pub comment: Option<&'a str>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct Parser {
 | 
				
			||||||
 | 
						pub class_regex: Regex,
 | 
				
			||||||
 | 
						pub macro_regex: Regex,
 | 
				
			||||||
 | 
						pub property_regex: Regex,
 | 
				
			||||||
 | 
						pub fn_regex: Regex,
 | 
				
			||||||
 | 
						pub fn_param_regex: Regex,
 | 
				
			||||||
 | 
						pub defaultprop_classinfo_regex: Regex,
 | 
				
			||||||
 | 
						pub enum_regex: Regex,
 | 
				
			||||||
 | 
						pub enum_variant_regex: Regex,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					pub struct ParseContext<'a> {
 | 
				
			||||||
 | 
						pub classes: Vec<ClassInfo<'a>>,
 | 
				
			||||||
 | 
						pub enums: Vec<EnumInfo<'a>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for ParseContext<'_> {
 | 
				
			||||||
 | 
						fn default() -> Self {
 | 
				
			||||||
 | 
							Self {
 | 
				
			||||||
 | 
								classes: Vec::new(),
 | 
				
			||||||
 | 
								enums: Vec::new(),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Parser {
 | 
				
			||||||
 | 
						pub fn new() -> Self {
 | 
				
			||||||
 | 
							Self {
 | 
				
			||||||
 | 
								class_regex: Regex::new(r#"(?<comment>(\s*\/\/\/.*\n)+)?\s*class\s+(?<name>\w+)(?:\s*:\s*public\s+(?<super>\w+))?.+?\{(?<body>[\s\S]*?)};"#).unwrap(),
 | 
				
			||||||
 | 
								macro_regex: Regex::new(r#"(?<comment>(\s*\/\/\/.*\n)+)?\s*(?<type>(Q|QML)_\w+)\s*(\((?<args>.*)\))?;"#).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+(?<type>(\w|::|<|>)+\*?)\s+(?<name>\w+)\((?<params>[\s\S]*?)\);"#).unwrap(),
 | 
				
			||||||
 | 
								fn_param_regex: Regex::new(r#"(?<type>(\w|::|<|>)+\*?)\s+(?<name>\w+)(,|$)"#).unwrap(),
 | 
				
			||||||
 | 
								defaultprop_classinfo_regex: Regex::new(r#"^\s*"DefaultProperty", "(?<prop>.+)"\s*$"#).unwrap(),
 | 
				
			||||||
 | 
								enum_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_variant_regex: Regex::new(r#"(?<comment>(\s*\/\/\/.*\n)+)?\s*(?<name>\w+)\s*=\s*\d+,"#).unwrap(),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pub fn parse_classes<'a>(&self, text: &'a str, ctx: &mut ParseContext<'a>) -> anyhow::Result<()> {
 | 
				
			||||||
 | 
							for class in self.class_regex.captures_iter(text) {
 | 
				
			||||||
 | 
								let comment = class.name("comment").map(|m| m.as_str());
 | 
				
			||||||
 | 
								let name = class.name("name").unwrap().as_str();
 | 
				
			||||||
 | 
								let superclass = class.name("super").map(|m| m.as_str());
 | 
				
			||||||
 | 
								let body = class.name("body").unwrap().as_str();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let mut classtype = None;
 | 
				
			||||||
 | 
								let mut qml_name = None;
 | 
				
			||||||
 | 
								let mut singleton = false;
 | 
				
			||||||
 | 
								let mut uncreatable = false;
 | 
				
			||||||
 | 
								let mut properties = Vec::new();
 | 
				
			||||||
 | 
								let mut default_property = None;
 | 
				
			||||||
 | 
								let mut invokables = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								(|| {
 | 
				
			||||||
 | 
									for macro_ in self.macro_regex.captures_iter(body) {
 | 
				
			||||||
 | 
										let comment = macro_.name("comment").map(|m| m.as_str());
 | 
				
			||||||
 | 
										let type_ = macro_.name("type").unwrap().as_str();
 | 
				
			||||||
 | 
										let args = macro_.name("args").map(|m| m.as_str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										(|| {
 | 
				
			||||||
 | 
											match type_ {
 | 
				
			||||||
 | 
												"Q_OBJECT" => classtype = Some(ClassType::Object),
 | 
				
			||||||
 | 
												"Q_GADGET" => classtype = Some(ClassType::Gadget),
 | 
				
			||||||
 | 
												"QML_ELEMENT" => qml_name = Some(name),
 | 
				
			||||||
 | 
												"QML_NAMED_ELEMENT" => qml_name = Some(args.ok_or_else(|| anyhow!("expected name for QML_NAMED_ELEMENT"))?),
 | 
				
			||||||
 | 
												"QML_SINGLETON" => singleton = true,
 | 
				
			||||||
 | 
												"QML_UNCREATABLE" => uncreatable = true,
 | 
				
			||||||
 | 
												"Q_PROPERTY" => {
 | 
				
			||||||
 | 
													let prop = 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();
 | 
				
			||||||
 | 
													let read = prop.name("read").is_some();
 | 
				
			||||||
 | 
													let write = prop.name("write").is_some();
 | 
				
			||||||
 | 
													let constant = prop.name("const").is_some();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
													properties.push(Property {
 | 
				
			||||||
 | 
														type_: prop.name("type").unwrap().as_str(),
 | 
				
			||||||
 | 
														name: prop.name("name").unwrap().as_str(),
 | 
				
			||||||
 | 
														comment,
 | 
				
			||||||
 | 
														readable: read || member,
 | 
				
			||||||
 | 
														writable: !constant && (write || member),
 | 
				
			||||||
 | 
														default: false,
 | 
				
			||||||
 | 
													});
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
												"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();
 | 
				
			||||||
 | 
														default_property = Some(prop);
 | 
				
			||||||
 | 
													}
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
												_ => {},
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											Ok::<_, anyhow::Error>(())
 | 
				
			||||||
 | 
										})().with_context(|| format!("while parsing macro `{}`", macro_.get(0).unwrap().as_str()))?;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									for invokable in self.fn_regex.captures_iter(body) {
 | 
				
			||||||
 | 
										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();
 | 
				
			||||||
 | 
										let params_raw = invokable.name("params").unwrap().as_str();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										let mut params = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										for param in self.fn_param_regex.captures_iter(params_raw) {
 | 
				
			||||||
 | 
											let type_ = param.name("type").unwrap().as_str();
 | 
				
			||||||
 | 
											let name = param.name("name").unwrap().as_str();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											params.push(InvokableParam {
 | 
				
			||||||
 | 
												type_,
 | 
				
			||||||
 | 
												name,
 | 
				
			||||||
 | 
											});
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										invokables.push(Invokable {
 | 
				
			||||||
 | 
											name,
 | 
				
			||||||
 | 
											ret: type_,
 | 
				
			||||||
 | 
											comment,
 | 
				
			||||||
 | 
											params,
 | 
				
			||||||
 | 
										});
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if let Some(prop) = default_property {
 | 
				
			||||||
 | 
										let prop = properties.iter_mut().find(|p| p.name == prop)
 | 
				
			||||||
 | 
											.ok_or_else(|| anyhow!("could not find default property `{prop}`"))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										prop.default = true;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									Ok::<_, anyhow::Error>(())
 | 
				
			||||||
 | 
								})().with_context(|| format!("while parsing class `{name}`"))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let Some(type_) = classtype else { continue };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								ctx.classes.push(ClassInfo {
 | 
				
			||||||
 | 
									type_,
 | 
				
			||||||
 | 
									name,
 | 
				
			||||||
 | 
									qml_name,
 | 
				
			||||||
 | 
									superclass,
 | 
				
			||||||
 | 
									singleton,
 | 
				
			||||||
 | 
									uncreatable,
 | 
				
			||||||
 | 
									comment,
 | 
				
			||||||
 | 
									properties,
 | 
				
			||||||
 | 
									invokables,
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Ok(())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pub fn parse_enums<'a>(&self, text: &'a str, ctx: &mut ParseContext<'a>) -> anyhow::Result<()> {
 | 
				
			||||||
 | 
							for enum_ in self.enum_regex.captures_iter(text) {
 | 
				
			||||||
 | 
								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 qml_name = enum_.name("qml_name").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,
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								ctx.enums.push(EnumInfo {
 | 
				
			||||||
 | 
									namespace,
 | 
				
			||||||
 | 
									enum_name,
 | 
				
			||||||
 | 
									qml_name,
 | 
				
			||||||
 | 
									comment,
 | 
				
			||||||
 | 
									variants,
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Ok(())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pub fn parse<'a>(&self, text: &'a str, ctx: &mut ParseContext<'a>) -> anyhow::Result<()> {
 | 
				
			||||||
 | 
							self.parse_classes(text, ctx)?;
 | 
				
			||||||
 | 
							self.parse_enums(text, ctx)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Ok(())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ParseContext<'_> {
 | 
				
			||||||
 | 
						pub fn gen_typespec(&self, module: &str) -> typespec::TypeSpec {
 | 
				
			||||||
 | 
							typespec::TypeSpec {
 | 
				
			||||||
 | 
								typemap: self.classes.iter().filter_map(|class| {
 | 
				
			||||||
 | 
									Some(typespec::QmlTypeMapping {
 | 
				
			||||||
 | 
										// filters gadgets
 | 
				
			||||||
 | 
										name: class.qml_name?.to_string(),
 | 
				
			||||||
 | 
										cname: class.name.to_string(),
 | 
				
			||||||
 | 
										module: Some(module.to_string()),
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								}).collect(),
 | 
				
			||||||
 | 
								classes: self.classes.iter().filter_map(|class| {
 | 
				
			||||||
 | 
									let (description, details) = class.comment.map(parse_details_desc)
 | 
				
			||||||
 | 
										.unwrap_or((None, None));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									Some(typespec::Class {
 | 
				
			||||||
 | 
										name: class.name.to_string(),
 | 
				
			||||||
 | 
										module: module.to_string(),
 | 
				
			||||||
 | 
										description,
 | 
				
			||||||
 | 
										details,
 | 
				
			||||||
 | 
										// filters gadgets
 | 
				
			||||||
 | 
										superclass: class.superclass?.to_string(),
 | 
				
			||||||
 | 
										singleton: class.singleton,
 | 
				
			||||||
 | 
										uncreatable: class.uncreatable,
 | 
				
			||||||
 | 
										properties: class.properties.iter().map(|p| (*p).into()).collect(),
 | 
				
			||||||
 | 
										functions: class.invokables.iter().map(|f| f.as_typespec()).collect(),
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								}).collect(),
 | 
				
			||||||
 | 
								gadgets: self.classes.iter().filter_map(|class| match class.type_ {
 | 
				
			||||||
 | 
									ClassType::Gadget => Some(typespec::Gadget {
 | 
				
			||||||
 | 
										cname: class.name.to_string(),
 | 
				
			||||||
 | 
										properties: class.properties.iter().map(|p| (*p).into()).collect(),
 | 
				
			||||||
 | 
									}),
 | 
				
			||||||
 | 
									_ => None,
 | 
				
			||||||
 | 
								}).collect(),
 | 
				
			||||||
 | 
								enums: self.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(),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<Property<'_>> for typespec::Property {
 | 
				
			||||||
 | 
						fn from(value: Property) -> Self {
 | 
				
			||||||
 | 
							Self {
 | 
				
			||||||
 | 
								type_: value.type_.to_string(),
 | 
				
			||||||
 | 
								name: value.name.to_string(),
 | 
				
			||||||
 | 
								details: value.comment.map(parse_details),
 | 
				
			||||||
 | 
								readable: value.readable,
 | 
				
			||||||
 | 
								writable: value.writable,
 | 
				
			||||||
 | 
								default: value.default,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<Variant<'_>> for typespec::Variant {
 | 
				
			||||||
 | 
						fn from(value: Variant<'_>) -> Self {
 | 
				
			||||||
 | 
							Self {
 | 
				
			||||||
 | 
								name: value.name.to_string(),
 | 
				
			||||||
 | 
								details: value.comment.map(parse_details),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Invokable<'_> {
 | 
				
			||||||
 | 
						fn as_typespec(&self) -> typespec::Function {
 | 
				
			||||||
 | 
							typespec::Function {
 | 
				
			||||||
 | 
								ret: self.ret.to_string(),
 | 
				
			||||||
 | 
								name: self.name.to_string(),
 | 
				
			||||||
 | 
								details: self.comment.map(parse_details),
 | 
				
			||||||
 | 
								params: self.params.iter().map(|p| (*p).into()).collect(),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<InvokableParam<'_>> for typespec::FnParam {
 | 
				
			||||||
 | 
						fn from(value: InvokableParam<'_>) -> Self {
 | 
				
			||||||
 | 
							Self {
 | 
				
			||||||
 | 
								type_: value.type_.to_string(),
 | 
				
			||||||
 | 
								name: value.name.to_string(),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn parse_details(text: &str) -> String {
 | 
				
			||||||
 | 
						let mut seen_content = false;
 | 
				
			||||||
 | 
						let mut callout = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let mut str = text.lines()
 | 
				
			||||||
 | 
					    .map(|line| {
 | 
				
			||||||
 | 
								line.trim()
 | 
				
			||||||
 | 
									.strip_prefix("///")
 | 
				
			||||||
 | 
									.map(|line| line.strip_prefix(' ').unwrap_or(line))
 | 
				
			||||||
 | 
									.unwrap_or(line)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					    .filter(|line| {
 | 
				
			||||||
 | 
								let any = !line.is_empty();
 | 
				
			||||||
 | 
								let filter = any || seen_content;
 | 
				
			||||||
 | 
								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);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					    .fold(String::new(), |accum, line| {
 | 
				
			||||||
 | 
								let sep = if accum.is_empty() { "" } else { "\n" };
 | 
				
			||||||
 | 
								accum + sep + line.as_ref()
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if callout {
 | 
				
			||||||
 | 
							str += "\n{{< /callout >}}";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						str
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn parse_details_desc(text: &str) -> (Option<String>, Option<String>) {
 | 
				
			||||||
 | 
						let details = parse_details(text);
 | 
				
			||||||
 | 
						dbg!(&details);
 | 
				
			||||||
 | 
						if details.starts_with('!') {
 | 
				
			||||||
 | 
							dbg!(&details, &details[1..].split_once('\n'));
 | 
				
			||||||
 | 
							match details[1..].split_once('\n') {
 | 
				
			||||||
 | 
								Some((desc, details)) => (Some(desc.strip_prefix(' ').unwrap_or(desc).to_string()), Some(details.to_string())),
 | 
				
			||||||
 | 
								None => (Some(details[1..].strip_prefix(' ').unwrap_or(&details[1..]).to_string()), None),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							(None, Some(details))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										192
									
								
								typegen/src/resolver.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								typegen/src/resolver.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,192 @@
 | 
				
			||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{outform::{self, Flag, PropertyType}, typespec::{FnParam, Function, Property, TypeSpec}};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn resolve_types(module: &str, typespec: TypeSpec) -> anyhow::Result<HashMap<String, outform::TypeInfo>> {
 | 
				
			||||||
 | 
						let mut outtypes = HashMap::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let types = typespec.typemap.iter()
 | 
				
			||||||
 | 
					    .filter(|type_| type_.module.as_ref().map(|v| v as &str) == Some(module));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let findqmltype = |cname: &str| typespec.typemap
 | 
				
			||||||
 | 
							.iter()
 | 
				
			||||||
 | 
							.find(|type_| type_.cname == cname);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for mapping in types {
 | 
				
			||||||
 | 
							let Some(class) = typespec.classes.iter().find(|class| class.name == mapping.cname) else {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let mut properties = Vec::<&Property>::new();
 | 
				
			||||||
 | 
							let mut functions = Vec::<&Function>::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// the first superclass availible from QML
 | 
				
			||||||
 | 
							let mut superclass = &class.superclass;
 | 
				
			||||||
 | 
							let superclass = loop {
 | 
				
			||||||
 | 
								let type_ = findqmltype(superclass);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if let Some(type_) = type_ {
 | 
				
			||||||
 | 
									break outform::Type::resolve(type_.module.as_ref().map(|v| v as &str), &type_.name)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let superctype = typespec.classes.iter().find(|class| &class.name == superclass);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								match superctype {
 | 
				
			||||||
 | 
									Some(superctype) => {
 | 
				
			||||||
 | 
										properties.extend(superctype.properties.iter());
 | 
				
			||||||
 | 
										functions.extend(superctype.functions.iter());
 | 
				
			||||||
 | 
										superclass = &superctype.superclass;
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									None => break outform::Type::unknown(),
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							fn qmlparamtype(ctype: &str, typespec: &TypeSpec) -> outform::Type {
 | 
				
			||||||
 | 
								let qtype = typespec.typemap
 | 
				
			||||||
 | 
									.iter()
 | 
				
			||||||
 | 
									.find(|type_| &type_.cname == ctype)
 | 
				
			||||||
 | 
									.map(|type_| (&type_.module, &type_.name))
 | 
				
			||||||
 | 
									.or_else(|| {
 | 
				
			||||||
 | 
										typespec.enums
 | 
				
			||||||
 | 
											.iter()
 | 
				
			||||||
 | 
											.find(|type_| type_.cname.as_ref().map(|v| v as &str) == Some(ctype))
 | 
				
			||||||
 | 
											.map(|type_| (&type_.module, &type_.name))
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								match qtype {
 | 
				
			||||||
 | 
									Some((module, name)) => outform::Type::resolve(module.as_ref().map(|v| v as &str), &name),
 | 
				
			||||||
 | 
									None => outform::Type::unknown(),
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							fn solveprop(prop: &Property, typespec: &TypeSpec) -> outform::Property {
 | 
				
			||||||
 | 
								let mut ctype = &prop.type_[..];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let flags = {
 | 
				
			||||||
 | 
									let mut flags = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if prop.default {
 | 
				
			||||||
 | 
										flags.push(Flag::Default);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if !prop.readable {
 | 
				
			||||||
 | 
										flags.push(Flag::Writeonly);
 | 
				
			||||||
 | 
									} else if !prop.writable {
 | 
				
			||||||
 | 
										flags.push(Flag::Readonly);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									flags
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let gadget = typespec.gadgets.iter()
 | 
				
			||||||
 | 
									.find(|gadget| gadget.cname == ctype);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								match gadget {
 | 
				
			||||||
 | 
									Some(gadget) => outform::Property {
 | 
				
			||||||
 | 
										type_: PropertyType::Gadget(
 | 
				
			||||||
 | 
											gadget.properties.iter()
 | 
				
			||||||
 | 
												.map(|prop| (prop.name.clone(), solveprop(prop, typespec).type_))
 | 
				
			||||||
 | 
												.collect()
 | 
				
			||||||
 | 
										),
 | 
				
			||||||
 | 
										details: prop.details.clone(),
 | 
				
			||||||
 | 
										flags,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									None => {
 | 
				
			||||||
 | 
										let mut list = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if ctype.starts_with("QQmlListProperty<") {
 | 
				
			||||||
 | 
											ctype = &ctype[17..ctype.len() - 1];
 | 
				
			||||||
 | 
											list = true;
 | 
				
			||||||
 | 
										} else if ctype.starts_with("QList<") {
 | 
				
			||||||
 | 
											ctype = &ctype[6..ctype.len() - 1];
 | 
				
			||||||
 | 
											list = true;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if ctype.ends_with('*') {
 | 
				
			||||||
 | 
											ctype = &ctype[..ctype.len() - 1];
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										let mut type_ = qmlparamtype(ctype, typespec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if list {
 | 
				
			||||||
 | 
											type_ = outform::Type {
 | 
				
			||||||
 | 
												type_: outform::TypeSource::Qt,
 | 
				
			||||||
 | 
												module: "qml".to_string(),
 | 
				
			||||||
 | 
												name: "list".to_string(),
 | 
				
			||||||
 | 
												of: Some(Box::new(type_)),
 | 
				
			||||||
 | 
											};
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										outform::Property {
 | 
				
			||||||
 | 
											type_: PropertyType::Type(type_),
 | 
				
			||||||
 | 
											details: prop.details.clone(),
 | 
				
			||||||
 | 
											flags,
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							fn solvefunc(func: &Function, typespec: &TypeSpec) -> outform::Function {
 | 
				
			||||||
 | 
								outform::Function {
 | 
				
			||||||
 | 
									ret: qmlparamtype(&func.ret, typespec),
 | 
				
			||||||
 | 
									name: func.name.clone(),
 | 
				
			||||||
 | 
									details: func.details.clone(),
 | 
				
			||||||
 | 
									params: func.params.iter().map(|FnParam { type_, name }| (name.clone(), qmlparamtype(type_, typespec))).collect(),
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							properties.extend(class.properties.iter());
 | 
				
			||||||
 | 
							properties.sort_by(|a, b| Ord::cmp(&a.name, &b.name));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							functions.extend(class.functions.iter());
 | 
				
			||||||
 | 
							functions.sort_by(|a, b| Ord::cmp(&a.name, &b.name));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let properties = properties.iter().map(|prop| (
 | 
				
			||||||
 | 
								prop.name.clone(),
 | 
				
			||||||
 | 
								solveprop(prop, &typespec)
 | 
				
			||||||
 | 
							)).collect::<HashMap<_, _>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let functions = functions.iter().map(|func| (
 | 
				
			||||||
 | 
								func.name.clone(),
 | 
				
			||||||
 | 
								solvefunc(func, &typespec)
 | 
				
			||||||
 | 
							)).collect::<HashMap<_, _>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let type_ = outform::ClassInfo {
 | 
				
			||||||
 | 
								superclass,
 | 
				
			||||||
 | 
								description: class.description.clone(),
 | 
				
			||||||
 | 
								details: class.details.clone(),
 | 
				
			||||||
 | 
								flags: {
 | 
				
			||||||
 | 
									let mut flags = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if class.singleton {
 | 
				
			||||||
 | 
										flags.push(Flag::Singleton);
 | 
				
			||||||
 | 
									} else if class.uncreatable {
 | 
				
			||||||
 | 
										flags.push(Flag::Uncreatable);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									flags
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								properties,
 | 
				
			||||||
 | 
								functions,
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							outtypes.insert(mapping.name.clone(), outform::TypeInfo::Class(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 {
 | 
				
			||||||
 | 
									description: enum_.description,
 | 
				
			||||||
 | 
									details: enum_.details,
 | 
				
			||||||
 | 
									variants: enum_.varaints.into_iter().map(|variant| (
 | 
				
			||||||
 | 
										variant.name,
 | 
				
			||||||
 | 
										outform::Variant {
 | 
				
			||||||
 | 
											details: variant.details,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									)).collect(),
 | 
				
			||||||
 | 
								}));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Ok(outtypes)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										88
									
								
								typegen/src/typespec.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								typegen/src/typespec.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,88 @@
 | 
				
			||||||
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct TypeSpec {
 | 
				
			||||||
 | 
						pub typemap: Vec<QmlTypeMapping>,
 | 
				
			||||||
 | 
						pub classes: Vec<Class>,
 | 
				
			||||||
 | 
						pub gadgets: Vec<Gadget>,
 | 
				
			||||||
 | 
						pub enums: Vec<Enum>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for TypeSpec {
 | 
				
			||||||
 | 
						fn default() -> Self {
 | 
				
			||||||
 | 
							Self {
 | 
				
			||||||
 | 
								typemap: Vec::new(),
 | 
				
			||||||
 | 
								classes: Vec::new(),
 | 
				
			||||||
 | 
								gadgets: Vec::new(),
 | 
				
			||||||
 | 
								enums: Vec::new(),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct QmlTypeMapping {
 | 
				
			||||||
 | 
						pub name: String,
 | 
				
			||||||
 | 
						pub cname: String,
 | 
				
			||||||
 | 
						pub module: Option<String>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct Class {
 | 
				
			||||||
 | 
						pub name: String,
 | 
				
			||||||
 | 
						pub module: String,
 | 
				
			||||||
 | 
						pub description: Option<String>,
 | 
				
			||||||
 | 
						pub details: Option<String>,
 | 
				
			||||||
 | 
						pub superclass: String,
 | 
				
			||||||
 | 
						pub singleton: bool,
 | 
				
			||||||
 | 
						pub uncreatable: bool,
 | 
				
			||||||
 | 
						pub properties: Vec<Property>,
 | 
				
			||||||
 | 
						pub functions: Vec<Function>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct Gadget {
 | 
				
			||||||
 | 
						pub cname: String,
 | 
				
			||||||
 | 
						pub properties: Vec<Property>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct Property {
 | 
				
			||||||
 | 
						#[serde(rename = "type")]
 | 
				
			||||||
 | 
						pub type_: String,
 | 
				
			||||||
 | 
						pub name: String,
 | 
				
			||||||
 | 
						pub details: Option<String>,
 | 
				
			||||||
 | 
						pub readable: bool,
 | 
				
			||||||
 | 
						pub writable: bool,
 | 
				
			||||||
 | 
						pub default: bool,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct Function {
 | 
				
			||||||
 | 
						pub ret: String,
 | 
				
			||||||
 | 
						pub name: String,
 | 
				
			||||||
 | 
						pub details: Option<String>,
 | 
				
			||||||
 | 
						pub params: Vec<FnParam>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct FnParam {
 | 
				
			||||||
 | 
						#[serde(rename = "type")]
 | 
				
			||||||
 | 
						pub type_: String,
 | 
				
			||||||
 | 
						pub name: String,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct Enum {
 | 
				
			||||||
 | 
						pub name: String,
 | 
				
			||||||
 | 
						pub cname: Option<String>,
 | 
				
			||||||
 | 
						pub module: Option<String>,
 | 
				
			||||||
 | 
						pub description: Option<String>,
 | 
				
			||||||
 | 
						pub details: Option<String>,
 | 
				
			||||||
 | 
						pub varaints: Vec<Variant>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub struct Variant {
 | 
				
			||||||
 | 
						pub name: String,
 | 
				
			||||||
 | 
						pub details: Option<String>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										57
									
								
								types/QtQuick.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								types/QtQuick.json
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,57 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						"typemap": [
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"name": "void",
 | 
				
			||||||
 | 
								"cname": "void",
 | 
				
			||||||
 | 
								"module": null
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"name": "variant",
 | 
				
			||||||
 | 
								"cname": "QVariant",
 | 
				
			||||||
 | 
								"module": null
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"name": "bool",
 | 
				
			||||||
 | 
								"cname": "bool",
 | 
				
			||||||
 | 
								"module": null
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"name": "int",
 | 
				
			||||||
 | 
								"cname": "qint32",
 | 
				
			||||||
 | 
								"module": null
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"name": "real",
 | 
				
			||||||
 | 
								"cname": "qreal",
 | 
				
			||||||
 | 
								"module": null
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"name": "string",
 | 
				
			||||||
 | 
								"cname": "QString",
 | 
				
			||||||
 | 
								"module": null
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"name": "color",
 | 
				
			||||||
 | 
								"cname": "QColor",
 | 
				
			||||||
 | 
								"module": null
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"name": "QtObject",
 | 
				
			||||||
 | 
								"cname": "QObject",
 | 
				
			||||||
 | 
								"module": "qml.QtQml"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"name": "Component",
 | 
				
			||||||
 | 
								"cname": "QQmlComponent",
 | 
				
			||||||
 | 
								"module": "qml.QtQml"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"name": "Item",
 | 
				
			||||||
 | 
								"cname": "QQuickItem",
 | 
				
			||||||
 | 
								"module": "qml.QtQuick"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						],
 | 
				
			||||||
 | 
						"classes": [],
 | 
				
			||||||
 | 
						"gadgets": [],
 | 
				
			||||||
 | 
						"enums": []
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue