@@ -4,6 +4,7 @@ import 'package:analyzer/dart/analysis/results.dart';
44import 'package:analyzer/dart/ast/ast.dart' ;
55import 'package:analyzer/dart/constant/value.dart' ;
66import 'package:analyzer/dart/element/element.dart' ;
7+ import 'package:analyzer/dart/element/nullability_suffix.dart' ;
78import 'package:analyzer/dart/element/type.dart' ;
89import 'package:build/build.dart' ;
910import 'package:code_builder/code_builder.dart' as c;
@@ -295,50 +296,95 @@ class GodotScriptAnnotationGenerator
295296 ? field.type
296297 : (field as PropertyAccessorElement ).returnType;
297298
298- buffer.writeln ('DartPropertyInfo<$parentType , $type >(' );
299+ // Handle lists super spec
300+ bool isList = type.isDartCoreList;
301+ DartType ? listType;
302+ if (isList && type is ParameterizedType ) {
303+ listType = type.typeArguments.first;
304+ }
305+
306+ buffer
307+ .writeln ('DartPropertyInfo<$parentType , ${isList ? 'Array' : type }>(' );
299308 buffer.writeln (' name: \' $exportName \' ,' );
300309 buffer.writeln (' typeInfo: ${_typeInfoForType (type )},' );
301310
302- final propertyHint = _getPropertyHint (type);
311+ final propertyHint = _getPropertyHint (type, packageName );
303312 if (propertyHint != null ) {
304- buffer.writeln (' hint: ${propertyHint .toString ()},' );
305- buffer.writeln (
306- ' hintString: \' ${_getPropertyHintString (type , packageName )}\' ,' );
313+ buffer.writeln (' hint: ${propertyHint .hint .toString ()},' );
314+ buffer.writeln (' hintString: \' ${propertyHint .hintString }\' ,' );
307315 }
308- buffer.writeln (' getter: (self) => self.${field .name },' );
309- buffer.writeln (' setter: (self, value) => self.${field .name } = value,' );
316+ var getterBody = 'self.${field .name }' ;
317+ var setterBody = 'self.${field .name } = value' ;
318+ if (isList && listType != null ) {
319+ if (listType.nullabilitySuffix != NullabilitySuffix .question) {
320+ log.warning (
321+ '$parentType .$exportName has a non-nullable element type. It is highly recommended'
322+ ' that you make List elements nullable in order to make editing them possible.' );
323+ }
324+ getterBody = 'GDArrayExtensions.fromList(self.${field .name })' ;
325+ setterBody = 'self.${field .name } = value.toDartList()' ;
326+ }
327+ buffer.writeln (' getter: (self) => $getterBody ,' );
328+ buffer.writeln (' setter: (self, value) => $setterBody ,' );
310329
311330 buffer.write (')' );
312331
313332 return buffer.toString ();
314333 }
315334
316- PropertyHint ? _getPropertyHint (DartType type) {
335+ ({PropertyHint hint, String hintString})? _getPropertyHint (
336+ DartType type, String packageName) {
317337 final element = type.element;
338+
339+ String getScriptResourceForType (DartType type) {
340+ final element = type.element;
341+ if (element is ClassElement &&
342+ _godotScriptChecker.hasAnnotationOf (element,
343+ throwOnUnresolved: false )) {
344+ final relativeName = element.library.firstFragment.source.fullName
345+ .replaceFirst ('/$packageName /' , '' );
346+ return 'res://src/$relativeName ' ;
347+ }
348+
349+ // Else, return its type
350+ return type.element! .name! ;
351+ }
352+
318353 if (element is ClassElement ) {
319354 for (final supertype in element.allSupertypes) {
320355 if (supertype.element.name == 'Node' ) {
321- return PropertyHint .nodeType;
356+ return (
357+ hint: PropertyHint .nodeType,
358+ hintString: getScriptResourceForType (type)
359+ );
322360 } else if (supertype.element.name == 'Resource' ) {
323- return PropertyHint .resourceType;
361+ return (
362+ hint: PropertyHint .resourceType,
363+ hintString: getScriptResourceForType (type)
364+ );
324365 }
325366 }
326367 }
327- return null ;
328- }
329368
330- String _getPropertyHintString (DartType type, String packageName) {
331- final element = type.element;
332- if (element is ClassElement &&
333- _godotScriptChecker.hasAnnotationOf (element,
334- throwOnUnresolved: false )) {
335- final relativeName = element.library.firstFragment.source.fullName
336- .replaceFirst ('/$packageName /' , '' );
337- return 'res://src/$relativeName ' ;
369+ if (type.isDartCoreList) {
370+ final arrayElementType = (type as ParameterizedType ).typeArguments.first;
371+ final arrayElementVariantType = _variantTypeForType (arrayElementType);
372+ if (arrayElementVariantType != null ) {
373+ final elementHint = _getPropertyHint (arrayElementType, packageName);
374+ if (elementHint != null ) {
375+ final hintString =
376+ '${arrayElementVariantType .value }/${elementHint .hint .value }:${elementHint .hintString }' ;
377+
378+ return (hint: PropertyHint .typeString, hintString: hintString);
379+ } else {
380+ final hintString =
381+ '${arrayElementVariantType .value }/${PropertyHint .none .value }:' ;
382+ return (hint: PropertyHint .typeString, hintString: hintString);
383+ }
384+ }
338385 }
339386
340- // Else, return its type
341- return type.element! .name! ;
387+ return null ;
342388 }
343389
344390 String _buildRpcMethodInfo (MethodElement method, DartObject rpcAnnotation) {
@@ -372,6 +418,8 @@ class GodotScriptAnnotationGenerator
372418
373419 if (isPrimitive (type)) {
374420 return 'PrimitiveTypeInfo.forType($typeName )!' ;
421+ } else if (type.isDartCoreList) {
422+ return 'Array.sTypeInfo' ;
375423 } else if (typeName == 'Variant' ) {
376424 return 'Variant.sTypeInfo' ;
377425 } else if (type is VoidType ) {
@@ -381,6 +429,60 @@ class GodotScriptAnnotationGenerator
381429 }
382430 }
383431
432+ VariantType ? _variantTypeForType (DartType type) {
433+ if (type.isDartCoreInt) {
434+ return VariantType .integer;
435+ } else if (type.isDartCoreDouble) {
436+ return VariantType .float;
437+ } else if (type.isDartCoreBool) {
438+ return VariantType .bool ;
439+ } else if (type.isDartCoreString) {
440+ return VariantType .string;
441+ } else if (type.isDartCoreEnum) {
442+ return VariantType .integer;
443+ }
444+
445+ const variantTypeMap = < String , VariantType > {
446+ 'Vector2' : VariantType .vector2,
447+ 'Vector2i' : VariantType .vector2i,
448+ 'Rect2' : VariantType .rect2,
449+ 'Rect2i' : VariantType .rect2i,
450+ 'Vector3' : VariantType .vector3,
451+ 'Vector3i' : VariantType .vector3i,
452+ 'Transform2D' : VariantType .transform2d,
453+ 'Vector4' : VariantType .vector4,
454+ 'Vector4i' : VariantType .vector4i,
455+ 'Plane' : VariantType .plane,
456+ 'Quaternion' : VariantType .quaternion,
457+ 'AABB' : VariantType .aabb,
458+ 'Basis' : VariantType .basis,
459+ 'Transform3D' : VariantType .transform3d,
460+ 'Projection' : VariantType .projection,
461+ 'Color' : VariantType .color,
462+ 'StringName' : VariantType .stringName,
463+ 'GDStringName' : VariantType .string,
464+ 'NodePath' : VariantType .nodePath,
465+ 'RID' : VariantType .rid,
466+ 'PackedByteArray' : VariantType .packedByteArray,
467+ 'PackedInt32Array' : VariantType .packedInt32Array,
468+ 'PackedInt64Array' : VariantType .packedFloat64Array,
469+ 'PackedFloatArray' : VariantType .packedFloat64Array,
470+ 'PackedStringArray' : VariantType .packedStringArray,
471+ 'PackedVector2Array' : VariantType .packedVector2Array,
472+ 'PackedVector3Array' : VariantType .packedVector3Array,
473+ 'PackedVector4Array' : VariantType .packedVector4Array,
474+ 'PackedColor4Array' : VariantType .packedColorArray,
475+ // Not supported via conversion
476+ // Object
477+ // Callable
478+ // Signal
479+ // Dictionary
480+ // Array
481+ };
482+
483+ return variantTypeMap[type.element? .name];
484+ }
485+
384486 String _convertVirtualMethodName (String methodName) {
385487 var name = methodName;
386488 if (methodName.startsWith (RegExp ('v[A-Z]' ))) {
0 commit comments