diff --git a/src/GDShrapt.Reader.Tests/SyntaxTests.cs b/src/GDShrapt.Reader.Tests/SyntaxTests.cs index 6f42ab0..7cbb570 100644 --- a/src/GDShrapt.Reader.Tests/SyntaxTests.cs +++ b/src/GDShrapt.Reader.Tests/SyntaxTests.cs @@ -1,6 +1,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Linq; +using System.Xml.Linq; namespace GDShrapt.Reader.Tests { @@ -414,5 +415,51 @@ func _process(delta) -> void: AssertHelper.CompareCodeStrings(code, @class.ToString()); AssertHelper.NoInvalidTokens(@class); } + + [TestMethod] + public void AttributesDeclaredBeforeTest() + { + var reader = new GDScriptReader(); + + var code = @" +@tool +@static_unload +class_name MyClass extends Node +@export var a = ""Hello"" +@onready @export var b = ""init_value_b"""; + + var @class = reader.ParseFileContent(code); + + var members = @class.Members.ToArray(); + + Assert.AreEqual(9, members.Length); + + members[8].AttributesDeclaredBefore.Select(x => x.ToString()).Should().BeEquivalentTo(new[] + { + "@export ", + "@onready " + }); + + members[8].AttributesDeclaredBeforeFromStartOfTheClass.Select(x => x.ToString()).Should().BeEquivalentTo(new[] + { + "@export ", + "@onready ", + "@export ", + "@static_unload", + "@tool" + }); + + members[5].AttributesDeclaredBeforeFromStartOfTheClass.Select(x => x.ToString()).Should().BeEquivalentTo(new[] + { + "@export ", + "@static_unload", + "@tool" + }); + + members[5].AttributesDeclaredBefore.Select(x => x.ToString()).Should().BeEquivalentTo(new[] + { + "@export " + }); + } } } diff --git a/src/GDShrapt.Reader/Basics/GDNode.cs b/src/GDShrapt.Reader/Basics/GDNode.cs index addd2b9..f37c07f 100644 --- a/src/GDShrapt.Reader/Basics/GDNode.cs +++ b/src/GDShrapt.Reader/Basics/GDNode.cs @@ -8,7 +8,7 @@ namespace GDShrapt.Reader /// /// Basic GDScript node, may contains multiple tokens /// - public abstract class GDNode : GDSyntaxToken, + public abstract class GDNode : GDSyntaxToken, IGDNode, ITokenReceiver, ITokenReceiver { @@ -21,7 +21,6 @@ public GDSyntaxToken[] FormTokensSetter { set => Form.SetFormUnsafe(value); } - public IEnumerable Tokens => Form.Direct(); public IEnumerable TokensReversed => Form.Reversed(); diff --git a/src/GDShrapt.Reader/Basics/IGDClassDeclaration.cs b/src/GDShrapt.Reader/Basics/IGDClassDeclaration.cs index 2213972..778dde8 100644 --- a/src/GDShrapt.Reader/Basics/IGDClassDeclaration.cs +++ b/src/GDShrapt.Reader/Basics/IGDClassDeclaration.cs @@ -2,7 +2,7 @@ namespace GDShrapt.Reader { - public interface IGDClassDeclaration : IGDSyntaxToken + public interface IGDClassDeclaration : IGDNode { GDIdentifier Identifier { get; } GDClassMembersList Members { get; } @@ -13,5 +13,6 @@ public interface IGDClassDeclaration : IGDSyntaxToken IEnumerable Enums { get; } IEnumerable InnerClasses { get; } IEnumerable IdentifiableMembers { get; } + IEnumerable CustomAttributes { get; } } } diff --git a/src/GDShrapt.Reader/Basics/IGDNode.cs b/src/GDShrapt.Reader/Basics/IGDNode.cs new file mode 100644 index 0000000..e90b120 --- /dev/null +++ b/src/GDShrapt.Reader/Basics/IGDNode.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + +namespace GDShrapt.Reader +{ + public interface IGDNode : IGDSyntaxToken + { + IEnumerable Nodes { get; } + IEnumerable NodesReversed { get; } + IEnumerable AllNodes { get; } + IEnumerable AllNodesReversed { get; } + IEnumerable AllTokens { get; } + IEnumerable AllTokensReversed { get; } + + int TokensCount { get; } + bool HasTokens { get; } + GDTokensForm Form { get; } + GDSyntaxToken[] FormTokensSetter { set; } + + void UpdateIntendation(); + } +} diff --git a/src/GDShrapt.Reader/Declarations/Class/GDClassMember.cs b/src/GDShrapt.Reader/Declarations/Class/GDClassMember.cs index 3f3dcf9..994008d 100644 --- a/src/GDShrapt.Reader/Declarations/Class/GDClassMember.cs +++ b/src/GDShrapt.Reader/Declarations/Class/GDClassMember.cs @@ -1,8 +1,10 @@ -namespace GDShrapt.Reader +using System.Collections.Generic; + +namespace GDShrapt.Reader { public abstract class GDClassMember : GDIntendedNode { - internal GDClassMember(int intendation) + internal GDClassMember(int intendation) : base(intendation) { } @@ -11,5 +13,62 @@ internal GDClassMember() : base() { } + + public IEnumerable AttributesDeclaredBefore + { + get + { + var @class = ClassDeclaration; + + if (@class == null) + yield break; + + bool foundThis = false; + + foreach (var item in @class.Members.NodesReversed) + { + if (!foundThis) + { + if (ReferenceEquals(item, this)) + foundThis = true; + } + else + { + if (item is GDCustomAttribute attr) + yield return attr; + else + yield break; + + } + } + } + } + + public IEnumerable AttributesDeclaredBeforeFromStartOfTheClass + { + get + { + var @class = ClassDeclaration; + + if (@class == null) + yield break; + + bool foundThis = false; + + foreach (var item in @class.Members.NodesReversed) + { + if (!foundThis) + { + if (ReferenceEquals(item, this)) + foundThis = true; + } + else + { + if (item is GDCustomAttribute attr) + yield return attr; + } + } + } + } } } diff --git a/src/GDShrapt.Reader/Declarations/GDClassDeclaration.cs b/src/GDShrapt.Reader/Declarations/GDClassDeclaration.cs index beea458..ddfc9d3 100644 --- a/src/GDShrapt.Reader/Declarations/GDClassDeclaration.cs +++ b/src/GDShrapt.Reader/Declarations/GDClassDeclaration.cs @@ -35,6 +35,8 @@ public GDClassDeclaration() public bool IsTool => Attributes.OfType().Any(); public IEnumerable Attributes => Members.OfType(); + public IEnumerable CustomAttributes => Members.OfType(); + public IEnumerable Variables => Members.OfType(); public IEnumerable Methods => Members.OfType(); public IEnumerable Enums => Members.OfType(); diff --git a/src/GDShrapt.Reader/Declarations/GDInnerClassDeclaration.cs b/src/GDShrapt.Reader/Declarations/GDInnerClassDeclaration.cs index b3b61f2..d923e8e 100644 --- a/src/GDShrapt.Reader/Declarations/GDInnerClassDeclaration.cs +++ b/src/GDShrapt.Reader/Declarations/GDInnerClassDeclaration.cs @@ -16,6 +16,7 @@ public sealed class GDInnerClassDeclaration : GDIdentifiableClassMember, IGDClas public IEnumerable Enums => Members.OfType(); public IEnumerable InnerClasses => Members.OfType(); public IEnumerable IdentifiableMembers => Members.OfType(); + public IEnumerable CustomAttributes => Members.OfType(); public GDClassKeyword ClassKeyword {