```
feat(gameplay): 更新单位属性和材质配置 - 调整多个单位的基础生命值和攻击力数值平衡 - 修改CoreBattleGround材质的纹理缩放比例 - 更新地砖材质颜色为白色 - 优化纹理资源设置,调整最大纹理尺寸并添加PlayableAds平台支持 - 修复弓箭手技能目标查找逻辑,改用统一的敌人检测方法 - 为水晶矿添加中立阵营设置和光球视觉效果同步 - 调整预制体中的全局缩放参数 - 为狼人和石巨人添加NavMeshAgent组件支持 - 优化水晶矿血条显示位置计算方式 ```
This commit is contained in:
1 parent
0e7de5f885
commit
53d95802e5
448 files changed
+133333
-472
No files matched your search
+26
@@ -0,0 +1,26 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Codely.Microsoft.CodeAnalysis;
|
||||
using Codely.Microsoft.CodeAnalysis.CSharp;
|
||||
using SyntaxTree = Codely.Microsoft.CodeAnalysis.SyntaxTree;
|
||||
|
||||
namespace UnityTcp.Editor.Tools
|
||||
{
|
||||
internal class FixAmbiguousReference : ScriptFixProvider
|
||||
{
|
||||
public override bool CanFix(Diagnostic diagnostic) => diagnostic.Id == "CS0104";
|
||||
|
||||
public override bool ApplyFix(ref SyntaxTree tree, Diagnostic diagnostic, ScriptFixContext context)
|
||||
{
|
||||
var message = diagnostic.GetMessage();
|
||||
var aliasMatch = Regex.Match(message, @"(?<=')[^']*(?=' is)");
|
||||
var nsMatch = Regex.Match(message, @"(?<=between ')[^']*(?=' and)");
|
||||
if (!nsMatch.Success || !aliasMatch.Success)
|
||||
return false;
|
||||
|
||||
var updated = tree.AddUsingAliasDirective(nsMatch.Value, aliasMatch.Value);
|
||||
if (ReferenceEquals(updated, tree)) return false;
|
||||
tree = updated;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f8bbefc3ee0e8bf46906e72e1eece553
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Codely.Microsoft.CodeAnalysis;
|
||||
using Codely.Microsoft.CodeAnalysis.CSharp;
|
||||
using UnityEngine;
|
||||
using SyntaxTree = Codely.Microsoft.CodeAnalysis.SyntaxTree;
|
||||
|
||||
using UnityTcp.Editor.Helpers;
|
||||
namespace UnityTcp.Editor.Tools
|
||||
{
|
||||
// Handles CS0246 / CS0234 by searching all loaded assemblies for the missing type
|
||||
// and adding the discovered assembly + its namespace on demand.
|
||||
// For CS0234 where the type exists in a different namespace (e.g. UnityEngine.SerializedObject
|
||||
// when SerializedObject actually lives in UnityEditor), this provider also rewrites the
|
||||
// source code to replace the wrong namespace prefix with the correct one.
|
||||
internal class FixMissingAssemblyReference : ScriptFixProvider
|
||||
{
|
||||
static readonly string[] k_DiagnosticIds = { "CS0246", "CS0234", "CS0103" };
|
||||
|
||||
public override bool CanFix(Diagnostic diagnostic) =>
|
||||
k_DiagnosticIds.Contains(diagnostic.Id);
|
||||
|
||||
public override bool ApplyFix(ref SyntaxTree tree, Diagnostic diagnostic, ScriptFixContext context)
|
||||
{
|
||||
var message = diagnostic.GetMessage();
|
||||
ExtractNames(message, out var missingName, out var parentNamespace);
|
||||
|
||||
if (string.IsNullOrEmpty(missingName))
|
||||
return false;
|
||||
|
||||
// CS0234: 'X' does not exist in namespace 'Y' → the missing thing is the namespace Y.X.
|
||||
// Try resolving it as a namespace first.
|
||||
if (!string.IsNullOrEmpty(parentNamespace))
|
||||
{
|
||||
var fullNamespace = parentNamespace + "." + missingName;
|
||||
if (TryResolveNamespace(fullNamespace, context))
|
||||
return true;
|
||||
|
||||
// CS0234 with a parent namespace that doesn't contain the type:
|
||||
// the type might exist in a different namespace. Try to find the correct one
|
||||
// and rewrite the source code's namespace prefix.
|
||||
var correctNamespace = FindCorrectNamespace(missingName, parentNamespace);
|
||||
if (correctNamespace != null)
|
||||
{
|
||||
// Also ensure the assembly reference + import are registered.
|
||||
TryResolveTypeName(missingName, context);
|
||||
// Rewrite source: parentNamespace.missingName → correctNamespace.missingName
|
||||
var updatedTree = RewriteNamespacePrefix(tree, parentNamespace, correctNamespace, missingName);
|
||||
if (updatedTree != null)
|
||||
{
|
||||
tree = updatedTree;
|
||||
CodelyLogger.Log($"[FixMissingAssemblyReference] Rewrote '{parentNamespace}.{missingName}' → '{correctNamespace}.{missingName}'");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback / CS0246: resolve by type name.
|
||||
return TryResolveTypeName(missingName, context);
|
||||
}
|
||||
|
||||
// Searches all assemblies for any exported type whose namespace equals or starts with
|
||||
// fullNamespace, then registers ALL matching assemblies.
|
||||
bool TryResolveNamespace(string fullNamespace, ScriptFixContext context)
|
||||
{
|
||||
var prefix = fullNamespace + ".";
|
||||
var resolved = false;
|
||||
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
if (assembly.IsDynamic)
|
||||
continue;
|
||||
|
||||
var location = assembly.Location;
|
||||
if (string.IsNullOrEmpty(location) || context.AddedLocations.Contains(location))
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
var hasMatch = assembly.GetExportedTypes()
|
||||
.Any(t => t.IsPublic &&
|
||||
(t.Namespace == fullNamespace ||
|
||||
(t.Namespace != null && t.Namespace.StartsWith(prefix, StringComparison.Ordinal))));
|
||||
|
||||
if (!hasMatch)
|
||||
continue;
|
||||
|
||||
context.References.Add(MetadataReference.CreateFromFile(location));
|
||||
context.AddedLocations.Add(location);
|
||||
|
||||
if (!context.Imports.Contains(fullNamespace))
|
||||
context.Imports.Add(fullNamespace);
|
||||
|
||||
CodelyLogger.Log($"[FixMissingAssemblyReference] Resolved namespace '{fullNamespace}' → {assembly.GetName().Name}");
|
||||
resolved = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Some assemblies throw on GetExportedTypes; skip them.
|
||||
}
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
// Searches all assemblies for exported types whose simple name matches typeName,
|
||||
// then registers ALL matching assemblies and their namespaces.
|
||||
bool TryResolveTypeName(string typeName, ScriptFixContext context)
|
||||
{
|
||||
var resolved = false;
|
||||
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
if (assembly.IsDynamic)
|
||||
continue;
|
||||
|
||||
var location = assembly.Location;
|
||||
if (string.IsNullOrEmpty(location) || context.AddedLocations.Contains(location))
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
var matches = assembly.GetExportedTypes()
|
||||
.Where(t => t.Name == typeName && t.IsPublic)
|
||||
.ToList();
|
||||
|
||||
if (matches.Count == 0)
|
||||
continue;
|
||||
|
||||
context.References.Add(MetadataReference.CreateFromFile(location));
|
||||
context.AddedLocations.Add(location);
|
||||
|
||||
foreach (var t in matches)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(t.Namespace) && !context.Imports.Contains(t.Namespace))
|
||||
context.Imports.Add(t.Namespace);
|
||||
}
|
||||
|
||||
var names = string.Join(", ", matches.Select(t => t.FullName));
|
||||
CodelyLogger.Log($"[FixMissingAssemblyReference] Resolved '{typeName}' → {assembly.GetName().Name} ({names})");
|
||||
resolved = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Some assemblies throw on GetExportedTypes; skip them.
|
||||
}
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
// CS0234: "The type or namespace name 'X' does not exist in the namespace 'Y'"
|
||||
static readonly Regex k_Cs0234Pattern =
|
||||
new Regex(@"'([^']+)'\s+does not exist in the namespace\s+'([^']+)'", RegexOptions.Compiled);
|
||||
|
||||
// CS0246: "The type or namespace name 'X' could not be found ..."
|
||||
static readonly Regex k_SingleQuotePattern =
|
||||
new Regex(@"'([^']+)'", RegexOptions.Compiled);
|
||||
|
||||
// Extracts the missing name and, for CS0234, the parent namespace.
|
||||
static void ExtractNames(string message, out string missingName, out string parentNamespace)
|
||||
{
|
||||
var m = k_Cs0234Pattern.Match(message);
|
||||
if (m.Success)
|
||||
{
|
||||
missingName = m.Groups[1].Value;
|
||||
parentNamespace = m.Groups[2].Value;
|
||||
return;
|
||||
}
|
||||
|
||||
var m2 = k_SingleQuotePattern.Match(message);
|
||||
missingName = m2.Success ? m2.Groups[1].Value : null;
|
||||
parentNamespace = null;
|
||||
}
|
||||
|
||||
// Searches all assemblies for a public type whose simple name matches typeName
|
||||
// but whose namespace differs from wrongNamespace. Returns the first matching
|
||||
// namespace, or null if no such type is found.
|
||||
internal static string FindCorrectNamespace(string typeName, string wrongNamespace)
|
||||
{
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
if (assembly.IsDynamic)
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
var match = assembly.GetExportedTypes()
|
||||
.FirstOrDefault(t => t.IsPublic
|
||||
&& t.Name == typeName
|
||||
&& t.Namespace != null
|
||||
&& t.Namespace != wrongNamespace);
|
||||
if (match != null)
|
||||
return match.Namespace;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Some assemblies throw on GetExportedTypes; skip them.
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Rewrites all occurrences of wrongNamespace.typeName in the source code to
|
||||
// correctNamespace.typeName by performing a text replacement and reparsing.
|
||||
static SyntaxTree RewriteNamespacePrefix(SyntaxTree tree, string wrongNamespace, string correctNamespace, string typeName)
|
||||
{
|
||||
var source = tree.GetText().ToString();
|
||||
var wrongPrefix = $"{wrongNamespace}.{typeName}";
|
||||
var correctPrefix = $"{correctNamespace}.{typeName}";
|
||||
|
||||
if (!source.Contains(wrongPrefix))
|
||||
return null;
|
||||
|
||||
var updatedSource = source.Replace(wrongPrefix, correctPrefix);
|
||||
return SyntaxFactory.ParseSyntaxTree(updatedSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8dff369db1c9a604893f04886ed906c9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Codely.Microsoft.CodeAnalysis;
|
||||
using Codely.Microsoft.CodeAnalysis.CSharp;
|
||||
using Codely.Microsoft.CodeAnalysis.Text;
|
||||
using SyntaxTree = Codely.Microsoft.CodeAnalysis.SyntaxTree;
|
||||
|
||||
namespace UnityTcp.Editor.Tools
|
||||
{
|
||||
internal class FixMissingBrace : ScriptFixProvider
|
||||
{
|
||||
// Jagged array rank: Type[][] or new Type[][]
|
||||
static readonly Regex k_JaggedArrayPattern = new Regex(
|
||||
@"\[\]\s*\[\]", RegexOptions.Compiled);
|
||||
|
||||
// Implicit array creation with named tuple: new[] { (name: ...)
|
||||
static readonly Regex k_NamedTupleArrayPattern = new Regex(
|
||||
@"new\s*\[\].*\(\s*\w+\s*:", RegexOptions.Compiled);
|
||||
|
||||
public override bool CanFix(Diagnostic diagnostic) => diagnostic.Id == "CS1513";
|
||||
|
||||
public override bool ApplyFix(ref SyntaxTree tree, Diagnostic diagnostic, ScriptFixContext context)
|
||||
{
|
||||
var source = tree.ToString();
|
||||
var lineSpan = diagnostic.Location.GetLineSpan();
|
||||
int diagLine = lineSpan.Span.Start.Line;
|
||||
|
||||
// Guard: skip CS1513 caused by script-incompatible syntax rather than
|
||||
// actual missing braces. These patterns confuse the script parser and
|
||||
// inserting } would make things worse.
|
||||
var lines = source.Split('\n');
|
||||
if (diagLine >= 0 && diagLine < lines.Length)
|
||||
{
|
||||
var lineText = lines[diagLine].Trim();
|
||||
if (k_JaggedArrayPattern.IsMatch(lineText))
|
||||
return false;
|
||||
if (k_NamedTupleArrayPattern.IsMatch(lineText))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Insert } at the diagnostic position
|
||||
var sourceText = SourceText.From(source);
|
||||
int insertPos = sourceText.Lines.GetPosition(lineSpan.Span.Start);
|
||||
var newSource = source.Insert(insertPos, "}\n");
|
||||
|
||||
var updated = SyntaxFactory.ParseSyntaxTree(newSource);
|
||||
if (ReferenceEquals(updated, tree)) return false;
|
||||
tree = updated;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2bb64ceba5600d54e8d51455ee673ef8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Codely.Microsoft.CodeAnalysis;
|
||||
using Codely.Microsoft.CodeAnalysis.CSharp;
|
||||
using SyntaxTree = Codely.Microsoft.CodeAnalysis.SyntaxTree;
|
||||
|
||||
namespace UnityTcp.Editor.Tools
|
||||
{
|
||||
internal class FixMissingImports : ScriptFixProvider
|
||||
{
|
||||
static readonly string[] k_DiagnosticIds = { "CS0246", "CS0103", "CS1061" };
|
||||
|
||||
readonly Dictionary<string, string[]> k_NamespaceKeywords = new Dictionary<string, string[]>
|
||||
{
|
||||
{ "System.Linq", new[] { "Where", "Select", "OrderBy", "Concat", "Any", "First", "Last", "ToList", "ToArray", "GroupBy", "Distinct" } },
|
||||
{ "System.Collections.Generic", new[] { "List<>", "Dictionary<>", "HashSet<>", "Queue<>", "Stack<>", "SortedList<>" } },
|
||||
{ "UnityEditor", new[] { "MonoScript", "AssetDatabase", "EditorUtility", "Selection", "SceneView", "PrefabUtility", "Undo", "EditorGUILayout", "SerializedObject", "SerializedProperty" } },
|
||||
{ "UnityEngine.SceneManagement", new[] { "SceneManager", "Scene", "LoadSceneMode" } },
|
||||
{ "UnityEditor.SceneManagement", new[] { "EditorSceneManager" } },
|
||||
};
|
||||
|
||||
public override bool CanFix(Diagnostic diagnostic)
|
||||
{
|
||||
if (!k_DiagnosticIds.Contains(diagnostic.Id))
|
||||
return false;
|
||||
var message = diagnostic.GetMessage();
|
||||
return k_NamespaceKeywords.Values.Any(kws => kws.Any(kw => message.Contains($"'{kw}'")));
|
||||
}
|
||||
|
||||
public override bool ApplyFix(ref SyntaxTree tree, Diagnostic diagnostic, ScriptFixContext context)
|
||||
{
|
||||
var message = diagnostic.GetMessage();
|
||||
foreach (var pair in k_NamespaceKeywords)
|
||||
{
|
||||
if (pair.Value.Any(kw => message.Contains($"'{kw}'")))
|
||||
{
|
||||
if (!context.Imports.Contains(pair.Key))
|
||||
{
|
||||
context.Imports.Add(pair.Key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 533b0cca22c2a3f4180a93f10585b5e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
using Codely.Microsoft.CodeAnalysis;
|
||||
using Codely.Microsoft.CodeAnalysis.CSharp;
|
||||
using SyntaxTree = Codely.Microsoft.CodeAnalysis.SyntaxTree;
|
||||
|
||||
namespace UnityTcp.Editor.Tools
|
||||
{
|
||||
internal class FixMissingParenthesis : ScriptFixProvider
|
||||
{
|
||||
public override bool CanFix(Diagnostic diagnostic) => diagnostic.Id == "CS1026";
|
||||
|
||||
public override bool ApplyFix(ref SyntaxTree tree, Diagnostic diagnostic, ScriptFixContext context)
|
||||
{
|
||||
var updated = tree.InsertTextAtLocation(diagnostic.Location, ")");
|
||||
if (ReferenceEquals(updated, tree)) return false;
|
||||
tree = updated;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3c1ebc774b153c4bba0119e184fe503
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
using Codely.Microsoft.CodeAnalysis;
|
||||
using Codely.Microsoft.CodeAnalysis.CSharp;
|
||||
using SyntaxTree = Codely.Microsoft.CodeAnalysis.SyntaxTree;
|
||||
|
||||
namespace UnityTcp.Editor.Tools
|
||||
{
|
||||
internal class FixMissingSemicolon : ScriptFixProvider
|
||||
{
|
||||
public override bool CanFix(Diagnostic diagnostic) => diagnostic.Id == "CS1002";
|
||||
|
||||
public override bool ApplyFix(ref SyntaxTree tree, Diagnostic diagnostic, ScriptFixContext context)
|
||||
{
|
||||
var updated = tree.InsertTextAtLocation(diagnostic.Location, ";");
|
||||
if (ReferenceEquals(updated, tree)) return false;
|
||||
tree = updated;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e183b257f3396a64697ab7b36fdec226
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
using Codely.Microsoft.CodeAnalysis;
|
||||
using Codely.Microsoft.CodeAnalysis.CSharp;
|
||||
using SyntaxTree = Codely.Microsoft.CodeAnalysis.SyntaxTree;
|
||||
|
||||
namespace UnityTcp.Editor.Tools
|
||||
{
|
||||
internal class FixMissingSquareBracket : ScriptFixProvider
|
||||
{
|
||||
public override bool CanFix(Diagnostic diagnostic) => diagnostic.Id == "CS1003";
|
||||
|
||||
public override bool ApplyFix(ref SyntaxTree tree, Diagnostic diagnostic, ScriptFixContext context)
|
||||
{
|
||||
var updated = tree.InsertTextAtLocation(diagnostic.Location, "]");
|
||||
if (ReferenceEquals(updated, tree)) return false;
|
||||
tree = updated;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d45c5e11512e28f4cacc0f2c04039542
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Codely.Microsoft.CodeAnalysis;
|
||||
using Codely.Microsoft.CodeAnalysis.CSharp;
|
||||
using Codely.Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using UnityEngine;
|
||||
using SyntaxTree = Codely.Microsoft.CodeAnalysis.SyntaxTree;
|
||||
|
||||
using UnityTcp.Editor.Helpers;
|
||||
namespace UnityTcp.Editor.Tools
|
||||
{
|
||||
// Handles CS0103 for common UnityEngine.Object static methods called without qualification
|
||||
// in Roslyn scripting context (e.g., FindObjectOfType<T>() → UnityEngine.Object.FindObjectOfType<T>()).
|
||||
//
|
||||
// In MonoBehaviour scripts these methods are inherited and can be called unqualified,
|
||||
// but Roslyn scripting has no base class, so they must be explicitly qualified.
|
||||
internal class FixUnqualifiedUnityStaticMethod : ScriptFixProvider
|
||||
{
|
||||
static readonly HashSet<string> k_UnityObjectMethods = new HashSet<string>
|
||||
{
|
||||
"FindObjectOfType",
|
||||
"FindObjectsOfType",
|
||||
"FindAnyObjectByType",
|
||||
"FindObjectsByType",
|
||||
"Instantiate",
|
||||
"Destroy",
|
||||
"DestroyImmediate",
|
||||
"DontDestroyOnLoad"
|
||||
};
|
||||
|
||||
const string k_Qualifier = "UnityEngine.Object.";
|
||||
|
||||
public override bool CanFix(Diagnostic diagnostic)
|
||||
{
|
||||
if (diagnostic.Id != "CS0103")
|
||||
return false;
|
||||
|
||||
var message = diagnostic.GetMessage();
|
||||
return k_UnityObjectMethods.Any(m => message.Contains($"'{m}'"));
|
||||
}
|
||||
|
||||
public override bool ApplyFix(ref SyntaxTree tree, Diagnostic diagnostic, ScriptFixContext context)
|
||||
{
|
||||
// Extract method name from diagnostic message
|
||||
var message = diagnostic.GetMessage();
|
||||
string methodName = null;
|
||||
foreach (var m in k_UnityObjectMethods)
|
||||
{
|
||||
if (message.Contains($"'{m}'"))
|
||||
{
|
||||
methodName = m;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (methodName == null)
|
||||
return false;
|
||||
|
||||
// Locate the exact node via diagnostic span. Spans are always fresh here because
|
||||
// CompileAndAutoFix recompiles after every tree-modifying fix before calling ApplyFix again.
|
||||
var root = tree.GetRoot();
|
||||
var spanNode = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
|
||||
|
||||
SyntaxNode target = null;
|
||||
for (var n = spanNode; n != null; n = n.Parent)
|
||||
{
|
||||
if (n is IdentifierNameSyntax id && id.Identifier.Text == methodName) { target = n; break; }
|
||||
if (n is GenericNameSyntax gen && gen.Identifier.Text == methodName) { target = n; break; }
|
||||
}
|
||||
|
||||
if (target == null || target.Parent is MemberAccessExpressionSyntax)
|
||||
return false;
|
||||
|
||||
var source = tree.GetText().ToString();
|
||||
tree = SyntaxFactory.ParseSyntaxTree(source.Insert(target.SpanStart, k_Qualifier));
|
||||
|
||||
CodelyLogger.Log($"[FixUnqualifiedUnityStaticMethod] Qualified '{methodName}' → '{k_Qualifier}{methodName}'");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9be25a0a8b570944e8d8f87bb8d934b3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
using System.Collections.Generic;
|
||||
using Codely.Microsoft.CodeAnalysis;
|
||||
|
||||
namespace UnityTcp.Editor.Tools
|
||||
{
|
||||
internal class ScriptFixContext
|
||||
{
|
||||
public List<string> Imports { get; }
|
||||
public List<MetadataReference> References { get; }
|
||||
public HashSet<string> AddedLocations { get; }
|
||||
|
||||
public ScriptFixContext(
|
||||
List<string> imports,
|
||||
List<MetadataReference> references,
|
||||
HashSet<string> addedLocations)
|
||||
{
|
||||
Imports = imports;
|
||||
References = references;
|
||||
AddedLocations = addedLocations;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92f47075059ef8b4a944a4a2b0bfafbe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
using Codely.Microsoft.CodeAnalysis;
|
||||
using Codely.Microsoft.CodeAnalysis.CSharp;
|
||||
using SyntaxTree = Codely.Microsoft.CodeAnalysis.SyntaxTree;
|
||||
|
||||
namespace UnityTcp.Editor.Tools
|
||||
{
|
||||
internal abstract class ScriptFixProvider
|
||||
{
|
||||
public abstract bool CanFix(Diagnostic diagnostic);
|
||||
|
||||
// Returns true if a fix was applied.
|
||||
// `tree` is updated via ref; context.Imports / context.References may be mutated.
|
||||
public abstract bool ApplyFix(ref SyntaxTree tree, Diagnostic diagnostic, ScriptFixContext context);
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ba8b9b9f2c239d4e890b1b4e4279348
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
using System.Linq;
|
||||
using Codely.Microsoft.CodeAnalysis;
|
||||
using Codely.Microsoft.CodeAnalysis.CSharp;
|
||||
using Codely.Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Codely.Microsoft.CodeAnalysis.Text;
|
||||
using SyntaxTree = Codely.Microsoft.CodeAnalysis.SyntaxTree;
|
||||
|
||||
namespace UnityTcp.Editor.Tools
|
||||
{
|
||||
internal static class ScriptSyntaxUtils
|
||||
{
|
||||
internal static SyntaxTree InsertTextAtLocation(this SyntaxTree tree, Location location, string text)
|
||||
{
|
||||
var lineSpan = location.GetLineSpan().Span;
|
||||
var sourceText = SourceText.From(tree.ToString());
|
||||
var position = sourceText.Lines.GetPosition(lineSpan.Start);
|
||||
var newSource = tree.ToString().Insert(position, text);
|
||||
return SyntaxFactory.ParseSyntaxTree(newSource);
|
||||
}
|
||||
|
||||
internal static SyntaxTree AddUsingAliasDirective(this SyntaxTree tree, string namespaceName, string aliasName)
|
||||
{
|
||||
if (!(tree.GetRoot() is CompilationUnitSyntax root))
|
||||
return tree;
|
||||
|
||||
bool exists = root.Usings.Any(u =>
|
||||
u.Name.ToString() == namespaceName && u.Alias?.Name.ToString() == aliasName);
|
||||
if (exists)
|
||||
return tree;
|
||||
|
||||
var usingDirective = SyntaxFactory
|
||||
.UsingDirective(SyntaxFactory.ParseName(namespaceName))
|
||||
.WithAlias(SyntaxFactory.NameEquals(SyntaxFactory.IdentifierName(aliasName)))
|
||||
.NormalizeWhitespace()
|
||||
.WithTrailingTrivia(SyntaxFactory.CarriageReturnLineFeed);
|
||||
|
||||
var newRoot = root.AddUsings(usingDirective).NormalizeWhitespace();
|
||||
return CSharpSyntaxTree.Create(newRoot);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57e3fbb1d95d4b647a36e4f169d8c4ba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in new issue
Block a user