UNPKG

@ts-for-gir/lib

Version:

Typescript .d.ts generator from GIR for gjs

280 lines (246 loc) 6.88 kB
import type { GirCallableParamElement, GirSignalElement } from "@gi.ts/parser"; import { GirDirection } from "@gi.ts/parser"; import type { FormatGenerator } from "../generators/generator.ts"; import { ArrayType, makeNullable, NativeType, NullableType, NumberType, ThisType, type TypeExpression, UnknownType, VoidType, } from "../gir.ts"; import type { OptionsLoad } from "../types/index.ts"; import type { Options } from "../types/introspected.ts"; import { getType, parseDoc, parseMetadata } from "../utils/gir-parsing.ts"; import { isIntrospectable } from "../utils/girs.ts"; import type { GirVisitor } from "../visitor.ts"; import { IntrospectedClassMember } from "./introspected-class-member.ts"; import { type IntrospectedClass, IntrospectedClassCallback, IntrospectedClassFunction, } from "./introspected-classes.ts"; import { IntrospectedFunctionParameter } from "./parameter.ts"; export enum IntrospectedSignalType { CONNECT, CONNECT_AFTER, EMIT, } export class IntrospectedSignal extends IntrospectedClassMember<IntrospectedClass> { parameters: IntrospectedFunctionParameter[]; return_type: TypeExpression; detailed: boolean; action: boolean; noRecurse: boolean; noHooks: boolean; when?: "first" | "last" | "cleanup"; constructor({ name, parameters = [], return_type = UnknownType, detailed = false, action = false, noRecurse = false, noHooks = false, when, parent, ...args }: Options<{ name: string; parameters?: IntrospectedFunctionParameter[]; return_type?: TypeExpression; detailed?: boolean; action?: boolean; noRecurse?: boolean; noHooks?: boolean; when?: "first" | "last" | "cleanup"; parent: IntrospectedClass; }>) { super(name, parent, { ...args }); this.parameters = parameters.map((p) => p.copy({ parent: this })); this.return_type = return_type; this.detailed = detailed; this.action = action; this.noRecurse = noRecurse; this.noHooks = noHooks; this.when = when; } accept(visitor: GirVisitor): IntrospectedSignal { const node = this.copy({ parameters: this.parameters.map((p) => { return p.accept(visitor); }), returnType: visitor.visitType?.(this.return_type), }); return visitor.visitSignal?.(node) ?? node; } copy({ parent = this.parent, parameters, returnType, detailed, action, noRecurse, noHooks, when, }: { parent?: IntrospectedClass; parameters?: IntrospectedFunctionParameter[]; returnType?: TypeExpression; detailed?: boolean; action?: boolean; noRecurse?: boolean; noHooks?: boolean; when?: "first" | "last" | "cleanup"; } = {}): IntrospectedSignal { return new IntrospectedSignal({ name: this.name, parent, parameters: parameters ?? this.parameters, return_type: returnType ?? this.return_type, detailed: detailed ?? this.detailed, action: action ?? this.action, noRecurse: noRecurse ?? this.noRecurse, noHooks: noHooks ?? this.noHooks, when: when ?? this.when, })._copyBaseProperties(this); } static fromXML(element: GirSignalElement, parent: IntrospectedClass, options: OptionsLoad): IntrospectedSignal { const ns = parent.namespace; const signal = new IntrospectedSignal({ name: element.$.name, parent, detailed: element.$.detailed === "1", action: element.$.action === "1", noRecurse: element.$["no-recurse"] === "1", noHooks: element.$["no-hooks"] === "1", when: element.$.when as "first" | "last" | "cleanup" | undefined, isIntrospectable: isIntrospectable(element), }); if (element.parameters?.[0].parameter) { signal.parameters.push( ...element.parameters[0].parameter .filter((p): p is GirCallableParamElement & { $: { name: string } } => !!p.$.name) .map((p) => IntrospectedFunctionParameter.fromXML(p, signal, options)), ); } const length_params = [] as number[]; signal.parameters = signal.parameters .map((p) => { const unwrapped_type = p.type.unwrap(); if (unwrapped_type instanceof ArrayType && unwrapped_type.length != null) { length_params.push(unwrapped_type.length); return p; } return p; }) .filter((_, i) => { // We remove any length parameters. return !length_params.includes(i); }) .reverse() .reduce( ({ allowOptions, params }, p) => { const { type, isOptional } = p; if (allowOptions) { if (type instanceof NullableType) { params.push(p.copy({ isOptional: true })); } else if (!isOptional) { params.push(p); return { allowOptions: false, params }; } else { params.push(p); } } else { if (isOptional) { params.push(p.copy({ type: makeNullable(type), isOptional: false })); } else { params.push(p); } } return { allowOptions, params }; }, { allowOptions: true, params: [] as IntrospectedFunctionParameter[], }, ) .params.reverse() .filter((p): p is IntrospectedFunctionParameter => p != null); signal.return_type = getType(ns, element["return-value"]?.[0]); if (options.loadDocs) { signal.doc = parseDoc(element); signal.metadata = parseMetadata(element); } return signal; } asEmit() { const emit = this.copy(); const parent = this.parent; const prefix_signal = emit.parameters.some((p) => { return p.name === "signal"; }); const parameters = [ new IntrospectedFunctionParameter({ name: prefix_signal ? "$signal" : "signal", type: NativeType.of(`'${this.name}'`), direction: GirDirection.In, }), ...emit.parameters, ]; const return_type = VoidType; return new IntrospectedClassFunction({ return_type, parameters, name: "emit", parent, }); } asConnect(after = false) { const connect = this.copy(); const name = after ? "connect_after" : "connect"; const parent = this.parent; const cb = new IntrospectedClassCallback({ parent, name: "callback", output_parameters: [], parameters: [ new IntrospectedFunctionParameter({ name: "_source", type: ThisType, direction: GirDirection.In, }), ...connect.parameters.map((p) => p.copy()), ], return_type: connect.return_type, }); const parameters = [ new IntrospectedFunctionParameter({ name: "signal", type: NativeType.of(`'${this.name}'`), direction: GirDirection.In, }), new IntrospectedFunctionParameter({ name: "callback", type: cb.asFunctionType(), direction: GirDirection.In, }), ]; const return_type = NumberType; return new IntrospectedClassFunction({ return_type, parameters, name, parent: this.parent, }); } asString<T extends FormatGenerator<unknown>>( generator: T, type?: IntrospectedSignalType, ): ReturnType<T["generateSignal"]> { return generator.generateSignal(this, type) as ReturnType<T["generateSignal"]>; } }