Skip to content

Commit

Permalink
Merge pull request #1982 from JetBrains/net211-yarkov-support-find-us…
Browse files Browse the repository at this point in the history
…ages-and-completion-for-animator

Support find usages and completion for animator
  • Loading branch information
MToolmaker committed Jan 14, 2021
2 parents d3e48de + bea88ac commit 892a111
Show file tree
Hide file tree
Showing 137 changed files with 28,968 additions and 154 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,13 @@
The same shortcut is defined for another menu item
</Description>
</Tag>

<Tag externalName="UnknownAnimatorStateNameWarning.HIGHLIGHTING_ID" default="WARNING">
<Title>There is no animator state with the same name in the project.</Title>
<Description>
There is no animator state with the same name in the project.
</Description>
</Tag>
</Group>
</SeverityConfiguration>

Expand All @@ -289,6 +296,13 @@
to use ERROR mode)
-->

<Warning name="UnknownAnimatorStateName" configurableSeverity="Unity.UnknownAnimatorStateName">
<Parameter type="IArgument" name="argument" />
<Message value="There is no animator state with the same name in the project." />
<Range>Argument.GetDocumentRange()</Range>
<Behavour overlapResolvePolicy="WARNING" />
</Warning>

<Warning name="UnknownInputAxes" configurableSeverity="Unity.UnknownInputAxes">
<Parameter type="ITreeNode" name="node" />
<Message value="The name is not defined in the Input manager. The call is likely to fail at runtime." />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System.Linq;
using JetBrains.Annotations;
using JetBrains.DataFlow;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Plugins.Unity.CSharp.Daemon.Errors;
using JetBrains.ReSharper.Plugins.Unity.Yaml;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.DeferredCaches.AnimatorUsages;
using JetBrains.ReSharper.Plugins.Yaml.Settings;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.Tree;

namespace JetBrains.ReSharper.Plugins.Unity.CSharp.Daemon.Stages.Analysis
{
[ElementProblemAnalyzer(typeof(IInvocationExpression),
HighlightingTypes = new[] {typeof(UnknownAnimatorStateNameWarning)})]
public class PlayAnimatorStateAnalyzer : UnityElementProblemAnalyzer<IInvocationExpression>
{
[NotNull] private readonly AssetSerializationMode myAssetSerializationMode;
[NotNull] private readonly YamlSupport myUnityYamlSupport;

public PlayAnimatorStateAnalyzer([NotNull] UnityApi unityApi,
[NotNull] AssetSerializationMode assetSerializationMode,
[NotNull] YamlSupport unityYamlSupport)
: base(unityApi)
{
myAssetSerializationMode = assetSerializationMode;
myUnityYamlSupport = unityYamlSupport;
}

protected override void Analyze([NotNull] IInvocationExpression invocation,
ElementProblemAnalyzerData data,
[NotNull] IHighlightingConsumer consumer)
{
if (!myAssetSerializationMode.IsForceText || !myUnityYamlSupport.IsParsingEnabled.Value) return;
var argument = GetStateNameArgumentFrom(invocation);
if (!(argument?.Value is ICSharpLiteralExpression literal) ||
!invocation.InvocationExpressionReference.IsAnimatorPlayMethod()) return;
var container = invocation.GetSolution().TryGetComponent<AnimatorScriptUsagesElementContainer>();
if (container == null ||
!(literal.ConstantValue.Value is string stateName) ||
container.ContainsStateName(stateName)) return;
consumer.AddHighlighting(new UnknownAnimatorStateNameWarning(argument));
}

[CanBeNull]
private static ICSharpArgument GetStateNameArgumentFrom([NotNull] IInvocationExpression invocationExpression)
{
return invocationExpression
.ArgumentList?
.Arguments
.FirstOrDefault(t =>
t != null &&
(t.IsNamedArgument && t.ArgumentName?.Equals("stateName") == true || !t.IsNamedArgument));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System.Collections.Generic;
using JetBrains.Annotations;
using JetBrains.Application.UI.Controls.BulbMenu.Items;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Feature.Services.Daemon;
using JetBrains.ReSharper.Plugins.Unity.CSharp.Daemon.Errors;
using JetBrains.ReSharper.Plugins.Unity.CSharp.Daemon.Stages.ContextSystem;
using JetBrains.ReSharper.Plugins.Unity.CSharp.Daemon.Stages.PerformanceCriticalCodeAnalysis.ContextSystem;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.DeferredCaches.AnimationEventsUsages;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.DeferredCaches.UnityEvents;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.CSharp.Tree;
Expand All @@ -18,33 +20,91 @@ namespace JetBrains.ReSharper.Plugins.Unity.CSharp.Daemon.Stages.Highlightings.I
public class EventHandlerDetector : UnityDeclarationHighlightingProviderBase
{
protected readonly UnityEventsElementContainer UnityEventsElementContainer;

private readonly AnimationEventUsagesContainer myAnimationEventUsagesContainer;

public EventHandlerDetector(ISolution solution, IApplicationWideContextBoundSettingStore settingsStore,
UnityEventsElementContainer unityEventsElementContainer,
PerformanceCriticalContextProvider contextProvider)
PerformanceCriticalContextProvider contextProvider,
[NotNull] AnimationEventUsagesContainer animationEventUsagesContainer)
: base(solution, settingsStore, contextProvider)
{
UnityEventsElementContainer = unityEventsElementContainer;
myAnimationEventUsagesContainer = animationEventUsagesContainer;
}

public override bool AddDeclarationHighlighting(IDeclaration treeNode, IHighlightingConsumer consumer,
IReadOnlyCallGraphContext context)
{
var declaredElement = treeNode.DeclaredElement;
var method = declaredElement as IMethod;
if (method is IAccessor)
return false;
if (method is IAccessor)
return TryAddAnimationEventHighlightingForAccessorMethod(treeNode, consumer, context, method);

if (declaredElement is IProperty property)
{
TryAddAnimationEventHighlightingForPropertyGetter(treeNode, consumer, context, property);
method = property.Setter;
}

return method != null && TryAddMethodHighlighting(treeNode, consumer, context, method);
}

if (method != null && UnityEventsElementContainer.GetAssetUsagesCount(method, out _) > 0)
private bool TryAddAnimationEventHighlightingForAccessorMethod(ITreeNode treeNode,
IHighlightingConsumer consumer,
IReadOnlyCallGraphContext context,
IDeclaredElement method)
{
var isAnimationEvent = myAnimationEventUsagesContainer.GetEventUsagesCountFor(method, out _) > 0;
if (isAnimationEvent) AddAnimationEventHighlighting(treeNode, consumer, context);
return isAnimationEvent;
}

private void TryAddAnimationEventHighlightingForPropertyGetter(ITreeNode treeNode,
IHighlightingConsumer consumer,
IReadOnlyCallGraphContext context,
IProperty property)
{
var getter = property.Getter;
if (getter != null && myAnimationEventUsagesContainer.GetEventUsagesCountFor(getter, out _) > 0)
{
AddHighlighting(consumer, treeNode as ICSharpDeclaration, "Event handler", "Unity event handler", context);
return true;
AddAnimationEventHighlighting(treeNode, consumer, context);
}
}

return false;
private bool TryAddMethodHighlighting(IDeclaration treeNode, IHighlightingConsumer consumer, IReadOnlyCallGraphContext context,
IMethod method)
{
var eventHandlersCount = UnityEventsElementContainer.GetAssetUsagesCount(method, out _);
var animationEventsCount = myAnimationEventUsagesContainer.GetEventUsagesCountFor(method, out _);
if (eventHandlersCount + animationEventsCount <= 0) return false;
if (eventHandlersCount != 0 && animationEventsCount == 0)
AddEventHandlerHighlighting(treeNode, consumer, context);
if (eventHandlersCount == 0 && animationEventsCount != 0)
AddAnimationEventHighlighting(treeNode, consumer, context);
AddAnimationEventAndEventHandlerHighlighting(treeNode, consumer, context);
return true;
}

private void AddEventHandlerHighlighting([NotNull] ITreeNode treeNode,
[NotNull] IHighlightingConsumer consumer,
IReadOnlyCallGraphContext context)
{
AddHighlighting(consumer, treeNode as ICSharpDeclaration, "Event handler", "Unity event handler", context);
}

private void AddAnimationEventHighlighting([NotNull] ITreeNode treeNode,
[NotNull] IHighlightingConsumer consumer,
IReadOnlyCallGraphContext context)
{
AddHighlighting(consumer, treeNode as ICSharpDeclaration, "Animation event", "Unity animation event", context);
}

private void AddAnimationEventAndEventHandlerHighlighting([NotNull] ITreeNode treeNode,
[NotNull] IHighlightingConsumer consumer,
IReadOnlyCallGraphContext context)
{
AddHighlighting(consumer, treeNode as ICSharpDeclaration, "Animation event and event handler",
"Unity animation event and Unity animation event", context);
}

protected override void AddHighlighting(IHighlightingConsumer consumer, ICSharpDeclaration element, string text,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using JetBrains.ReSharper.Daemon.UsageChecking;
using JetBrains.ReSharper.Plugins.Unity.Feature.Caches;
using JetBrains.ReSharper.Plugins.Unity.Yaml;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.DeferredCaches.AnimationEventsUsages;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.DeferredCaches.UnityEvents;
using JetBrains.ReSharper.Psi;

Expand Down Expand Up @@ -70,7 +71,9 @@ public bool SuppressUsageInspectionsOnElement(IDeclaredElement element, out Impl
return false;
}

if (IsEventHandler(unityApi, method) || IsRequiredSignatureMethod(method) ||
if (IsEventHandler(unityApi, method) ||
IsRequiredSignatureMethod(method) ||
IsAnimationEvent(solution, method) ||
IsImplicitlyUsedInterfaceMethod(method))
{
flags = ImplicitUseKindFlags.Access;
Expand All @@ -82,7 +85,9 @@ public bool SuppressUsageInspectionsOnElement(IDeclaredElement element, out Impl
flags = ImplicitUseKindFlags.Assign;
return true;

case IProperty property when IsEventHandler(unityApi, property.Setter) || IsImplicitlyUsedInterfaceProperty(property):
case IProperty property when IsEventHandler(unityApi, property.Setter) ||
IsImplicitlyUsedInterfaceProperty(property) ||
IsAnimationEvent(solution, property):
flags = ImplicitUseKindFlags.Assign;
return true;
}
Expand All @@ -91,6 +96,13 @@ public bool SuppressUsageInspectionsOnElement(IDeclaredElement element, out Impl
return false;
}

private static bool IsAnimationEvent(ISolution solution, IDeclaredElement property)
{
return solution
.GetComponent<AnimationEventUsagesContainer>()
.GetEventUsagesCountFor(property, out var isEstimatedResult) > 0 || isEstimatedResult;
}

private bool IsImplicitlyUsedInterfaceType(ITypeElement typeElement)
{
foreach (var implicitlyUsedTypeName in myImplicitlyUsedInterfaces)
Expand Down Expand Up @@ -152,7 +164,10 @@ private bool IsEventHandler(UnityApi unityApi, [CanBeNull] IMethod method)
if (!yamlParsingEnabled.Value || !assetSerializationMode.IsForceText || !solution.GetComponent<DeferredCacheController>().CompletedOnce.Value)
return unityApi.IsPotentialEventHandler(method, false); // if yaml parsing is disabled, we will consider private methods as unused

return solution.GetComponent<UnityEventsElementContainer>().GetAssetUsagesCount(method, out bool estimatedResult) > 0 || estimatedResult;
var eventsCount = solution
.GetComponent<UnityEventsElementContainer>()
.GetAssetUsagesCount(method, out bool estimatedResult);
return eventsCount > 0 || estimatedResult;
}

// If the method is marked with an attribute that has a method that is itself marked with RequiredSignature,
Expand Down
18 changes: 15 additions & 3 deletions resharper/resharper-unity/src/CSharp/ExpressionReferenceUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,20 @@ private static bool IsSpecificMethod(IInvocationExpression invocationExpression,

public static bool IsSceneManagerSceneRelatedMethod(this IInvocationExpressionReference reference)
{
return IsSceneRelatedMethod(reference, IsSceneManagerLoadScene);
return IsRelatedMethod(reference, IsSceneManagerLoadScene);
}

public static bool IsEditorSceneManagerSceneRelatedMethod(this IInvocationExpressionReference reference)
{
return IsSceneRelatedMethod(reference, IsEditorSceneManagerLoadScene);
return IsRelatedMethod(reference, IsEditorSceneManagerLoadScene);
}

public static bool IsAnimatorPlayMethod(this IInvocationExpressionReference reference)
{
return IsRelatedMethod(reference, IsAnimatorPlay);
}

private static bool IsSceneRelatedMethod(IInvocationExpressionReference reference, Func<IMethod, bool> checker)
private static bool IsRelatedMethod(IInvocationExpressionReference reference, Func<IMethod, bool> checker)
{
var result = reference.Resolve();
if (checker(result.DeclaredElement as IMethod))
Expand Down Expand Up @@ -117,5 +122,12 @@ private static bool IsSceneManagerLoadScene(IMethod method)

return false;
}

private static bool IsAnimatorPlay(IMethod method)
{
return method != null &&
method.ShortName.StartsWith("Play") &&
method.GetContainingType()?.GetClrName().Equals(KnownTypes.Animator) == true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using JetBrains.ReSharper.Feature.Services.Lookup;
using JetBrains.ReSharper.Features.Intellisense.CodeCompletion.CSharp.Rules;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.Caches;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.DeferredCaches.AnimatorUsages;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.CSharp;
using JetBrains.ReSharper.Psi.CSharp.Tree;
Expand Down Expand Up @@ -41,6 +42,11 @@ protected override bool AddLookupItems(CSharpCodeCompletionContext context, IIte
var cache = context.NodeInFile.GetSolution().GetComponent<UnityProjectSettingsCache>();
completionItems = cache.GetAllPossibleSceneNames();

} // animator state completion
else if (IsSpecificArgumentInSpecificMethod(context, out argumentLiteral, IsPlayAnimationMethod, IsCorrespondingArgument("stateName")))
{
var container = context.NodeInFile.GetSolution().GetComponent<AnimatorScriptUsagesElementContainer>();
completionItems = container.GetStateNames();
} // tag completion, tag == "..."
else if (IsTagEquality(context, out argumentLiteral))
{
Expand Down Expand Up @@ -137,6 +143,11 @@ private bool IsLoadSceneMethod(IInvocationExpression invocationExpression)
return invocationExpression.InvocationExpressionReference.IsSceneManagerSceneRelatedMethod() ||
invocationExpression.InvocationExpressionReference.IsEditorSceneManagerSceneRelatedMethod();
}

private static bool IsPlayAnimationMethod([NotNull] IInvocationExpression invocationExpression)
{
return invocationExpression.InvocationExpressionReference.IsAnimatorPlayMethod();
}

private bool IsTagEquality(CSharpCodeCompletionContext context, out ICSharpLiteralExpression stringLiteral)
{
Expand Down
Loading

0 comments on commit 892a111

Please sign in to comment.