From 375a05336a6431fad9e61e9b7f48a6976ddf3d4d Mon Sep 17 00:00:00 2001 From: Josef Date: Fri, 11 Jul 2025 10:16:45 +0200 Subject: [PATCH] Docs Generator + Biquad/Smoother --- RokojoriPlugin.cs | 4 +- Runtime/Animation/Shake/Shake.cs | 6 +- Runtime/Files/FilePath.cs | 8 + Runtime/Math/Biquad.cs | 172 ++++++++++++ Runtime/Math/Range.cs | 1 - Runtime/Math/RangeI.cs | 92 ++++++ Runtime/Math/RangeI.cs.uid | 1 + Runtime/Math/Smoother.cs | 6 +- Runtime/Procedural/Assets/Grass/GrassPatch.cs | 40 +-- Runtime/Text/Lexing/LexerEvent.cs | 215 ++++++++++++-- .../Text/Lexing/LexerLibrary/CSharpLexer.cs | 37 +++ .../Text/Lexing/LexerLibrary/GDShaderLexer.cs | 71 +++++ .../Lexing/LexerLibrary/GDShaderLexer.cs.uid | 1 + Runtime/Text/Lexing/LexerList.cs | 132 +++++++++ Runtime/Text/Lexing/LexerMatcherLibrary.cs | 14 +- Runtime/Text/Text.cs | 39 +++ Runtime/Time/DateTime/Date.cs | 29 ++ Runtime/Time/DateTime/Date.cs.uid | 1 + Runtime/Time/DateTime/DateMath.cs | 13 +- Runtime/Tools/Lists.cs | 27 ++ Runtime/Tools/Trillean.cs | 5 + Tools/Git/Git.cs | 120 ++++++++ Tools/Git/GitTest.cs | 35 ++- Tools/blender/Blender.cs | 13 + Tools/docs/ClassDocGenerator.cs | 90 ++++-- Tools/docs/ClassDocGenerator.cs.uid | 1 + Tools/docs/ClassDocInfo.cs | 32 ++- Tools/docs/ClassDocInfo.cs.uid | 1 + Tools/docs/CreateDoc.cs | 26 ++ Tools/docs/DocComments.cs | 2 +- Tools/docs/DocComments.cs.uid | 1 + Tools/docs/DocGenerator.cs | 143 ++++++++-- Tools/docs/DocGenerator.cs.uid | 1 + Tools/docs/GetDocsType.cs | 57 ++++ Tools/docs/GetDocsType.cs.uid | 1 + Tools/docs/ShaderDocGenerator.cs | 262 ++++++++++++++++++ Tools/docs/ShaderDocGenerator.cs.uid | 1 + 37 files changed, 1597 insertions(+), 103 deletions(-) create mode 100644 Runtime/Math/Biquad.cs create mode 100644 Runtime/Math/RangeI.cs create mode 100644 Runtime/Math/RangeI.cs.uid create mode 100644 Runtime/Text/Lexing/LexerLibrary/GDShaderLexer.cs create mode 100644 Runtime/Text/Lexing/LexerLibrary/GDShaderLexer.cs.uid create mode 100644 Runtime/Text/Lexing/LexerList.cs create mode 100644 Runtime/Time/DateTime/Date.cs create mode 100644 Runtime/Time/DateTime/Date.cs.uid create mode 100644 Tools/docs/ClassDocGenerator.cs.uid create mode 100644 Tools/docs/ClassDocInfo.cs.uid create mode 100644 Tools/docs/DocComments.cs.uid create mode 100644 Tools/docs/DocGenerator.cs.uid create mode 100644 Tools/docs/GetDocsType.cs create mode 100644 Tools/docs/GetDocsType.cs.uid create mode 100644 Tools/docs/ShaderDocGenerator.cs create mode 100644 Tools/docs/ShaderDocGenerator.cs.uid diff --git a/RokojoriPlugin.cs b/RokojoriPlugin.cs index 20c49c4..1dafab6 100644 --- a/RokojoriPlugin.cs +++ b/RokojoriPlugin.cs @@ -169,8 +169,8 @@ namespace Rokojori } float reloadDuration = 2; - DateTime lastUpdateTime = DateTime.Now; - DateTime lastDisposalTime = DateTime.Now; + DateTimeOffset lastUpdateTime = DateTime.Now; + DateTimeOffset lastDisposalTime = DateTime.Now; public void SaveCache( string relativeCachePath, object data ) { diff --git a/Runtime/Animation/Shake/Shake.cs b/Runtime/Animation/Shake/Shake.cs index fa98094..0f74474 100644 --- a/Runtime/Animation/Shake/Shake.cs +++ b/Runtime/Animation/Shake/Shake.cs @@ -170,9 +170,9 @@ namespace Rokojori var smoothStrength = rawSmooth * 250f; var current = TransformData.From( targets[ i ], shakeEffect.globalPosition, shakeEffect.globalRotation ); - combined.position = Smoother.SmoothTimeInvariant( current.position, combined.position, delta, smoothStrength ); - combined.rotation = Smoother.SmoothTimeInvariant( current.rotation, combined.rotation, delta, smoothStrength ); - combined.scale = Smoother.SmoothTimeInvariant( current.scale, combined.scale, delta, smoothStrength ); + combined.position = Smoother.SmoothTimeVariant( current.position, combined.position, delta, smoothStrength ); + combined.rotation = Smoother.SmoothTimeVariant( current.rotation, combined.rotation, delta, smoothStrength ); + combined.scale = Smoother.SmoothTimeVariant( current.scale, combined.scale, delta, smoothStrength ); } combined.Set( targets[ i ], shakeEffect.globalPosition, shakeEffect.globalRotation ); diff --git a/Runtime/Files/FilePath.cs b/Runtime/Files/FilePath.cs index 454df57..9b9df97 100644 --- a/Runtime/Files/FilePath.cs +++ b/Runtime/Files/FilePath.cs @@ -118,6 +118,14 @@ namespace Rokojori return rp; } + public string absolutePath + { + get + { + return fullPath; + } + } + public string absoluteParentPath { get diff --git a/Runtime/Math/Biquad.cs b/Runtime/Math/Biquad.cs new file mode 100644 index 0000000..5aa57a9 --- /dev/null +++ b/Runtime/Math/Biquad.cs @@ -0,0 +1,172 @@ +using Godot; + +namespace Rokojori +{ + public enum BiquadType + { + LowPass, + BandPass, + HighPass, + Peak, + LowShelf, + HighShelf, + Notch + } + + public class Biquad + { + + public float a0 = 0; + public float a1 = 0; + public float a2 = 0; + public float a3 = 0; + public float a4 = 0; + + public float x1 = 0; + public float x2 = 0; + public float y1 = 0; + public float y2 = 0; + + float process( float inputSample ) + { + var result = this.a0 * inputSample + + this.a1 * this.x1 + + this.a2 * this.x2 - + this.a3 * this.y1 - + this.a4 * this.y2; + + this.x2 = this.x1; + this.x1 = inputSample; + + this.y2 = this.y1; + this.y1 = result; + + return result; + } + + public static Biquad Create( BiquadType filterType, float gainDB, float frequency, float bandwidth, float sampleRate ) + { + var filter = new Biquad(); + float A= 0, omega= 0, sn= 0, cs= 0, alpha= 0, beta = 0; + float a0= 0, a1= 0, a2= 0, b0= 0, b1= 0, b2 = 0; + + var M_LN2 = 0.69314718055994530942f; + + A = Mathf.Pow( 10, gainDB / 40 ); + omega = 2 * Mathf.Pi * frequency / sampleRate; + sn = Mathf.Sin( omega ); + cs = Mathf.Cos( omega ); + alpha = ( sn * Mathf.Sinh( M_LN2 / 2 * bandwidth * omega / sn ) ); + beta = Mathf.Sqrt(A + A); + + switch ( filterType ) + { + case BiquadType.LowPass: + { + b0 = (1 - cs) /2; + b1 = 1 - cs; + b2 = (1 - cs) /2; + a0 = 1 + alpha; + a1 = -2 * cs; + a2 = 1 - alpha; + } + break; + + case BiquadType.HighPass: + { + b0 = (1 + cs) /2; + b1 = -(1 + cs); + b2 = (1 + cs) /2; + a0 = 1 + alpha; + a1 = -2 * cs; + a2 = 1 - alpha; + } + break; + + case BiquadType.BandPass: + { + b0 = alpha; + b1 = 0; + b2 = -alpha; + a0 = 1 + alpha; + a1 = -2 * cs; + a2 = 1 - alpha; + } + break; + + case BiquadType.Notch: + { + b0 = 1; + b1 = -2 * cs; + b2 = 1; + a0 = 1 + alpha; + a1 = -2 * cs; + a2 = 1 - alpha; + } + break; + + case BiquadType.Peak: + { + b0 = 1 + (alpha * A); + b1 = -2 * cs; + b2 = 1 - (alpha * A); + a0 = 1 + (alpha /A); + a1 = -2 * cs; + a2 = 1 - (alpha /A); + } + break; + + case BiquadType.LowShelf: + { + b0 = A * ((A + 1) - (A - 1) * cs + beta * sn); + b1 = 2 * A * ((A - 1) - (A + 1) * cs); + b2 = A * ((A + 1) - (A - 1) * cs - beta * sn); + a0 = (A + 1) + (A - 1) * cs + beta * sn; + a1 = -2 * ((A - 1) + (A + 1) * cs); + a2 = (A + 1) + (A - 1) * cs - beta * sn; + } + break; + + case BiquadType.HighShelf: + { + b0 = A * ((A + 1) + (A - 1) * cs + beta * sn); + b1 = -2 * A * ((A - 1) + (A + 1) * cs); + b2 = A * ((A + 1) + (A - 1) * cs - beta * sn); + a0 = (A + 1) - (A - 1) * cs + beta * sn; + a1 = 2 * ((A - 1) - (A + 1) * cs); + a2 = (A + 1) - (A - 1) * cs - beta * sn; + } + break; + + } + + filter.a0 = b0 / a0; + filter.a1 = b1 / a0; + filter.a2 = b2 / a0; + filter.a3 = a1 / a0; + filter.a4 = a2 / a0; + + filter.x1 = filter.x2 = 0; + filter.y1 = filter.y2 = 0; + + return filter; + } + + public static float filter( float inputSample, float[] coefficients, float[] buffer ) + { + var result = coefficients[ 0 ] * inputSample + + coefficients[ 1 ] * buffer[ 0 ] + + coefficients[ 2 ] * buffer[ 1 ] - + coefficients[ 3 ] * buffer[ 2 ] - + coefficients[ 4 ] * buffer[ 3 ]; + + buffer[ 1 ] = buffer[ 0 ]; + buffer[ 0 ] = inputSample; + + buffer[ 3 ] = buffer[ 2 ]; + buffer[ 2 ] = result; + + return result; + } + } +} \ No newline at end of file diff --git a/Runtime/Math/Range.cs b/Runtime/Math/Range.cs index 54b8c2e..64a1d80 100644 --- a/Runtime/Math/Range.cs +++ b/Runtime/Math/Range.cs @@ -4,7 +4,6 @@ using Godot; namespace Rokojori { - [System.Serializable] public class Range { public float min; diff --git a/Runtime/Math/RangeI.cs b/Runtime/Math/RangeI.cs new file mode 100644 index 0000000..d968c42 --- /dev/null +++ b/Runtime/Math/RangeI.cs @@ -0,0 +1,92 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; + +namespace Rokojori +{ + public class RangeI + { + public int min; + public int max; + + public RangeI( int min, int max ) + { + this.min = min; + this.max = max; + } + + public void EnsureCorrectness() + { + if ( max < min ) + { + var b = max; max = min; min = b; + } + } + + public bool Contains( int value ) + { + return min <= value && value <= max; + } + + public bool Overlaps( RangeI other ) + { + if ( other.max < min ) { return false; } + if ( other.min > max ) { return false; } + + if ( other.Contains( min ) || other.Contains( max ) ) + { + return true; + } + + if ( Contains( other.min ) || Contains( other.max ) ) + { + return true; + } + + return false; + } + + public RangeI Copy() + { + return new RangeI( min, max ); + } + + public int center { get { return ( max + min ) / 2; } } + public int range { get { return max - min; } } + public int length { get { return max - min; } } + + public static bool Contains( float min, float max, float value ) + { + return min <= value && value <= max; + } + + public static bool ContainsExclusive( float min, float max, float value ) + { + return min < value && value < max; + } + + public static bool ContainsExclusiveMax( float min, float max, float value ) + { + return min <= value && value < max; + } + + public static bool Overlap( float minA, float maxA, float minB, float maxB ) + { + if ( maxB < minA ) { return false; } + if ( minB > maxA ) { return false; } + + if ( Contains( minB, maxB, minA ) || Contains( minB, maxB, maxA ) ) + { + return true; + } + + if ( Contains( minA, maxA, minB ) || Contains( minA, maxA, maxB ) ) + { + return true; + } + + return false; + + } + } +} \ No newline at end of file diff --git a/Runtime/Math/RangeI.cs.uid b/Runtime/Math/RangeI.cs.uid new file mode 100644 index 0000000..c0e36bc --- /dev/null +++ b/Runtime/Math/RangeI.cs.uid @@ -0,0 +1 @@ +uid://bfe6pnhqcnw1q diff --git a/Runtime/Math/Smoother.cs b/Runtime/Math/Smoother.cs index 2698dc6..01098c0 100644 --- a/Runtime/Math/Smoother.cs +++ b/Runtime/Math/Smoother.cs @@ -8,19 +8,19 @@ namespace Rokojori { float overflowDelta = 0; - public static float SmoothTimeInvariant( float value, float nextValue, float delta, float coefficient ) + public static float SmoothTimeVariant( float value, float nextValue, float delta, float coefficient ) { var lerpAmount = Mathf.Exp( -coefficient * delta ); return Mathf.Lerp( nextValue, value, lerpAmount ); } - public static Vector2 SmoothTimeInvariant( Vector2 value, Vector2 nextValue, float delta, float coefficient ) + public static Vector2 SmoothTimeVariant( Vector2 value, Vector2 nextValue, float delta, float coefficient ) { var lerpAmount = Mathf.Exp( -coefficient * delta ); return Math2D.Lerp( nextValue, value, lerpAmount ); } - public static Vector3 SmoothTimeInvariant( Vector3 value, Vector3 nextValue, float delta, float coefficient ) + public static Vector3 SmoothTimeVariant( Vector3 value, Vector3 nextValue, float delta, float coefficient ) { var lerpAmount = Mathf.Exp( -coefficient * delta ); return Math3D.Lerp( nextValue, value, lerpAmount ); diff --git a/Runtime/Procedural/Assets/Grass/GrassPatch.cs b/Runtime/Procedural/Assets/Grass/GrassPatch.cs index 89a0f1b..e2034a9 100644 --- a/Runtime/Procedural/Assets/Grass/GrassPatch.cs +++ b/Runtime/Procedural/Assets/Grass/GrassPatch.cs @@ -19,12 +19,12 @@ namespace Rokojori public int seed = 1984; - [Export] - public bool update; - - [Export] - public bool updateAlways; - + [ExportToolButton( "Create")] + public Callable createButton => Callable.From( ()=> + { + CreatePatch(); + } + ); [Export] public Material material; @@ -252,34 +252,6 @@ namespace Rokojori [Export] public Curve highCurve = MathX.Curve( 0, 1 ); - // SerializedGodotObject _cached; - - public override void _Process( double delta ) - { - if ( ! ( update || updateAlways ) ) - { - return; - } - - update = false; - - - /*var current = SerializedGodotObject.Create( this ); - - var isEquals = _cached != null && _cached.Equals( current ); - - if ( _cached != null && _cached.Equals( current ) ) - { - return; - }*/ - - - - CreatePatch(); - - // _cached = current; - } - float _maxWidth = 0; Vector3 _patchOffset = Vector3.Zero; diff --git a/Runtime/Text/Lexing/LexerEvent.cs b/Runtime/Text/Lexing/LexerEvent.cs index f5f3cf8..3604468 100644 --- a/Runtime/Text/Lexing/LexerEvent.cs +++ b/Runtime/Text/Lexing/LexerEvent.cs @@ -132,6 +132,19 @@ namespace Rokojori return false; } + public bool IsAnyOf( params LexerMatcher[] matchers ) + { + for ( int i = 0; i < matchers.Length; i++ ) + { + if ( Is( matchers[ i ] ) ) + { + return true; + } + } + + return false; + } + public bool IsAny( LexerMatcher matcher, params string[] matches ) { if ( matches == null || matches.Length == 0 ) @@ -160,6 +173,9 @@ namespace Rokojori public class FindResult { public FindResultType type = FindResultType.NotFound; + + public bool found => FindResultType.Found == type; + public int index; @@ -225,11 +241,66 @@ namespace Rokojori } - public static FindResult Find( List tokens, int offset, System.Func evaluator ) + public static List> FindSequences( List tokens, System.Func matcher ) + { + var sequences = new List>(); + + List currentSequence = null; + + for ( int i = 0; i < tokens.Count; i++ ) + { + var token = tokens[ i ]; + var result = matcher( i, currentSequence != null ); + + if ( currentSequence != null ) + { + if ( Trillean.True == result ) + { + currentSequence.Add( token ); + } + else if ( Trillean.False == result ) + { + currentSequence.Add( token ); + + sequences.Add( currentSequence ); + currentSequence = null; + } + } + else + { + if ( Trillean.True == result ) + { + currentSequence = new List(); + currentSequence.Add( token ); + } + + if ( Trillean.False == result ) + { + sequences.Add( new List(){ tokens[ i ] } ); + sequences.Add( null ); + return sequences; + } + + } + + } + + return sequences; + } + + public static FindResult ReverseFind( List tokens, int offset, System.Func evaluator ) + { + return Find( tokens, offset, evaluator, false ); + } + + public static FindResult Find( List tokens, int offset, System.Func evaluator, bool forward = true ) { var result = new FindResult(); - for ( int i = offset; i < tokens.Count; i++ ) + var increment = forward ? 1 : -1; + var end = forward ? tokens.Count : -1; + + for ( int i = offset; i != end && i >= 0 && i < tokens.Count; i += increment ) { var tokenResult = evaluator( tokens[ i ] ); @@ -289,11 +360,12 @@ namespace Rokojori } + + static List openTypes = new List(){ "(","[","{" }; + static List closingTypes = new List(){ ")","]","}" }; + public static FindResult FindClosingBracket( List tokens, int offset ) - { - var openTypes = new List(){ "(","[","{" }; - var closingTypes = new List(){ ")","]","}" }; - + { var token = tokens[ offset ]; var bracketIndex = openTypes.IndexOf( token.match ); @@ -325,26 +397,135 @@ namespace Rokojori return FindCloser( tokens, offset, counter ); } - public static FindResult FindCloser( List tokens, int offset, System.Func counter ) + public static FindResult FindCloser( List tokens, int offset, System.Func counter, bool forward = true ) { var result = new FindResult(); var currentValue = 0; - for ( int i = offset; i < tokens.Count; i++ ) + if ( forward ) { - var countResult = counter( tokens[ i ] ); - - currentValue += countResult; - - if ( currentValue == 0 ) + for ( int i = offset; i < tokens.Count; i++ ) { - result.type = FindResultType.Found; - result.index = i; - return result; - } + var countResult = counter( tokens[ i ] ); + + currentValue += countResult; + + if ( currentValue == 0 ) + { + result.type = FindResultType.Found; + result.index = i; + return result; + } + } + } + else + { + for ( int i = offset; i >= 0; i-- ) + { + var countResult = counter( tokens[ i ] ); + + currentValue += countResult; + + if ( currentValue == 0 ) + { + result.type = FindResultType.Found; + result.index = i; + return result; + } + } } return result; } + + public static FindResult FindOpeningBracket( List tokens, int offset, string blockBracket = "{" ) + { + var result = Find( tokens, offset, + ( le ) => + { + return le.Is( LexerMatcherLibrary.BracketMatcher, blockBracket ) ? FindResultType.Found : FindResultType.KeepSearching; + } + ); + + return result; + } + + public static FindResult ReverseFindOpeningBracket( List tokens, int offset ) + { + + var token = tokens[ offset ]; + + var bracketIndex = closingTypes.IndexOf( token.match ); + + if ( bracketIndex == -1 ) + { + var result = new FindResult( FindResultType.NotFound, offset ); + return result; + } + + var opener = openTypes[ bracketIndex ]; + var closer = closingTypes[ bracketIndex ]; + + var counter = ( LexerEvent le ) => + { + if ( le.Is( LexerMatcherLibrary.BracketMatcher, closer ) ) + { + return 1; + } + + if ( le.Is( LexerMatcherLibrary.BracketMatcher, opener ) ) + { + return -1; + } + + return 0; + }; + + return FindCloser( tokens, offset, counter, false ); + } + + + + public static List GetBlocks( List tokens ) + { + var index = 0; + List blocks = new List(); + + while ( index < tokens.Count ) + { + var openResult = FindOpeningBracket( tokens, index ); + + if ( ! openResult.found || openResult.index < index ) + { + // RJLog.Log( "No OpeningBracket after", index ); + + if ( index == 0 ) + { + RJLog.Log( tokens.Map( t => t.type + ": '" + t.match + "'" ).Join( "\n" ) ); + } + return blocks; + } + + + var closeResult = FindClosingBracket( tokens, openResult.index ); + + if ( ! closeResult.found || closeResult.index <= openResult.index ) + { + // RJLog.Log( "No ClosingBracket after", index ); + return null; + } + + var range = new RangeI( openResult.index, closeResult.index ); + blocks.Add( range ); + + index = closeResult.index + 1; + + } + + return blocks; + } + + + } } \ No newline at end of file diff --git a/Runtime/Text/Lexing/LexerLibrary/CSharpLexer.cs b/Runtime/Text/Lexing/LexerLibrary/CSharpLexer.cs index 0565457..9b1010e 100644 --- a/Runtime/Text/Lexing/LexerLibrary/CSharpLexer.cs +++ b/Runtime/Text/Lexing/LexerLibrary/CSharpLexer.cs @@ -20,6 +20,39 @@ namespace Rokojori return events; } + public static List> GetAllObjectDefinitions( string source ) + { + var tokens = Lex( source ).Filter( e => ! ( e.isDone || e.isError ) ); + + var sequences = LexerEvent.FindSequences( tokens, + ( int index, bool inSequence ) => + { + var le = tokens[ index ]; + + if ( + + le.Is( LexerMatcherLibrary.ClassMatcher ) || + le.Is( LexerMatcherLibrary.StructMatcher ) || + le.Is( LexerMatcherLibrary.InterfaceMatcher ) || + le.Is( LexerMatcherLibrary.RecordMatcher ) || + le.Is( LexerMatcherLibrary.EnumMatcher ) + ) + { + return Trillean.True; + } + + if ( inSequence && le.Is( LexerMatcherLibrary.CwordMatcher ) ) + { + return Trillean.False; + } + + return Trillean.Any; + } + ); + + return sequences; + } + public CSharpLexer() { AddAllMatchers( @@ -37,6 +70,10 @@ namespace Rokojori LexerMatcherLibrary.BracketMatcher, LexerMatcherLibrary.AccessModifierMatcher, LexerMatcherLibrary.ClassMatcher, + LexerMatcherLibrary.EnumMatcher, + LexerMatcherLibrary.StructMatcher, + LexerMatcherLibrary.InterfaceMatcher, + LexerMatcherLibrary.RecordMatcher, LexerMatcherLibrary.OperatorMatcher, LexerMatcherLibrary.CFunctionMatcher, LexerMatcherLibrary.CwordMatcher, diff --git a/Runtime/Text/Lexing/LexerLibrary/GDShaderLexer.cs b/Runtime/Text/Lexing/LexerLibrary/GDShaderLexer.cs new file mode 100644 index 0000000..b74f13c --- /dev/null +++ b/Runtime/Text/Lexing/LexerLibrary/GDShaderLexer.cs @@ -0,0 +1,71 @@ +using System.Collections; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace Rokojori +{ + public class GDShaderLexer:Lexer + { + public static List Lex( string source ) + { + var lexer = new GDShaderLexer(); + var events = lexer.LexToList( source ); + + if ( lexer.hasError ) + { + return null; + } + + events.ForEach( ev => { ev.GrabMatch( source ); } ); + return events; + } + + public static readonly LexerMatcher RenderSettingsFlagMatcher = + new LexerMatcher( "RenderSettingsFlag", @"\b(?:shader_type|render_mode)\b" ); + + public static readonly LexerMatcher TypeMatcher = + new LexerMatcher( "ShaderType", @"\b(?:spatial|canvas_item|particles|sky|fog)\b" ); + + public static readonly LexerMatcher UsageMatcher = + new LexerMatcher( "Usage", @"\b(?:uniform|varying)\b" ); + + public static readonly LexerMatcher InOutMatcher = + new LexerMatcher( "InOut", @"\b(?:inout|in|out)\b" ); + // public static readonly LexerMatcher RenderModeMatcher = + // new LexerMatcher( "RenderMode", @"\b(?:spatial|canvas|particles)\b" ); + + public static LexerMatcher[] ignore = + [ + LexerMatcherLibrary.SingleLineCommentMatcher, + LexerMatcherLibrary.MultiLineCommentMatcher, + LexerMatcherLibrary.BreakMatcher, + LexerMatcherLibrary.WhiteSpaceMatcher + ]; + + public GDShaderLexer() + { + AddAllMatchers( + LexerMatcherLibrary.SingleLineCommentMatcher, + LexerMatcherLibrary.MultiLineCommentMatcher, + LexerMatcherLibrary.DoubleQuotedStringMatcher, + LexerMatcherLibrary.CInstructionMatcher, + RenderSettingsFlagMatcher, + TypeMatcher, + UsageMatcher, + InOutMatcher, + LexerMatcherLibrary.NumberMatcher, + LexerMatcherLibrary.BoolMatcher, + LexerMatcherLibrary.BreakMatcher, + LexerMatcherLibrary.WhiteSpaceMatcher, + LexerMatcherLibrary.LogicMatcher, + LexerMatcherLibrary.BracketMatcher, + LexerMatcherLibrary.AccessModifierMatcher, + LexerMatcherLibrary.StructMatcher, + LexerMatcherLibrary.OperatorMatcher, + LexerMatcherLibrary.CFunctionMatcher, + LexerMatcherLibrary.CwordMatcher, + LexerMatcherLibrary.AnySymbolMatcher + ); + } + } +} \ No newline at end of file diff --git a/Runtime/Text/Lexing/LexerLibrary/GDShaderLexer.cs.uid b/Runtime/Text/Lexing/LexerLibrary/GDShaderLexer.cs.uid new file mode 100644 index 0000000..d762362 --- /dev/null +++ b/Runtime/Text/Lexing/LexerLibrary/GDShaderLexer.cs.uid @@ -0,0 +1 @@ +uid://dpsvkg6t4s7js diff --git a/Runtime/Text/Lexing/LexerList.cs b/Runtime/Text/Lexing/LexerList.cs new file mode 100644 index 0000000..543645a --- /dev/null +++ b/Runtime/Text/Lexing/LexerList.cs @@ -0,0 +1,132 @@ +using System.Collections; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace Rokojori +{ + public class LexerList + { + protected List _events; + + public List events => _events; + + public LexerList Filter( System.Predicate le ) + { + return Create( events.Filter( e => le( e ) ) ); + } + + public LexerList GetAll( LexerMatcher lexerMatcher ) + { + return Create( events.Filter( e => e.Is( lexerMatcher ) ) ); + } + + public List Seperate( int start, int end, LexerMatcher[] ignore = null ) + { + return Seperate( ",", start, end, ignore ); + } + + public List Seperate( string seperator, int start, int end, LexerMatcher[] ignore = null ) + { + var list = new List(); + + var currentStart = -1; + + for ( int i = start; i <= end; i++ ) + { + if ( _events[ i ].MatchIs( seperator ) ) + { + if ( currentStart != -1 ) + { + list.Add( new RangeI( currentStart, i - 1 ) ); + } + + currentStart = -1; + } + else if ( ignore != null && _events[ i ].IsAnyOf( ignore ) ) + { + continue; + } + else if ( currentStart == -1 ) + { + currentStart = i; + } + + } + + if ( currentStart != -1 ) + { + list.Add( new RangeI( currentStart, end ) ); + } + + return list; + } + + public void ForEach( LexerMatcher lexerMatcher, System.Action action ) + { + events.ForEach( + ( e )=> + { + if ( ! e.Is( lexerMatcher ) ) + { + return; + } + + action( e ); + } + ); + } + + public LexerEvent.FindResult FindOpeningBracket( int offset, string blockBracket = "{" ) + { + return LexerEvent.FindOpeningBracket( _events, offset, blockBracket ); + } + + public LexerEvent.FindResult ReverseFindOpeningBracket( int offset ) + { + return LexerEvent.ReverseFindOpeningBracket( _events, offset ); + } + + public LexerEvent.FindResult ReverseFind( int offset, System.Func evaluator ) + { + return LexerEvent.Find( _events, offset, evaluator, false ); + } + + public LexerEvent.FindResult Find( int offset, System.Func evaluator ) + { + return LexerEvent.Find( _events, offset, evaluator, true ); + } + + public List> FindSequences( System.Func matcher ) + { + return LexerEvent.FindSequences( _events, matcher ); + } + + public List GetBlocks() + { + return LexerEvent.GetBlocks( _events ); + } + + public LexerList Range( RangeI range ) + { + return Range( range.min, range.max ); + } + + public LexerList Range( int start, int end = -1 ) + { + if ( end == -1 ) + { + end = _events.Count; + } + + return Create( _events.Sub( start, end - start + 1 ) ); + } + + public static LexerList Create( List lexerEvents ) + { + var list = new LexerList(); + list._events = lexerEvents; + + return list; + } + } +} \ No newline at end of file diff --git a/Runtime/Text/Lexing/LexerMatcherLibrary.cs b/Runtime/Text/Lexing/LexerMatcherLibrary.cs index f630511..67258e9 100644 --- a/Runtime/Text/Lexing/LexerMatcherLibrary.cs +++ b/Runtime/Text/Lexing/LexerMatcherLibrary.cs @@ -72,8 +72,20 @@ namespace Rokojori public static readonly LexerMatcher ClassMatcher = new LexerMatcher( "Class", @"\bclass\b" ); + public static readonly LexerMatcher EnumMatcher = + new LexerMatcher( "Enum", @"\benum\b" ); + + public static readonly LexerMatcher InterfaceMatcher = + new LexerMatcher( "Interface", @"\binterface\b" ); + + public static readonly LexerMatcher StructMatcher = + new LexerMatcher( "Struct", @"\bstruct\b" ); + + public static readonly LexerMatcher RecordMatcher = + new LexerMatcher( "Record", @"\brecord\b" ); + public static readonly LexerMatcher AccessModifierMatcher = - new LexerMatcher( "AccessModifier", @"\b(?:public|protected|private)\b" ); + new LexerMatcher( "AccessModifier", @"\b(?:public|protected|private|const)\b" ); public static readonly LexerMatcher SingleLineCommentMatcher = new LexerMatcher( "SingleLineComment", @"//.*" ); diff --git a/Runtime/Text/Text.cs b/Runtime/Text/Text.cs index cb567cf..aa3d984 100644 --- a/Runtime/Text/Text.cs +++ b/Runtime/Text/Text.cs @@ -1,6 +1,7 @@ using System.Collections; using System.Collections.Generic; +using System.Text.RegularExpressions; namespace Rokojori @@ -23,6 +24,44 @@ namespace Rokojori return path; } + + public static string Replace( this string source, Regex regex, string replacement ) + { + return regex.Replace( source, replacement ); + } + + public static string SubstringAfterMatching( this string source, string match ) + { + var index = source.IndexOf( match ); + + return index == -1 ? source : source.Substring( index + match.Length ); + } + + public static long ToLong( this string source ) + { + return long.Parse( source ); + } + + public static string ReplaceStart( this string source, string start, string replacement = "" ) + { + if ( source.StartsWith( start ) ) + { + return replacement + source.Substring( start.Length ); + } + + return source; + } + + public static string ReplaceEnd( this string source, string ending, string replacement = "" ) + { + if ( source.EndsWith( ending ) ) + { + return source.Substring( 0, source.Length - ending.Length ) + replacement; + } + + return source; + } + } } \ No newline at end of file diff --git a/Runtime/Time/DateTime/Date.cs b/Runtime/Time/DateTime/Date.cs new file mode 100644 index 0000000..0da8bf6 --- /dev/null +++ b/Runtime/Time/DateTime/Date.cs @@ -0,0 +1,29 @@ + +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System; +using Godot; + + +namespace Rokojori +{ + [GlobalClass] + [Tool] + public partial class Date:Resource + { + public int year = 1; + public int month = 1; + public int day = 1; + + public int hours = 0; + public int minutes = 0; + public int second = 0; + public float milliseconds = 0; + + public int utcHoursOffset = 0; + public int utcMinutesOffset = 0; + public float utcMillisecondsOffset = 0; + + } +} \ No newline at end of file diff --git a/Runtime/Time/DateTime/Date.cs.uid b/Runtime/Time/DateTime/Date.cs.uid new file mode 100644 index 0000000..eaba169 --- /dev/null +++ b/Runtime/Time/DateTime/Date.cs.uid @@ -0,0 +1 @@ +uid://cvl5k8x7rrybt diff --git a/Runtime/Time/DateTime/DateMath.cs b/Runtime/Time/DateTime/DateMath.cs index 2eab84c..fbebb85 100644 --- a/Runtime/Time/DateTime/DateMath.cs +++ b/Runtime/Time/DateTime/DateMath.cs @@ -10,16 +10,21 @@ namespace Rokojori { public static class DateMath { - - public static float GetDifference( this DateTime a, DateTime b ) + public static bool IsNewerThan( this DateTimeOffset a, DateTimeOffset b ) + { + var difference = GetDifference( a, b ); + return difference > 0; + } + + public static float GetDifference( this DateTimeOffset a, DateTimeOffset b ) { return (float) ( ( a - b ).TotalSeconds ); } - public static bool HasExpired( this DateTime oldTime, float duration ) + public static bool HasExpired( this DateTimeOffset oldTime, float duration ) { - var difference = GetDifference( DateTime.Now, oldTime ); + var difference = GetDifference( DateTimeOffset.Now, oldTime ); return difference >= duration; } diff --git a/Runtime/Tools/Lists.cs b/Runtime/Tools/Lists.cs index 3841a15..2899305 100644 --- a/Runtime/Tools/Lists.cs +++ b/Runtime/Tools/Lists.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Text; using System; using Godot; +using System.Threading.Tasks; namespace Rokojori { @@ -458,6 +459,11 @@ namespace Rokojori return GetLast( list ); } + public static T ReverseAt( this List list, int index ) + { + return list[ list.Count - 1 - index ]; + } + public static void RemoveIncreasingSortedIndices( List list, List increasinglySortedRemovals ) { for ( var i = increasinglySortedRemovals.Count - 1; i >= 0; i-- ) @@ -658,6 +664,27 @@ namespace Rokojori return mapped; } + public static async Task> FilterAsync( this List list, Func> filter ) + { + var outputList = new List(); + + var index = 0; + + foreach ( var it in list ) + { + var result = await filter( it ); + + if ( result ) + { + outputList.Add( it ); + } + + index++; + } + + return outputList; + } + public static void Filter( List inputList, List list, Func filter ) { for ( int i = 0; i < inputList.Count; i++ ) diff --git a/Runtime/Tools/Trillean.cs b/Runtime/Tools/Trillean.cs index 75cefa2..697ae5c 100644 --- a/Runtime/Tools/Trillean.cs +++ b/Runtime/Tools/Trillean.cs @@ -15,6 +15,11 @@ namespace Rokojori public static class TrilleanLogic { + public static Trillean FromBool( bool value ) + { + return value ? Trillean.True : Trillean.False; + } + public static bool ToBool( Trillean value, bool any = true ) { if ( Trillean.Any == value ) diff --git a/Tools/Git/Git.cs b/Tools/Git/Git.cs index 8600bb4..371be88 100644 --- a/Tools/Git/Git.cs +++ b/Tools/Git/Git.cs @@ -4,6 +4,7 @@ using Rokojori; using System.Diagnostics; using System.Collections.Generic; using System.Threading.Tasks; +using System; namespace Rokojori.Tools { @@ -15,6 +16,125 @@ namespace Rokojori.Tools public class Git { + public static async Task GetFileChangedTime( string path ) + { + var arguments = new List() + { + "log -1 --format=\"%ct\" -- " + path.EscapeAsPathForCommandLine() + }; + + var response = await Run( arguments, RegexUtility.ParentPath( path ) ); + + if ( response.exitCode != 0 ) + { + return null; + } + + try + { + var time = System.DateTimeOffset.FromUnixTimeSeconds( response.rawResponse.ToLong() ); + return time; + } + catch( System.Exception e ) + { + RJLog.Error( e ); + } + + return null; + } + + public static async Task IsFileNewerThan( string path, System.DateTimeOffset time ) + { + var _fileChangedTime = await GetFileChangedTime( path ); + + if ( _fileChangedTime == null ) + { + return false; + } + + var fileChangedTime = (DateTimeOffset) _fileChangedTime; + + + return fileChangedTime.IsNewerThan( time ); + + + } + + public static async Task Run( List arguments, string workingDirectory = null ) + { + var response = new GitResponse(); + + var joinedArgs = arguments.Join( " "); + + RJLog.Log( "GIT", joinedArgs ); + + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "git", + Arguments = joinedArgs, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + } + + }; + + if ( workingDirectory != null ) + { + process.StartInfo.WorkingDirectory = workingDirectory; + } + + process.Start(); + + var outputResult = new List(); + var errorResult = new List(); + + process.OutputDataReceived += (sender, e) => + { + if ( e.Data == null ) + { + return; + } + + RJLog.Log( e.Data ); + outputResult.Add( e.Data ); + }; + + process.ErrorDataReceived += (sender, e) => + { + if ( e.Data == null ) + { + return; + } + + RJLog.Error( e.Data ); + errorResult.Add( e.Data ); + }; + + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + + + await process.WaitForExitAsync(); + + + response.exitCode = process.ExitCode; + + if ( process.ExitCode == 0 ) + { + response.rawResponse = outputResult.Join( "" ); + } + else + { + response.rawResponse = errorResult.Join( "" ); + } + + return response; + } + public static async Task GetStatus( string path ) { var process = new Process diff --git a/Tools/Git/GitTest.cs b/Tools/Git/GitTest.cs index d63aa79..f3d8a63 100644 --- a/Tools/Git/GitTest.cs +++ b/Tools/Git/GitTest.cs @@ -4,6 +4,7 @@ using Rokojori; using System.Diagnostics; using System.Collections.Generic; using System.Threading.Tasks; +using System; namespace Rokojori.Tools { @@ -12,7 +13,7 @@ namespace Rokojori.Tools public partial class GitTest:Node { [ExportToolButton("Status")] - public Callable StatusButton => Callable.From( + public Callable statusButton => Callable.From( ()=> { GetStatus(); @@ -22,9 +23,39 @@ namespace Rokojori.Tools async void GetStatus() { var response = await Git.GetStatus( ProjectSettings.GlobalizePath( "res://") ); - this.LogInfo( response.exitCode, ">>", response.rawResponse ); + this.LogInfo( response.exitCode, ">>", response.rawResponse ); + } + + [ExportGroup( "File")] + [Export] + public string filePath; + + [ExportToolButton("Get Change Time")] + public Callable changeTimeButton => Callable.From( + ()=> + { + GetTime(); + } + ); + + async void GetTime() + { + var optionalTime = await Git.GetFileChangedTime( ProjectSettings.GlobalizePath( filePath ) ); + + if ( optionalTime != null ) + { + var time = (DateTimeOffset) optionalTime; + this.LogInfo( "LOCAL:", time.ToLocalTime(), " || UTC:", time,"\n>>", filePath ); + } + else + { + this.LogInfo( "Could not receive time info" ); + } + } + + } } #endif \ No newline at end of file diff --git a/Tools/blender/Blender.cs b/Tools/blender/Blender.cs index b6c5049..501fa34 100644 --- a/Tools/blender/Blender.cs +++ b/Tools/blender/Blender.cs @@ -79,12 +79,25 @@ namespace Rokojori.Tools process.OutputDataReceived += (sender, e) => { + + + if ( e.Data == null ) + { + return; + } + RJLog.Log( e.Data ); outputResult.Add( e.Data ); + }; process.ErrorDataReceived += (sender, e) => { + if ( e.Data == null ) + { + return; + } + RJLog.Error( e.Data ); errorResult.Add( e.Data ); }; diff --git a/Tools/docs/ClassDocGenerator.cs b/Tools/docs/ClassDocGenerator.cs index 7068a1f..d0fe29e 100644 --- a/Tools/docs/ClassDocGenerator.cs +++ b/Tools/docs/ClassDocGenerator.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System; using System.Reflection; using System.Text.RegularExpressions; +using System.Linq; namespace Rokojori.DocGenerator { @@ -40,23 +41,32 @@ namespace Rokojori.DocGenerator return "CSharp.svg"; } - public ClassDocInfo Create( string filePath, Type type, List icons ) + public ClassDocInfo Create( string filePath, Type classType, string definitionType, List icons ) { var data = filePath == null ? "" : FilesSync.LoadUTF8( filePath ); var comments = new DocComments(); comments.Grab( data ); - RJLog.Log( type.Name, "Comments:", comments.entries.Count ); + var typeName = GetDocsType.Of( classType ); + var baseTypeName = classType.BaseType; + var typeNameSpace = classType.Namespace; + + RJLog.Log( typeName, "Comments:", comments.entries.Count ); - info.name = type.Name; - info.csNameSpace = type.Namespace; - info.doc = comments.FindDoc( "class", type.Name ); + info.name = typeName; + info.csNameSpace = typeNameSpace; + info.definitionType = definitionType; + info.sourcePath = filePath.SubstringAfterMatching( "addons/rokojori_action_library/"); - info.generics = Lists.Map( type.GenericTypeArguments, t => t + "" ); + info.doc = comments.FindDoc( "class", typeName ); + + var genericArguments = classType.GetGenericArguments().ToList(); + info.generics = genericArguments.Map( t => t.Name ); + info.interfaces = classType.GetInterfaces().ToList().Map( i => GetDocsType.Of( i ) ); var allBindings = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly; - var fields = Lists.ToList( type.GetFields( allBindings ) ); + var fields = Lists.ToList( classType.GetFields( allBindings ) ); fields = Lists.Filter( fields, f => ! f.IsPrivate ); var godotTypes = new List() @@ -67,7 +77,7 @@ namespace Rokojori.DocGenerator - var it = type.BaseType; + var it = baseTypeName; while ( it != null ) { @@ -86,7 +96,7 @@ namespace Rokojori.DocGenerator } else { - info.extendingClasses.Add( it.Name ); + info.extendingClasses.Add( GetDocsType.Of( it ) ); it = it.BaseType; } @@ -95,14 +105,59 @@ namespace Rokojori.DocGenerator info.icon = GetIcon( data, info.name, info.extendingClasses, icons ); + var constructors = Lists.ToList( classType.GetConstructors( allBindings ) ); + constructors = Lists.Filter( constructors, m => ! m.IsPrivate ); + + constructors.ForEach( + ( con ) => + { + var memberInfo = MemberInfo.CreateConstructor(); + + memberInfo.name = typeName; + memberInfo.dataType = typeName; + + memberInfo.csNameSpace = typeNameSpace; + // memberInfo.generics = info.generics; + // memberInfo.doc = comments.FindDoc( "constructor", con.Name ); + + memberInfo.modifiers.Add( con.IsPublic ? "public" : "protected" ); + + var parameters = Lists.ToList( con.GetParameters() ); + parameters.ForEach( + ( p )=> + { + var parameterType = new ParameterType(); + parameterType.name = p.Name; + parameterType.type = GetDocsType.Of( p.ParameterType ); + parameterType.csNameSpace = p.ParameterType.Namespace; + parameterType.generics = Lists.Map( p.ParameterType.GetGenericArguments(), t => t + "" ); + + memberInfo.parameters.Add( parameterType ); + + } + ); + + info.memberInfos.Add( memberInfo ); + } + + + ); + + fields.ForEach( ( f )=> { + if ( f.Name == "value__" ) + { + return; + } var memberInfo = MemberInfo.CreateField(); memberInfo.name = f.Name; + + memberInfo.csNameSpace = f.FieldType.Namespace; - memberInfo.dataType = f.FieldType.Name; + memberInfo.dataType = GetDocsType.Of( f.FieldType ); memberInfo.generics = Lists.Map( f.FieldType.GetGenericArguments(), t => t + "" ); memberInfo.doc = comments.FindDoc( "field", f.Name ); @@ -119,7 +174,7 @@ namespace Rokojori.DocGenerator ); - var methods = Lists.ToList( type.GetMethods( allBindings ) ); + var methods = Lists.ToList( classType.GetMethods( allBindings ) ); methods = Lists.Filter( methods, m => ! m.IsPrivate ); var godotInternalMethods = new List() @@ -132,7 +187,8 @@ namespace Rokojori.DocGenerator "GetGodotPropertyList", "GetGodotPropertyDefaultValues", "SaveGodotObjectData", - "RestoreGodotObjectData" + "RestoreGodotObjectData", + "InvokeGodotClassStaticMethod" }; methods.ForEach( @@ -144,9 +200,11 @@ namespace Rokojori.DocGenerator } var memberInfo = MemberInfo.CreateMethod(); - + + memberInfo.isAccessor = GetDocsType.isAccessorMethod( m ); memberInfo.name = m.Name; - memberInfo.dataType = m.ReturnType.Name; + memberInfo.dataType = GetDocsType.Of( m.ReturnType ); + memberInfo.csNameSpace = m.ReturnType.Namespace; memberInfo.generics = Lists.Map( m.GetGenericArguments(), t => t + "" ); memberInfo.doc = comments.FindDoc( "method", m.Name ); @@ -164,9 +222,9 @@ namespace Rokojori.DocGenerator { var parameterType = new ParameterType(); parameterType.name = p.Name; - parameterType.type = p.ParameterType.Name; + parameterType.type = GetDocsType.Of( p.ParameterType ); parameterType.csNameSpace = p.ParameterType.Namespace; - parameterType.generics = Lists.Map( p.ParameterType.GenericTypeArguments, t => t + "" ); + parameterType.generics = Lists.Map( p.ParameterType.GetGenericArguments(), t => t + "" ); memberInfo.parameters.Add( parameterType ); diff --git a/Tools/docs/ClassDocGenerator.cs.uid b/Tools/docs/ClassDocGenerator.cs.uid new file mode 100644 index 0000000..1d78107 --- /dev/null +++ b/Tools/docs/ClassDocGenerator.cs.uid @@ -0,0 +1 @@ +uid://ddfpf66np8qpk diff --git a/Tools/docs/ClassDocInfo.cs b/Tools/docs/ClassDocInfo.cs index d1e12bc..f5fcb04 100644 --- a/Tools/docs/ClassDocInfo.cs +++ b/Tools/docs/ClassDocInfo.cs @@ -9,7 +9,8 @@ namespace Rokojori.DocGenerator public string name; public string type; public string csNameSpace; - public List generics; + public List modifiers = []; + public List generics = []; } public class MemberInfo @@ -19,6 +20,11 @@ namespace Rokojori.DocGenerator public string name; public string dataType; public string csNameSpace; + public string path; + public bool isAccessor; + public List attributes = new List(); + public int lineIndex; + public int lineOffset; public List modifiers = new List(); public List generics; @@ -26,6 +32,9 @@ namespace Rokojori.DocGenerator public static readonly string Field = "Field"; public static readonly string Method = "Method"; + public static readonly string Constructor = "Constructor"; + public static readonly string Uniform = "Uniform"; + public static readonly string Varying = "Varying"; public static MemberInfo Create( string memberType ) { @@ -43,6 +52,21 @@ namespace Rokojori.DocGenerator { return MemberInfo.Create( MemberInfo.Method ); } + + public static MemberInfo CreateConstructor() + { + return MemberInfo.Create( MemberInfo.Constructor ); + } + + public static MemberInfo CreateUniform() + { + return MemberInfo.Create( MemberInfo.Uniform ); + } + + public static MemberInfo CreateVarying() + { + return MemberInfo.Create( MemberInfo.Varying ); + } } public class ClassDocInfo @@ -51,8 +75,14 @@ namespace Rokojori.DocGenerator public string name =""; public string doc; public string icon; + public string sourcePath; + public string definitionType; + public List generics = new List(); + public List interfaces = new List(); public List extendingClasses = new List(); public List memberInfos = new List(); + + } } \ No newline at end of file diff --git a/Tools/docs/ClassDocInfo.cs.uid b/Tools/docs/ClassDocInfo.cs.uid new file mode 100644 index 0000000..5e8273d --- /dev/null +++ b/Tools/docs/ClassDocInfo.cs.uid @@ -0,0 +1 @@ +uid://cogfo5xxdtqo5 diff --git a/Tools/docs/CreateDoc.cs b/Tools/docs/CreateDoc.cs index 387433e..d8f82f4 100644 --- a/Tools/docs/CreateDoc.cs +++ b/Tools/docs/CreateDoc.cs @@ -2,6 +2,7 @@ using Godot; using System.Collections.Generic; +using System; namespace Rokojori.DocGenerator { @@ -29,6 +30,21 @@ namespace Rokojori.DocGenerator [Export] public string outputPath ="res://addons/rokojori_action_library/Tools/docs/output"; + [Export] + public bool clearDirectory = false; + + [Export] + public bool c_sharp = true; + + [Export] + public bool shaders = true; + + [Export] + public bool processOnlyNewerThanCheckTime = true; + + [Export] + public float hoursCheckTime; + [ExportToolButton( "Create")] public Callable createButton => Callable.From( ()=>{ Generate(); } ); @@ -47,11 +63,21 @@ namespace Rokojori.DocGenerator void Generate() { + var absoluteLibraryPath = ProjectSettings.GlobalizePath( "res://addons/rokojori_action_library/Runtime" ); var absoluteIconPath = ProjectSettings.GlobalizePath( "res://addons/rokojori_action_library/Icons" ); var absoluteOutputPath = ProjectSettings.GlobalizePath( outputPath ); + if ( clearDirectory ) + { + FilesSync.Delete( absoluteOutputPath ); + FilesSync.EnsureDirectoryExists( absoluteOutputPath ); + } + var generator = new DocGenerator(); + generator.shaders = shaders; + generator.cSharp = c_sharp; + var icons = Lists.Map( FilesSync.GetFiles( absoluteIconPath, ( fp => fp.fileExtension == ".svg" ) ), fp => fp.fullFileName ); generator.Generate( absoluteLibraryPath, absoluteOutputPath, icons ); } diff --git a/Tools/docs/DocComments.cs b/Tools/docs/DocComments.cs index c83af7c..3dc69e7 100644 --- a/Tools/docs/DocComments.cs +++ b/Tools/docs/DocComments.cs @@ -78,7 +78,7 @@ namespace Rokojori.DocGenerator public string FindDoc( string type, string name ) { - var e= entries.Find( e => e.type == type && e.name == name ); + var e = entries.Find( e => e.type == type && e.name == name ); return e == null ? null : e.doc; } diff --git a/Tools/docs/DocComments.cs.uid b/Tools/docs/DocComments.cs.uid new file mode 100644 index 0000000..cdd5ad2 --- /dev/null +++ b/Tools/docs/DocComments.cs.uid @@ -0,0 +1 @@ +uid://cwvi31krqm3ik diff --git a/Tools/docs/DocGenerator.cs b/Tools/docs/DocGenerator.cs index 9bf6b0c..0be333b 100644 --- a/Tools/docs/DocGenerator.cs +++ b/Tools/docs/DocGenerator.cs @@ -4,40 +4,81 @@ using Godot; using Rokojori; using System.Collections.Generic; using System; +using Microsoft.VisualBasic; +using Rokojori.Tools; +using System.Threading.Tasks; namespace Rokojori.DocGenerator { public class ClassTypeEntry { public Type type; + public string definitionType; public string path; } + public class ShaderTypeEntry + { + public string path; + } + + public class DocGenerator { string path = ""; string outputPath = ""; List icons = new List(); + + public bool checkChangeTime = false; + public DateTimeOffset changeTime; public List classFiles = new List(); public List classTypes = new List(); + public List shaderTypes = new List(); + + public bool shaders = true; + public bool cSharp = true; - public void Generate( string path, string outputPath, List icons ) + public async void Generate( string path, string outputPath, List icons ) { this.path = path; this.outputPath = outputPath; this.icons = icons; - GetFiles(); + await GetFiles(); CreateTypesFromFiles(); - IncludeDefaultTypes(); + // IncludeDefaultTypes(); GenerateDocsFromTypes(); } - void GetFiles() + async Task GetFiles() { - classFiles = FilesSync.GetFiles( path, f => f.fileExtension == ".cs", true ); + classFiles = FilesSync.GetFiles( path, + f => + { + var isCS = f.fileExtension == ".cs"; + var isShader = f.fileExtension == ".gdshader" || f.fileExtension == ".gdshaderinc"; + + if ( isShader ) + { + RJLog.Log( "SHADER: FILE", f.absolutePath ); + } + + return ( cSharp && isCS ) || ( shaders && isShader ); + }, + true + ); + + if ( checkChangeTime ) + { + classFiles = await classFiles.FilterAsync( + async ( f ) => + { + return await Git.IsFileNewerThan( f.absolutePath, changeTime ); + } + ); + } } void IncludeDefaultTypes() @@ -67,45 +108,111 @@ namespace Rokojori.DocGenerator void CreateTypesFromFiles() { var genericType = new EventSlot().GetType(); - var namespaceLabel = "Rokojori"; + var namespaceLabel = "Rokojori"; var assembly = genericType.Assembly; classFiles.ForEach( ( cf )=> { var name = cf.fileName; - var type = ReflectionHelper.GetTypeByName( namespaceLabel + "." + name ); - if ( type == null ) + if ( cf.fileExtension == ".cs" ) { - type = ReflectionHelper.GetTypeByNameFromAssembly( name, assembly ); - } + var fileContent = FilesSync.LoadUTF8( cf.fullPath ); + var definitions = CSharpLexer.GetAllObjectDefinitions( fileContent ); - if ( type != null ) - { - var entry = new ClassTypeEntry(); - entry.type = type; - entry.path = cf.fullPath; + RJLog.Log( cf.fileName, ">>", definitions.Join( "," ) ); - classTypes.Add( entry); + for ( int i = 0; i < definitions.Count; i++ ) + { + var sequence = definitions[ i ]; + var definitionType = sequence[ 0 ]; + var definitionName = sequence[ 1 ]; + + RJLog.Log( "Adding definition:", definitionName.match ); + AddCSharpsClassType( cf, definitionName.match, definitionType.match, namespaceLabel, assembly ); + + } } + else if ( cf.fileExtension == ".gdshader" || cf.fileExtension == ".gdshaderinc" ) + { + var shaderType = new ShaderTypeEntry(); + shaderType.path = cf.absolutePath; + shaderTypes.Add( shaderType ); + + RJLog.Log( "SHADER: Adding definition:", cf.absolutePath ); + } + } ); + } + + void AddCSharpsClassType( FilePath cf, string name, string definitionType, string namespaceLabel, System.Reflection.Assembly assembly ) + { + // var name = cf.fileName; + var type = ReflectionHelper.GetTypeByName( namespaceLabel + "." + name ); + + if ( type == null ) + { + type = ReflectionHelper.GetTypeByNameFromAssembly( name, assembly ); + } + + if ( type == null ) + { + return; + } + + var entry = new ClassTypeEntry(); + entry.type = type; + entry.path = cf.fullPath; + entry.definitionType = definitionType; + + classTypes.Add( entry); + } void GenerateDocsFromTypes() { + FilesSync.EnsureDirectoryExists( outputPath ); + classTypes.ForEach( ( c )=> { var cdg = new ClassDocGenerator(); - var cinfo = cdg.Create( c.path, c.type, icons ); + var cinfo = cdg.Create( c.path, c.type, c.definitionType, icons ); + var outputPathName = cinfo.name.Replace( "`", "-" ).Replace( "<", "-" ).Replace( ">", "-" ); - var outputPath = FilePath.Join( this.outputPath, cinfo.name + ".json" ); + var outputPath = FilePath.Join( this.outputPath, outputPathName + ".json" ); FilesSync.SaveJSON( outputPath, cinfo ); } ); + + RJLog.Log( "SHADER: shaderTypes", shaderTypes.Count ); + + shaderTypes.ForEach( + ( s ) => + { + try + { + RJLog.Log( "SHADER: DOC", s.path ); + var sdg = new ShaderDocGenerator(); + var cinfo = sdg.Create( s.path ); + var prefix = cinfo.definitionType + "." ; + var outputPathName = prefix + cinfo.name; + + var outputPath = FilePath.Join( this.outputPath, outputPathName + ".json" ); + RJLog.Log( "SHADER: SAVING", s.path, ">>", outputPathName ); + FilesSync.SaveJSON( outputPath, cinfo ); + } + catch ( System.Exception e ) + { + RJLog.Error( s.path ); + RJLog.Error( e ); + } + + } + ); } } } \ No newline at end of file diff --git a/Tools/docs/DocGenerator.cs.uid b/Tools/docs/DocGenerator.cs.uid new file mode 100644 index 0000000..eeb1dd8 --- /dev/null +++ b/Tools/docs/DocGenerator.cs.uid @@ -0,0 +1 @@ +uid://cgosf8d75v54v diff --git a/Tools/docs/GetDocsType.cs b/Tools/docs/GetDocsType.cs new file mode 100644 index 0000000..f0843df --- /dev/null +++ b/Tools/docs/GetDocsType.cs @@ -0,0 +1,57 @@ + +using Godot; + +using Rokojori; +using System.Collections.Generic; +using System; +using System.Text.RegularExpressions; +using System.Linq; +namespace Rokojori.DocGenerator +{ + public static class GetDocsType + { + public static bool isAccessorMethod( System.Reflection.MethodInfo m ) + { + var isSetAccessor = m.DeclaringType.GetProperties().Any( prop => prop.GetSetMethod() == m ); + + if ( isSetAccessor ) + { + return true; + } + + var isGetAccessor = m.DeclaringType.GetProperties().Any( prop => prop.GetGetMethod() == m ); + + return isGetAccessor; + } + + public static Dictionary replacements = new Dictionary() + { + { "Int32", "int" }, + { "Single", "float" }, + { "Boolean", "bool" } + }; + public static string Of( Type type ) + { + var docType = type.Name; + + if ( replacements.ContainsKey( docType ) ) + { + docType = replacements[ docType ]; + } + + if ( docType.Contains( "Action" ) && type.Namespace.Contains( "System" ) ) + { + docType = docType.Replace( "Action", "System.Action" ); + } + + if ( docType.Contains( "`" ) ) + { + var generics = Lists.Map( type.GetGenericArguments(), t => Of( t ) ); + var replacement = "<" + generics.Join( "," ) + ">"; + docType = docType.Replace( new Regex( "`.*$" ), replacement ); + } + + return docType; + } + } +} \ No newline at end of file diff --git a/Tools/docs/GetDocsType.cs.uid b/Tools/docs/GetDocsType.cs.uid new file mode 100644 index 0000000..74ddfdc --- /dev/null +++ b/Tools/docs/GetDocsType.cs.uid @@ -0,0 +1 @@ +uid://dt5vmpng5n2qb diff --git a/Tools/docs/ShaderDocGenerator.cs b/Tools/docs/ShaderDocGenerator.cs new file mode 100644 index 0000000..74ec78c --- /dev/null +++ b/Tools/docs/ShaderDocGenerator.cs @@ -0,0 +1,262 @@ + +using Godot; + +using Rokojori; +using System.Collections.Generic; +using System; +using System.Reflection; +using System.Text.RegularExpressions; +using System.Linq; + +namespace Rokojori.DocGenerator +{ + public class ShaderDocGenerator + { + public static readonly string GDShaderExtension = ".gdshader"; + public static readonly string GDShaderIncludeExtension = ".gdshaderinc"; + + ClassDocInfo info = new ClassDocInfo(); + + List _lexerEvents; + LexerList tokens; + + public ClassDocInfo Create( string filePath ) + { + var fileContent = FilesSync.LoadUTF8( filePath ); + _lexerEvents = GDShaderLexer.Lex( fileContent ); + tokens = LexerList.Create( _lexerEvents ); + + if ( filePath.EndsWith( GDShaderIncludeExtension ) ) + { + info.definitionType = "GDShaderInclude"; + } + else + { + info.definitionType = "GDShader"; + } + + info.name = RegexUtility.TrimToLastPathFragment( filePath ).ReplaceEnd( GDShaderIncludeExtension, "" ).ReplaceEnd( GDShaderExtension, "" ); + + GetIncludes(); + GetUniforms(); + GetBlocks(); + + + return info; + } + + void GetIncludes() + { + var includePrexix = "#include "; + + RJLog.Log( "Processing includes:", tokens.GetAll( LexerMatcherLibrary.CInstructionMatcher ).events.Count ); + + tokens.ForEach( LexerMatcherLibrary.CInstructionMatcher, + ( t )=> + { + + var match = t.match; + + RJLog.Log( "Processing include:", match ); + + if ( ! match.StartsWith( includePrexix ) ) + { + RJLog.Log( "Processing include:", match ); + return; + } + + + var path = match.ReplaceStart( includePrexix, "" ); + path = path.Substring( 1, path.Length - 2 ); + var name = RegexUtility.TrimToLastPathFragment( path ).TrimSuffix( ".gdshaderinc"); + + + var memberInfo = MemberInfo.Create( "ShaderInclude" ); + memberInfo.name = name; + memberInfo.path = path; + memberInfo.dataType = "GDShaderInclude"; + + info.memberInfos.Add( memberInfo ); + } + ); + } + + void GetUniforms() + { + var sequences = tokens.FindSequences( + ( int tokenIndex, bool inSequence ) => + { + var token = tokens.events[ tokenIndex ]; + + if ( ! inSequence ) + { + if ( token.Is( GDShaderLexer.UsageMatcher ) ) + { + return Trillean.True; + } + + return Trillean.Any; + } + else + { + return TrilleanLogic.FromBool( ! token.Is( LexerMatcherLibrary.OperatorMatcher, ";" ) ); + } + + } + ); + + // RJLog.Log( "Uniforms/Varyings:", sequences.Count ); + + sequences.ForEach( + ( s )=> + { + if ( s == null ) + { + return; + } + + + var filtered = s.Filter( s => ! s.IsAnyOf( GDShaderLexer.ignore ) ); + + // RJLog.Log( "'" + filtered.Map( s => s.match ).Join( "', '") + "'" ); + + if ( filtered.Count < 3 ) + { + return; + } + + + var first = filtered[ 0 ]; + var isUniform = first.MatchIs( "uniform" ); + var memberInfo = isUniform ? MemberInfo.CreateUniform() : MemberInfo.CreateVarying(); + + memberInfo.dataType = filtered[ 1 ].match; + memberInfo.name = filtered[ 2 ].match; + + // LINE INFO + + info.memberInfos.Add( memberInfo ); + } + ); + } + + void GetBlocks() + { + var blocks = tokens.GetBlocks(); + + if ( blocks == null ) + { + return; + } + + // RJLog.Log( "Num blocks:", blocks.Count ); + blocks.ForEach( b => ProcessBlock( b ) ); + } + + void ProcessBlock( RangeI blockRange ) + { + var blockStart = blockRange.min; + + var firstTokenBeforeResult = tokens.ReverseFind( blockStart - 1, + ( t ) => + { + // RJLog.Log( "Token type:", t ); + + if ( t.IsAnyOf( GDShaderLexer.ignore ) ) + { + return LexerEvent.FindResultType.KeepSearching; + } + + if ( ! + ( + t.Is( LexerMatcherLibrary.BracketMatcher, ")" ) || + t.Is( LexerMatcherLibrary.CwordMatcher ) + ) + ) + { + // RJLog.Log( "Invalid token type:", t ); + return LexerEvent.FindResultType.Error; + } + + return LexerEvent.FindResultType.Found; + } + ); + + if ( ! firstTokenBeforeResult.found ) + { + // RJLog.Log( "No first token before block found!" ); + return; + + } + + var firstToken = _lexerEvents[ firstTokenBeforeResult.index ]; + var isMethod = firstToken.Is( LexerMatcherLibrary.BracketMatcher, ")" ); + + + + if ( isMethod ) + { + var closeBracketResult = firstTokenBeforeResult; + var openBracketResult = tokens.ReverseFindOpeningBracket( closeBracketResult.index ); + + if ( ! openBracketResult.found ) + { + return; + } + + var nameToken = tokens.ReverseFind( openBracketResult.index, + ( t ) => + { + return t.Is( LexerMatcherLibrary.CFunctionMatcher ) ? LexerEvent.FindResultType.Found : LexerEvent.FindResultType.KeepSearching; + } + ); + + if ( ! nameToken.found ) + { + return; + } + + var typeToken = tokens.ReverseFind( nameToken.index - 1, + ( t ) => + { + return t.Is( LexerMatcherLibrary.CwordMatcher ) ? LexerEvent.FindResultType.Found : LexerEvent.FindResultType.KeepSearching; + } + ); + + var memberInfo = MemberInfo.CreateMethod(); + memberInfo.name = tokens.events[ nameToken.index ].match; + memberInfo.dataType = tokens.events[ typeToken.index ].match; + + var parameters = tokens.Seperate( openBracketResult.index + 1, closeBracketResult.index - 1, GDShaderLexer.ignore ); + + parameters.ForEach( + ( p )=> + { + var pValues = tokens.Range( p ).Filter( t => ! t.IsAnyOf( GDShaderLexer.ignore ) ); + + var parameterType = new ParameterType(); + parameterType.name = pValues.events.ReverseAt( 0 ).match; + parameterType.type = pValues.events.ReverseAt( 1 ).match; + + if ( pValues.events.Count > 2 ) + { + parameterType.modifiers.Add( pValues.events.ReverseAt( 2 ).match ); + } + + memberInfo.parameters.Add( parameterType ); + + } + ); + + info.memberInfos.Add( memberInfo ); + + // RJLog.Log( "Method found!", memberInfo.memberType, memberInfo.name ); + + } + else + { + // RJLog.Log( "No method found!", firstToken ); + } + } + } +} \ No newline at end of file diff --git a/Tools/docs/ShaderDocGenerator.cs.uid b/Tools/docs/ShaderDocGenerator.cs.uid new file mode 100644 index 0000000..3df73c9 --- /dev/null +++ b/Tools/docs/ShaderDocGenerator.cs.uid @@ -0,0 +1 @@ +uid://btne55wgtelrw