mirror of
https://github.com/godotengine/godot-vscode-plugin.git
synced 2025-12-31 13:48:24 +03:00
Add debugger support for Typed Dictionaries (#764)
Add decoding support for Typed Dictionaries
This commit is contained in:
@@ -24,9 +24,8 @@ import {
|
||||
ENCODE_FLAG_64,
|
||||
ENCODE_FLAG_OBJECT_AS_ID,
|
||||
ENCODE_FLAG_TYPED_ARRAY_MASK,
|
||||
ENCODE_FLAG_TYPED_ARRAY_BUILTIN,
|
||||
ENCODE_FLAG_TYPED_ARRAY_CLASS_NAME,
|
||||
ENCODE_FLAG_TYPED_ARRAY_SCRIPT,
|
||||
ENCODE_FLAG_TYPED_DICT_MASK,
|
||||
ContainerTypeFlags,
|
||||
RID,
|
||||
Callable,
|
||||
Signal,
|
||||
@@ -145,15 +144,9 @@ export class VariantDecoder {
|
||||
case GDScriptTypes.SIGNAL:
|
||||
return this.decode_Signal(model);
|
||||
case GDScriptTypes.DICTIONARY:
|
||||
return this.decode_Dictionary(model);
|
||||
return this.decode_Dictionary(model, type);
|
||||
case GDScriptTypes.ARRAY:
|
||||
if (type & ENCODE_FLAG_TYPED_ARRAY_MASK) {
|
||||
const with_script_name = (type & ENCODE_FLAG_TYPED_ARRAY_CLASS_NAME) != 0;
|
||||
const with_script_path = (type & ENCODE_FLAG_TYPED_ARRAY_SCRIPT) != 0;
|
||||
return this.decode_TypedArray(model, with_script_name || with_script_path);
|
||||
} else {
|
||||
return this.decode_Array(model);
|
||||
}
|
||||
return this.decode_Array(model, type);
|
||||
case GDScriptTypes.PACKED_BYTE_ARRAY:
|
||||
return this.decode_PackedByteArray(model);
|
||||
case GDScriptTypes.PACKED_INT32_ARRAY:
|
||||
@@ -215,6 +208,15 @@ export class VariantDecoder {
|
||||
return output;
|
||||
}
|
||||
|
||||
private decode_ContainerTypeFlag(model: BufferModel, type: GDScriptTypes, bitOffset: number) {
|
||||
const shiftedType = (type >> bitOffset) & 0b11;
|
||||
if (shiftedType === ContainerTypeFlags.BUILTIN) {
|
||||
return this.decode_UInt32(model);
|
||||
} else {
|
||||
return this.decode_String(model);
|
||||
}
|
||||
}
|
||||
|
||||
private decode_AABBf(model: BufferModel) {
|
||||
return new AABB(this.decode_Vector3f(model), this.decode_Vector3f(model));
|
||||
}
|
||||
@@ -223,25 +225,16 @@ export class VariantDecoder {
|
||||
return new AABB(this.decode_Vector3d(model), this.decode_Vector3d(model));
|
||||
}
|
||||
|
||||
private decode_Array(model: BufferModel) {
|
||||
const output: Array<any> = [];
|
||||
private decode_Array(model: BufferModel, type: GDScriptTypes) {
|
||||
const output = [];
|
||||
|
||||
const count = this.decode_UInt32(model);
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const value = this.decode_variant(model);
|
||||
output.push(value);
|
||||
let arrayType = null;
|
||||
if (type & ENCODE_FLAG_TYPED_ARRAY_MASK) {
|
||||
arrayType = this.decode_ContainerTypeFlag(model, type, 16);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private decode_TypedArray(model: BufferModel, with_scripts: boolean) {
|
||||
const output: Array<any> = [];
|
||||
|
||||
// TODO: the type information is currently discarded
|
||||
// it needs to be decoded and then packed into the output somehow
|
||||
const type = with_scripts ? this.decode_String(model) : this.decode_UInt32(model);
|
||||
|
||||
const count = this.decode_UInt32(model);
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
@@ -275,8 +268,20 @@ export class VariantDecoder {
|
||||
return new Color(rgb.x, rgb.y, rgb.z, a);
|
||||
}
|
||||
|
||||
private decode_Dictionary(model: BufferModel) {
|
||||
const output = new Map<any, any>();
|
||||
private decode_Dictionary(model: BufferModel, type: GDScriptTypes) {
|
||||
const output = new Map();
|
||||
|
||||
let keyType = null;
|
||||
let valueType = null;
|
||||
if (type & ENCODE_FLAG_TYPED_DICT_MASK) {
|
||||
keyType = this.decode_ContainerTypeFlag(model, type, 16);
|
||||
valueType = this.decode_ContainerTypeFlag(model, type, 18);
|
||||
|
||||
// console.log("type:", (type >> 16) & 0b11, "keyType:", keyType);
|
||||
// console.log("type:", type >> 18, "valueType:", valueType);
|
||||
}
|
||||
// TODO: the type information is currently discarded
|
||||
// it needs to be decoded and then packed into the output somehow
|
||||
|
||||
const count = this.decode_UInt32(model);
|
||||
for (let i = 0; i < count; i++) {
|
||||
|
||||
@@ -54,10 +54,14 @@ export enum GDScriptTypes {
|
||||
|
||||
export const ENCODE_FLAG_64 = 1 << 16;
|
||||
export const ENCODE_FLAG_OBJECT_AS_ID = 1 << 16;
|
||||
export const ENCODE_FLAG_TYPED_ARRAY_MASK = 3 << 16;
|
||||
export const ENCODE_FLAG_TYPED_ARRAY_BUILTIN = 1 << 16;
|
||||
export const ENCODE_FLAG_TYPED_ARRAY_CLASS_NAME = 2 << 16;
|
||||
export const ENCODE_FLAG_TYPED_ARRAY_SCRIPT = 3 << 16;
|
||||
export const ENCODE_FLAG_TYPED_ARRAY_MASK = 0b11 << 16;
|
||||
export const ENCODE_FLAG_TYPED_DICT_MASK = 0b1111 << 16;
|
||||
|
||||
export enum ContainerTypeFlags {
|
||||
BUILTIN = 1,
|
||||
CLASS_NAME = 2,
|
||||
SCRIPT = 3,
|
||||
}
|
||||
|
||||
export interface BufferModel {
|
||||
buffer: Buffer;
|
||||
|
||||
@@ -1,23 +1,13 @@
|
||||
import {
|
||||
TreeDataProvider,
|
||||
EventEmitter,
|
||||
Event,
|
||||
ProviderResult,
|
||||
TreeItem,
|
||||
TreeItemCollapsibleState,
|
||||
} from "vscode";
|
||||
import { TreeDataProvider, EventEmitter, Event, ProviderResult, TreeItem, TreeItemCollapsibleState } from "vscode";
|
||||
import { GodotVariable, RawObject, ObjectId } from "./debug_runtime";
|
||||
|
||||
export class InspectorProvider implements TreeDataProvider<RemoteProperty> {
|
||||
private _on_did_change_tree_data: EventEmitter<
|
||||
private _on_did_change_tree_data: EventEmitter<RemoteProperty | undefined> = new EventEmitter<
|
||||
RemoteProperty | undefined
|
||||
> = new EventEmitter<RemoteProperty | undefined>();
|
||||
>();
|
||||
private tree: RemoteProperty | undefined;
|
||||
|
||||
public readonly onDidChangeTreeData: Event<RemoteProperty> | undefined = this
|
||||
._on_did_change_tree_data.event;
|
||||
|
||||
constructor() {}
|
||||
public readonly onDidChangeTreeData: Event<RemoteProperty> | undefined = this._on_did_change_tree_data.event;
|
||||
|
||||
public clean_up() {
|
||||
if (this.tree) {
|
||||
@@ -26,12 +16,7 @@ export class InspectorProvider implements TreeDataProvider<RemoteProperty> {
|
||||
}
|
||||
}
|
||||
|
||||
public fill_tree(
|
||||
element_name: string,
|
||||
class_name: string,
|
||||
object_id: number,
|
||||
variable: GodotVariable
|
||||
) {
|
||||
public fill_tree(element_name: string, class_name: string, object_id: number, variable: GodotVariable) {
|
||||
this.tree = this.parse_variable(variable, object_id);
|
||||
this.tree.label = element_name;
|
||||
this.tree.collapsibleState = TreeItemCollapsibleState.Expanded;
|
||||
@@ -39,9 +24,7 @@ export class InspectorProvider implements TreeDataProvider<RemoteProperty> {
|
||||
this._on_did_change_tree_data.fire(undefined);
|
||||
}
|
||||
|
||||
public getChildren(
|
||||
element?: RemoteProperty
|
||||
): ProviderResult<RemoteProperty[]> {
|
||||
public getChildren(element?: RemoteProperty): ProviderResult<RemoteProperty[]> {
|
||||
if (!this.tree) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
@@ -57,15 +40,11 @@ export class InspectorProvider implements TreeDataProvider<RemoteProperty> {
|
||||
return element;
|
||||
}
|
||||
|
||||
public get_changed_value(
|
||||
parents: RemoteProperty[],
|
||||
property: RemoteProperty,
|
||||
new_parsed_value: any
|
||||
) {
|
||||
public get_changed_value(parents: RemoteProperty[], property: RemoteProperty, new_parsed_value: any) {
|
||||
const idx = parents.length - 1;
|
||||
const value = parents[idx].value;
|
||||
if (Array.isArray(value)) {
|
||||
const idx = parseInt(property.label);
|
||||
const idx = Number.parseInt(property.label);
|
||||
if (idx < value.length) {
|
||||
value[idx] = new_parsed_value;
|
||||
}
|
||||
@@ -104,13 +83,9 @@ export class InspectorProvider implements TreeDataProvider<RemoteProperty> {
|
||||
if (Number.isInteger(value)) {
|
||||
rendered_value = `${value}`;
|
||||
} else {
|
||||
rendered_value = `${parseFloat(value.toFixed(5))}`;
|
||||
rendered_value = `${Number.parseFloat(value.toFixed(5))}`;
|
||||
}
|
||||
} else if (
|
||||
typeof value === "bigint" ||
|
||||
typeof value === "boolean" ||
|
||||
typeof value === "string"
|
||||
) {
|
||||
} else if (typeof value === "bigint" || typeof value === "boolean" || typeof value === "string") {
|
||||
rendered_value = `${value}`;
|
||||
} else if (typeof value === "undefined") {
|
||||
rendered_value = "null";
|
||||
@@ -131,25 +106,21 @@ export class InspectorProvider implements TreeDataProvider<RemoteProperty> {
|
||||
let child_props: RemoteProperty[] = [];
|
||||
|
||||
if (value) {
|
||||
const sub_variables =
|
||||
typeof value["sub_values"] === "function" &&
|
||||
value instanceof ObjectId === false
|
||||
? value.sub_values()
|
||||
: Array.isArray(value)
|
||||
? value.map((va, i) => {
|
||||
return { name: `${i}`, value: va };
|
||||
})
|
||||
: value instanceof Map
|
||||
? Array.from(value.keys()).map((va) => {
|
||||
const name =
|
||||
typeof va["rendered_value"] === "function"
|
||||
? va.rendered_value()
|
||||
: `${va}`;
|
||||
const map_value = value.get(va);
|
||||
let sub_variables = [];
|
||||
if (typeof value.sub_values === "function" && value instanceof ObjectId === false) {
|
||||
sub_variables = value.sub_values();
|
||||
} else if (Array.isArray(value)) {
|
||||
sub_variables = value.map((va, i) => {
|
||||
return { name: `${i}`, value: va };
|
||||
});
|
||||
} else if (value instanceof Map) {
|
||||
sub_variables = Array.from(value.keys()).map((va) => {
|
||||
const name = typeof va.rendered_value === "function" ? va.rendered_value() : `${va}`;
|
||||
const map_value = value.get(va);
|
||||
return { name: name, value: map_value };
|
||||
});
|
||||
}
|
||||
|
||||
return { name: name, value: map_value };
|
||||
})
|
||||
: [];
|
||||
child_props = sub_variables?.map((va) => {
|
||||
return this.parse_variable(va, object_id);
|
||||
});
|
||||
@@ -160,14 +131,12 @@ export class InspectorProvider implements TreeDataProvider<RemoteProperty> {
|
||||
value,
|
||||
object_id,
|
||||
child_props,
|
||||
child_props.length === 0
|
||||
? TreeItemCollapsibleState.None
|
||||
: TreeItemCollapsibleState.Collapsed
|
||||
child_props.length === 0 ? TreeItemCollapsibleState.None : TreeItemCollapsibleState.Collapsed,
|
||||
);
|
||||
out_prop.description = rendered_value;
|
||||
out_prop.properties.forEach((prop) => {
|
||||
for (const prop of out_prop.properties) {
|
||||
prop.parent = out_prop;
|
||||
});
|
||||
}
|
||||
out_prop.description = rendered_value;
|
||||
|
||||
if (value instanceof ObjectId) {
|
||||
@@ -180,11 +149,10 @@ export class InspectorProvider implements TreeDataProvider<RemoteProperty> {
|
||||
typeof value === "string"
|
||||
) {
|
||||
out_prop.contextValue = "editable_value";
|
||||
} else if (
|
||||
Array.isArray(value) ||
|
||||
(value instanceof Map && value instanceof RawObject === false)
|
||||
) {
|
||||
out_prop.properties.forEach((prop) => (prop.changes_parent = true));
|
||||
} else if (Array.isArray(value) || (value instanceof Map && value instanceof RawObject === false)) {
|
||||
for (const prop of out_prop.properties) {
|
||||
prop.parent = out_prop;
|
||||
}
|
||||
}
|
||||
|
||||
return out_prop;
|
||||
@@ -200,7 +168,7 @@ export class RemoteProperty extends TreeItem {
|
||||
public value: any,
|
||||
public object_id: number,
|
||||
public properties: RemoteProperty[],
|
||||
public collapsibleState?: TreeItemCollapsibleState
|
||||
public collapsibleState?: TreeItemCollapsibleState,
|
||||
) {
|
||||
super(label, collapsibleState);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user