using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; namespace RobotNet.WebApp.Scripts.Monaco; public static class MonacoExtensions { public static bool IsInStaticContext(this SyntaxNode node) { // this/base calls are always static. if (node.FirstAncestorOrSelf() != null) { return true; } var memberDeclaration = node.FirstAncestorOrSelf(); if (memberDeclaration == null) { return false; } switch (memberDeclaration.Kind()) { case SyntaxKind.MethodDeclaration: case SyntaxKind.ConstructorDeclaration: case SyntaxKind.EventDeclaration: case SyntaxKind.IndexerDeclaration: return GetModifiers(memberDeclaration).Any(SyntaxKind.StaticKeyword); case SyntaxKind.PropertyDeclaration: return GetModifiers(memberDeclaration).Any(SyntaxKind.StaticKeyword) || node.IsFoundUnder((PropertyDeclarationSyntax p) => p.Initializer); case SyntaxKind.FieldDeclaration: case SyntaxKind.EventFieldDeclaration: // Inside a field one can only access static members of a type (unless it's top-level). return !memberDeclaration.Parent.IsKind(SyntaxKind.CompilationUnit); case SyntaxKind.DestructorDeclaration: return false; } // Global statements are not a static context. if (node.FirstAncestorOrSelf() != null) { return false; } // any other location is considered static return true; } public static SyntaxTokenList GetModifiers(SyntaxNode member) { if (member != null) { switch (member.Kind()) { case SyntaxKind.EnumDeclaration: return ((EnumDeclarationSyntax)member).Modifiers; case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.StructDeclaration: return ((TypeDeclarationSyntax)member).Modifiers; case SyntaxKind.DelegateDeclaration: return ((DelegateDeclarationSyntax)member).Modifiers; case SyntaxKind.FieldDeclaration: return ((FieldDeclarationSyntax)member).Modifiers; case SyntaxKind.EventFieldDeclaration: return ((EventFieldDeclarationSyntax)member).Modifiers; case SyntaxKind.ConstructorDeclaration: return ((ConstructorDeclarationSyntax)member).Modifiers; case SyntaxKind.DestructorDeclaration: return ((DestructorDeclarationSyntax)member).Modifiers; case SyntaxKind.PropertyDeclaration: return ((PropertyDeclarationSyntax)member).Modifiers; case SyntaxKind.EventDeclaration: return ((EventDeclarationSyntax)member).Modifiers; case SyntaxKind.IndexerDeclaration: return ((IndexerDeclarationSyntax)member).Modifiers; case SyntaxKind.OperatorDeclaration: return ((OperatorDeclarationSyntax)member).Modifiers; case SyntaxKind.ConversionOperatorDeclaration: return ((ConversionOperatorDeclarationSyntax)member).Modifiers; case SyntaxKind.MethodDeclaration: return ((MethodDeclarationSyntax)member).Modifiers; case SyntaxKind.GetAccessorDeclaration: case SyntaxKind.SetAccessorDeclaration: case SyntaxKind.AddAccessorDeclaration: case SyntaxKind.RemoveAccessorDeclaration: return ((AccessorDeclarationSyntax)member).Modifiers; } } return default; } public static bool IsFoundUnder(this SyntaxNode node, Func childGetter) where TParent : SyntaxNode { var ancestor = node.GetAncestor(); if (ancestor == null) { return false; } var child = childGetter(ancestor); // See if node passes through child on the way up to ancestor. return node.GetAncestorsOrThis().Contains(child); } public static TNode? GetAncestor(this SyntaxNode node) where TNode : SyntaxNode { var current = node.Parent; while (current != null) { if (current is TNode tNode) { return tNode; } current = current.GetParent(); } return null; } public static IEnumerable GetAncestorsOrThis(this SyntaxNode node) where TNode : SyntaxNode { var current = node; while (current != null) { if (current is TNode tNode) { yield return tNode; } current = current.GetParent(); } } private static SyntaxNode? GetParent(this SyntaxNode node) { return node is IStructuredTriviaSyntax trivia ? trivia.ParentTrivia.Token.Parent : node.Parent; } }