├── .gitattributes ├── .gitignore ├── License.md ├── README.md ├── UnityRefactorHelper.sln └── UnityRefactorHelper ├── Cache.cs ├── Constants.cs ├── Helpers ├── FileIdGenerator.cs ├── Md4.cs └── UnityProjectFileIdReplacer.cs ├── Key.snk ├── Model ├── FileScanItem.cs ├── ProjectScanItem.cs ├── ProjectSyncItem.cs └── Settings.cs ├── Properties ├── Annotations.cs └── AssemblyInfo.cs ├── Resources ├── CommandIcon.png └── MainIcon.png ├── Service ├── CommonService.cs └── ProjectService.cs ├── SettingsToolWindow.cs ├── SolutionEvents.cs ├── UnityRefactorHelper.csproj ├── UnityRefactorHelperSettingsCommand.cs ├── UnityRefactorHelperSettingsPackage.cs ├── UnityRefactorHelperSettingsPackage.vsct ├── VSPackage.resx ├── View ├── SyncProjectControl.xaml ├── SyncProjectControl.xaml.cs ├── UnityRefactorHelperSettingsControl.xaml └── UnityRefactorHelperSettingsControl.xaml.cs ├── ViewModel └── ToolWindowViewModel.cs ├── app.config ├── packages.config └── source.extension.vsixmanifest /.gitattributes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PDarkTemplar/UnityRefactorHelper/9b288bc7d6dff152f73bb7cc65424959d9bace13/.gitattributes -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | packages 2 | *.DotSettings 3 | /**/obj 4 | /**/bin -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 PDarkTemplar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity Refactor Helper 2 | Visual Studio 2015, 2017 helper extension for Unity script refactoring. 3 | 4 | Download 5 | 6 | This extension will be helpful if you keep Unity scripts in separate libraries. Extension works only with C#. Scripts references in the scenes and prefabs will be broken after refactoring (changing namespace or class name). This extension monitors changes in project files and update scene and prefab files after assembly build. 7 | 8 | Set the asset serialization to force text and version control to visible Meta files in Unity (Edit -> Project Settings -> Editor). 9 | 10 | Visual Studio extension located at View -> Other Windows -> Unity Refactor Helper. It is become active after solution is open and all projects are loaded. 11 | 12 | #Possible settings 13 | 20 | 21 | #Additional Information 22 | You can edit project GUID by clicking on the project name in the watch projects. Extension creates two files (configuration and cache) in the *.sln file folder. It tracks file save and synchronize files after build if it is required. Tracking occurs only inside the library, file move tracking between libraries is not supported. 23 | -------------------------------------------------------------------------------- /UnityRefactorHelper.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityRefactorHelper", "UnityRefactorHelper\UnityRefactorHelper.csproj", "{D7D4741D-E917-4509-A620-ABE6A9573784}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {D7D4741D-E917-4509-A620-ABE6A9573784}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {D7D4741D-E917-4509-A620-ABE6A9573784}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {D7D4741D-E917-4509-A620-ABE6A9573784}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {D7D4741D-E917-4509-A620-ABE6A9573784}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /UnityRefactorHelper/Cache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using EnvDTE; 4 | using Microsoft.VisualStudio.Shell; 5 | using Microsoft.VisualStudio.Shell.Interop; 6 | using UnityRefactorHelper.Model; 7 | 8 | namespace UnityRefactorHelper 9 | { 10 | public class Cache : IDisposable 11 | { 12 | static Cache() 13 | { 14 | Instance = new Cache(); 15 | } 16 | 17 | private Cache() 18 | { 19 | ScanProjects = new List(); 20 | } 21 | 22 | public OleMenuCommandService OleMenuCommandService { get; set; } 23 | public DTE Dte { get; set; } 24 | public IVsSolution SolutionService { get; set; } 25 | 26 | public Settings Settings { get; set; } 27 | public List ScanProjects { get; set; } 28 | 29 | public static Cache Instance { get; private set; } 30 | 31 | public void Dispose() 32 | { 33 | SolutionService = null; 34 | Dte = null; 35 | OleMenuCommandService = null; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /UnityRefactorHelper/Constants.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UnityRefactorHelper 4 | { 5 | public static class Constants 6 | { 7 | public const int CommandId = 0x0100; 8 | public static readonly Guid CommandSet = new Guid("4043f182-b7f8-4f5f-a41e-2a9b83329ac7"); 9 | } 10 | } -------------------------------------------------------------------------------- /UnityRefactorHelper/Helpers/FileIdGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using System.Text; 3 | 4 | namespace UnityRefactorHelper.Helpers 5 | { 6 | public static class FileIdGenerator 7 | { 8 | public static int Compute(string namespaceName, string className) 9 | { 10 | if (string.IsNullOrEmpty(namespaceName)) 11 | namespaceName = string.Empty; 12 | 13 | var toBeHashed = "s\0\0\0" + namespaceName + className; 14 | 15 | using (HashAlgorithm hash = new Md4()) 16 | { 17 | var hashed = hash.ComputeHash(Encoding.UTF8.GetBytes(toBeHashed)); 18 | 19 | var result = 0; 20 | 21 | for (var i = 3; i >= 0; --i) 22 | { 23 | result <<= 8; 24 | result |= hashed[i]; 25 | } 26 | 27 | return result; 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /UnityRefactorHelper/Helpers/Md4.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Security.Cryptography; 4 | 5 | namespace UnityRefactorHelper.Helpers 6 | { 7 | public sealed class Md4 : HashAlgorithm 8 | { 9 | private readonly uint[] _x; 10 | private uint _a; 11 | private uint _b; 12 | private int _bytesProcessed; 13 | private uint _c; 14 | private uint _d; 15 | 16 | public Md4() 17 | { 18 | _x = new uint[16]; 19 | 20 | Initialize(); 21 | } 22 | 23 | public override void Initialize() 24 | { 25 | _a = 0x67452301; 26 | _b = 0xefcdab89; 27 | _c = 0x98badcfe; 28 | _d = 0x10325476; 29 | 30 | _bytesProcessed = 0; 31 | } 32 | 33 | protected override void HashCore(byte[] array, int offset, int length) 34 | { 35 | ProcessMessage(Bytes(array, offset, length)); 36 | } 37 | 38 | protected override byte[] HashFinal() 39 | { 40 | try 41 | { 42 | ProcessMessage(Padding()); 43 | 44 | return new[] {_a, _b, _c, _d}.SelectMany(word => Bytes(word)).ToArray(); 45 | } 46 | finally 47 | { 48 | Initialize(); 49 | } 50 | } 51 | 52 | private void ProcessMessage(IEnumerable bytes) 53 | { 54 | foreach (var b in bytes) 55 | { 56 | var c = _bytesProcessed & 63; 57 | var i = c >> 2; 58 | var s = (c & 3) << 3; 59 | 60 | _x[i] = (_x[i] & ~((uint) 255 << s)) | ((uint) b << s); 61 | 62 | if (c == 63) 63 | { 64 | Process16WordBlock(); 65 | } 66 | 67 | _bytesProcessed++; 68 | } 69 | } 70 | 71 | private static IEnumerable Bytes(byte[] bytes, int offset, int length) 72 | { 73 | for (var i = offset; i < length; i++) 74 | { 75 | yield return bytes[i]; 76 | } 77 | } 78 | 79 | private IEnumerable Bytes(uint word) 80 | { 81 | yield return (byte) (word & 255); 82 | yield return (byte) ((word >> 8) & 255); 83 | yield return (byte) ((word >> 16) & 255); 84 | yield return (byte) ((word >> 24) & 255); 85 | } 86 | 87 | private IEnumerable Repeat(byte value, int count) 88 | { 89 | for (var i = 0; i < count; i++) 90 | { 91 | yield return value; 92 | } 93 | } 94 | 95 | private IEnumerable Padding() 96 | { 97 | return Repeat(128, 1) 98 | .Concat(Repeat(0, ((_bytesProcessed + 8) & 0x7fffffc0) + 55 - _bytesProcessed)) 99 | .Concat(Bytes((uint) _bytesProcessed << 3)) 100 | .Concat(Repeat(0, 4)); 101 | } 102 | 103 | private void Process16WordBlock() 104 | { 105 | var aa = _a; 106 | var bb = _b; 107 | var cc = _c; 108 | var dd = _d; 109 | 110 | foreach (var k in new[] {0, 4, 8, 12}) 111 | { 112 | aa = Round1Operation(aa, bb, cc, dd, _x[k], 3); 113 | dd = Round1Operation(dd, aa, bb, cc, _x[k + 1], 7); 114 | cc = Round1Operation(cc, dd, aa, bb, _x[k + 2], 11); 115 | bb = Round1Operation(bb, cc, dd, aa, _x[k + 3], 19); 116 | } 117 | 118 | foreach (var k in new[] {0, 1, 2, 3}) 119 | { 120 | aa = Round2Operation(aa, bb, cc, dd, _x[k], 3); 121 | dd = Round2Operation(dd, aa, bb, cc, _x[k + 4], 5); 122 | cc = Round2Operation(cc, dd, aa, bb, _x[k + 8], 9); 123 | bb = Round2Operation(bb, cc, dd, aa, _x[k + 12], 13); 124 | } 125 | 126 | foreach (var k in new[] {0, 2, 1, 3}) 127 | { 128 | aa = Round3Operation(aa, bb, cc, dd, _x[k], 3); 129 | dd = Round3Operation(dd, aa, bb, cc, _x[k + 8], 9); 130 | cc = Round3Operation(cc, dd, aa, bb, _x[k + 4], 11); 131 | bb = Round3Operation(bb, cc, dd, aa, _x[k + 12], 15); 132 | } 133 | 134 | unchecked 135 | { 136 | _a += aa; 137 | _b += bb; 138 | _c += cc; 139 | _d += dd; 140 | } 141 | } 142 | 143 | private static uint ROL(uint value, int numberOfBits) 144 | { 145 | return (value << numberOfBits) | (value >> (32 - numberOfBits)); 146 | } 147 | 148 | private static uint Round1Operation(uint a, uint b, uint c, uint d, uint xk, int s) 149 | { 150 | unchecked 151 | { 152 | return ROL(a + ((b & c) | (~b & d)) + xk, s); 153 | } 154 | } 155 | 156 | private static uint Round2Operation(uint a, uint b, uint c, uint d, uint xk, int s) 157 | { 158 | unchecked 159 | { 160 | return ROL(a + ((b & c) | (b & d) | (c & d)) + xk + 0x5a827999, s); 161 | } 162 | } 163 | 164 | private static uint Round3Operation(uint a, uint b, uint c, uint d, uint xk, int s) 165 | { 166 | unchecked 167 | { 168 | return ROL(a + (b ^ c ^ d) + xk + 0x6ed9eba1, s); 169 | } 170 | } 171 | } 172 | } -------------------------------------------------------------------------------- /UnityRefactorHelper/Helpers/UnityProjectFileIdReplacer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Text.RegularExpressions; 5 | using UnityRefactorHelper.Model; 6 | using UnityRefactorHelper.Service; 7 | 8 | namespace UnityRefactorHelper.Helpers 9 | { 10 | public class UnityProjectFileIdReplacer 11 | { 12 | private readonly string _vsProject; 13 | 14 | public UnityProjectFileIdReplacer(string vsProject) 15 | { 16 | _vsProject = vsProject; 17 | } 18 | 19 | public void Replace() 20 | { 21 | var scanProject = Cache.Instance.ScanProjects.FirstOrDefault(x => x.ProjectName == _vsProject); 22 | if (scanProject == null) return; 23 | 24 | ProcessFiles(scanProject); 25 | } 26 | 27 | private void ProcessFiles(ProjectScanItem projectScanItem) 28 | { 29 | var path = Cache.Instance.Settings.UnityProjectPath; 30 | var scenes = GetFilePathes(path, "*.unity"); 31 | var prefabs = GetFilePathes(path, "*.prefab"); 32 | var replaceIn = new List(); 33 | replaceIn.AddRange(scenes); 34 | replaceIn.AddRange(prefabs); 35 | 36 | ProcessReplace(replaceIn, projectScanItem); 37 | ProcessProjectScanItem(projectScanItem); 38 | } 39 | 40 | private IEnumerable GetFilePathes(string parentPath, string mask) 41 | { 42 | var filenames = Directory.GetFiles(parentPath, mask, SearchOption.AllDirectories); 43 | return filenames; 44 | } 45 | 46 | private void ProcessReplace(IEnumerable replacePathes, ProjectScanItem projectScanItem) 47 | { 48 | foreach (var replacePath in replacePathes) 49 | { 50 | var lines = File.ReadAllLines(replacePath); 51 | var newLines = new List(); 52 | foreach (var line in lines) 53 | { 54 | var isFound = false; 55 | foreach (var file in projectScanItem.Files.Where(x => x.NewId.HasValue)) 56 | { 57 | var r = Regex.Match(line, 58 | $" m_Script: {{fileID: {file.OldId}, guid: {projectScanItem.ProjectGuid.ToString("n").ToLower()}, type: 3}}", 59 | RegexOptions.IgnoreCase); 60 | if (r.Success) 61 | { 62 | newLines.Add( 63 | r.Result( 64 | $" m_Script: {{fileID: {file.NewId}, guid: {projectScanItem.ProjectGuid.ToString("n").ToLower()}, type: 3}}")); 65 | isFound = true; 66 | break; 67 | } 68 | } 69 | if (!isFound) 70 | newLines.Add(line); 71 | } 72 | File.WriteAllLines(replacePath, newLines.ToArray()); 73 | } 74 | } 75 | 76 | private void ProcessProjectScanItem(ProjectScanItem projectScanItem) 77 | { 78 | var renamed = projectScanItem.Files.Where(x => x.NewId.HasValue); 79 | foreach (var r in renamed) 80 | { 81 | r.OldId = r.NewId.Value; 82 | r.NewId = null; 83 | } 84 | 85 | CommonService.SaveSyncProjectItemsToCache(); 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /UnityRefactorHelper/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PDarkTemplar/UnityRefactorHelper/9b288bc7d6dff152f73bb7cc65424959d9bace13/UnityRefactorHelper/Key.snk -------------------------------------------------------------------------------- /UnityRefactorHelper/Model/FileScanItem.cs: -------------------------------------------------------------------------------- 1 | namespace UnityRefactorHelper.Model 2 | { 3 | public class FileScanItem 4 | { 5 | public string ProjectFileId { get; set; } 6 | public int OldId { get; set; } 7 | public int? NewId { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /UnityRefactorHelper/Model/ProjectScanItem.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace UnityRefactorHelper.Model 4 | { 5 | public class ProjectScanItem : ProjectSyncItem 6 | { 7 | public ProjectScanItem() 8 | { 9 | Files = new List(); 10 | } 11 | 12 | public List Files { get; } 13 | } 14 | } -------------------------------------------------------------------------------- /UnityRefactorHelper/Model/ProjectSyncItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UnityRefactorHelper.Model 4 | { 5 | public class ProjectSyncItem 6 | { 7 | public string ProjectName { get; set; } 8 | public Guid ProjectGuid { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /UnityRefactorHelper/Model/Settings.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | 3 | namespace UnityRefactorHelper.Model 4 | { 5 | public class Settings 6 | { 7 | public Settings() 8 | { 9 | ProjectSyncItems = new ObservableCollection(); 10 | } 11 | 12 | public bool IsEnabled { get; set; } 13 | public string UnityProjectPath { get; set; } 14 | public ObservableCollection ProjectSyncItems { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /UnityRefactorHelper/Properties/Annotations.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | #pragma warning disable 1591 4 | // ReSharper disable UnusedMember.Global 5 | // ReSharper disable MemberCanBePrivate.Global 6 | // ReSharper disable UnusedAutoPropertyAccessor.Global 7 | // ReSharper disable IntroduceOptionalParameters.Global 8 | // ReSharper disable MemberCanBeProtected.Global 9 | // ReSharper disable InconsistentNaming 10 | // ReSharper disable CheckNamespace 11 | 12 | namespace UnityRefactorHelper.Annotations 13 | { 14 | /// 15 | /// Indicates that the value of the marked element could be null sometimes, 16 | /// so the check for null is necessary before its usage. 17 | /// 18 | /// 19 | /// [CanBeNull] public object Test() { return null; } 20 | /// public void UseTest() { 21 | /// var p = Test(); 22 | /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' 23 | /// } 24 | /// 25 | [AttributeUsage( 26 | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | 27 | AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event)] 28 | public sealed class CanBeNullAttribute : Attribute 29 | { 30 | } 31 | 32 | /// 33 | /// Indicates that the value of the marked element could never be null. 34 | /// 35 | /// 36 | /// [NotNull] public object Foo() { 37 | /// return null; // Warning: Possible 'null' assignment 38 | /// } 39 | /// 40 | [AttributeUsage( 41 | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | 42 | AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event)] 43 | public sealed class NotNullAttribute : Attribute 44 | { 45 | } 46 | 47 | /// 48 | /// Indicates that collection or enumerable value does not contain null elements. 49 | /// 50 | [AttributeUsage( 51 | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | 52 | AttributeTargets.Delegate | AttributeTargets.Field)] 53 | public sealed class ItemNotNullAttribute : Attribute 54 | { 55 | } 56 | 57 | /// 58 | /// Indicates that collection or enumerable value can contain null elements. 59 | /// 60 | [AttributeUsage( 61 | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | 62 | AttributeTargets.Delegate | AttributeTargets.Field)] 63 | public sealed class ItemCanBeNullAttribute : Attribute 64 | { 65 | } 66 | 67 | /// 68 | /// Indicates that the marked method builds string by format pattern and (optional) arguments. 69 | /// Parameter, which contains format string, should be given in constructor. The format string 70 | /// should be in -like form. 71 | /// 72 | /// 73 | /// [StringFormatMethod("message")] 74 | /// public void ShowError(string message, params object[] args) { /* do something */ } 75 | /// public void Foo() { 76 | /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string 77 | /// } 78 | /// 79 | [AttributeUsage( 80 | AttributeTargets.Constructor | AttributeTargets.Method | 81 | AttributeTargets.Property | AttributeTargets.Delegate)] 82 | public sealed class StringFormatMethodAttribute : Attribute 83 | { 84 | /// 85 | /// Specifies which parameter of an annotated method should be treated as format-string 86 | /// 87 | public StringFormatMethodAttribute(string formatParameterName) 88 | { 89 | FormatParameterName = formatParameterName; 90 | } 91 | 92 | public string FormatParameterName { get; private set; } 93 | } 94 | 95 | /// 96 | /// For a parameter that is expected to be one of the limited set of values. 97 | /// Specify fields of which type should be used as values for this parameter. 98 | /// 99 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] 100 | public sealed class ValueProviderAttribute : Attribute 101 | { 102 | public ValueProviderAttribute(string name) 103 | { 104 | Name = name; 105 | } 106 | 107 | [NotNull] 108 | public string Name { get; private set; } 109 | } 110 | 111 | /// 112 | /// Indicates that the function argument should be string literal and match one 113 | /// of the parameters of the caller function. For example, ReSharper annotates 114 | /// the parameter of . 115 | /// 116 | /// 117 | /// public void Foo(string param) { 118 | /// if (param == null) 119 | /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol 120 | /// } 121 | /// 122 | [AttributeUsage(AttributeTargets.Parameter)] 123 | public sealed class InvokerParameterNameAttribute : Attribute 124 | { 125 | } 126 | 127 | /// 128 | /// Indicates that the method is contained in a type that implements 129 | /// System.ComponentModel.INotifyPropertyChanged interface and this method 130 | /// is used to notify that some property value changed. 131 | /// 132 | /// 133 | /// The method should be non-static and conform to one of the supported signatures: 134 | /// 135 | /// NotifyChanged(string) 136 | /// NotifyChanged(params string[]) 137 | /// NotifyChanged{T}(Expression{Func{T}}) 138 | /// NotifyChanged{T,U}(Expression{Func{T,U}}) 139 | /// SetProperty{T}(ref T, T, string) 140 | /// 141 | /// 142 | /// 143 | /// public class Foo : INotifyPropertyChanged { 144 | /// public event PropertyChangedEventHandler PropertyChanged; 145 | /// [NotifyPropertyChangedInvocator] 146 | /// protected virtual void NotifyChanged(string propertyName) { ... } 147 | /// 148 | /// private string _name; 149 | /// public string Name { 150 | /// get { return _name; } 151 | /// set { _name = value; NotifyChanged("LastName"); /* Warning */ } 152 | /// } 153 | /// } 154 | /// 155 | /// Examples of generated notifications: 156 | /// 157 | /// NotifyChanged("Property") 158 | /// NotifyChanged(() => Property) 159 | /// NotifyChanged((VM x) => x.Property) 160 | /// SetProperty(ref myField, value, "Property") 161 | /// 162 | /// 163 | [AttributeUsage(AttributeTargets.Method)] 164 | public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute 165 | { 166 | public NotifyPropertyChangedInvocatorAttribute() 167 | { 168 | } 169 | 170 | public NotifyPropertyChangedInvocatorAttribute(string parameterName) 171 | { 172 | ParameterName = parameterName; 173 | } 174 | 175 | public string ParameterName { get; private set; } 176 | } 177 | 178 | /// 179 | /// Describes dependency between method input and output. 180 | /// 181 | /// 182 | ///

Function Definition Table syntax:

183 | /// 184 | /// FDT ::= FDTRow [;FDTRow]* 185 | /// FDTRow ::= Input => Output | Output <= Input 186 | /// Input ::= ParameterName: Value [, Input]* 187 | /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} 188 | /// Value ::= true | false | null | notnull | canbenull 189 | /// 190 | /// If method has single input parameter, it's name could be omitted.
191 | /// Using halt (or void/nothing, which is the same) 192 | /// for method output means that the methos doesn't return normally.
193 | /// canbenull annotation is only applicable for output parameters.
194 | /// You can use multiple [ContractAnnotation] for each FDT row, 195 | /// or use single attribute with rows separated by semicolon.
196 | ///
197 | /// 198 | /// 199 | /// [ContractAnnotation("=> halt")] 200 | /// public void TerminationMethod() 201 | /// 202 | /// 203 | /// [ContractAnnotation("halt <= condition: false")] 204 | /// public void Assert(bool condition, string text) // regular assertion method 205 | /// 206 | /// 207 | /// [ContractAnnotation("s:null => true")] 208 | /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty() 209 | /// 210 | /// 211 | /// // A method that returns null if the parameter is null, 212 | /// // and not null if the parameter is not null 213 | /// [ContractAnnotation("null => null; notnull => notnull")] 214 | /// public object Transform(object data) 215 | /// 216 | /// 217 | /// [ContractAnnotation("s:null=>false; =>true,result:notnull; =>false, result:null")] 218 | /// public bool TryParse(string s, out Person result) 219 | /// 220 | /// 221 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] 222 | public sealed class ContractAnnotationAttribute : Attribute 223 | { 224 | public ContractAnnotationAttribute([NotNull] string contract) 225 | : this(contract, false) 226 | { 227 | } 228 | 229 | public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) 230 | { 231 | Contract = contract; 232 | ForceFullStates = forceFullStates; 233 | } 234 | 235 | public string Contract { get; private set; } 236 | public bool ForceFullStates { get; private set; } 237 | } 238 | 239 | /// 240 | /// Indicates that marked element should be localized or not. 241 | /// 242 | /// 243 | /// [LocalizationRequiredAttribute(true)] 244 | /// public class Foo { 245 | /// private string str = "my string"; // Warning: Localizable string 246 | /// } 247 | /// 248 | [AttributeUsage(AttributeTargets.All)] 249 | public sealed class LocalizationRequiredAttribute : Attribute 250 | { 251 | public LocalizationRequiredAttribute() : this(true) 252 | { 253 | } 254 | 255 | public LocalizationRequiredAttribute(bool required) 256 | { 257 | Required = required; 258 | } 259 | 260 | public bool Required { get; private set; } 261 | } 262 | 263 | /// 264 | /// Indicates that the value of the marked type (or its derivatives) 265 | /// cannot be compared using '==' or '!=' operators and Equals() 266 | /// should be used instead. However, using '==' or '!=' for comparison 267 | /// with null is always permitted. 268 | /// 269 | /// 270 | /// [CannotApplyEqualityOperator] 271 | /// class NoEquality { } 272 | /// class UsesNoEquality { 273 | /// public void Test() { 274 | /// var ca1 = new NoEquality(); 275 | /// var ca2 = new NoEquality(); 276 | /// if (ca1 != null) { // OK 277 | /// bool condition = ca1 == ca2; // Warning 278 | /// } 279 | /// } 280 | /// } 281 | /// 282 | [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] 283 | public sealed class CannotApplyEqualityOperatorAttribute : Attribute 284 | { 285 | } 286 | 287 | /// 288 | /// When applied to a target attribute, specifies a requirement for any type marked 289 | /// with the target attribute to implement or inherit specific type or types. 290 | /// 291 | /// 292 | /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement 293 | /// public class ComponentAttribute : Attribute { } 294 | /// [Component] // ComponentAttribute requires implementing IComponent interface 295 | /// public class MyComponent : IComponent { } 296 | /// 297 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 298 | [BaseTypeRequired(typeof (Attribute))] 299 | public sealed class BaseTypeRequiredAttribute : Attribute 300 | { 301 | public BaseTypeRequiredAttribute([NotNull] Type baseType) 302 | { 303 | BaseType = baseType; 304 | } 305 | 306 | [NotNull] 307 | public Type BaseType { get; private set; } 308 | } 309 | 310 | /// 311 | /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), 312 | /// so this symbol will not be marked as unused (as well as by other usage inspections). 313 | /// 314 | [AttributeUsage(AttributeTargets.All)] 315 | public sealed class UsedImplicitlyAttribute : Attribute 316 | { 317 | public UsedImplicitlyAttribute() 318 | : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) 319 | { 320 | } 321 | 322 | public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) 323 | : this(useKindFlags, ImplicitUseTargetFlags.Default) 324 | { 325 | } 326 | 327 | public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) 328 | : this(ImplicitUseKindFlags.Default, targetFlags) 329 | { 330 | } 331 | 332 | public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) 333 | { 334 | UseKindFlags = useKindFlags; 335 | TargetFlags = targetFlags; 336 | } 337 | 338 | public ImplicitUseKindFlags UseKindFlags { get; private set; } 339 | public ImplicitUseTargetFlags TargetFlags { get; private set; } 340 | } 341 | 342 | /// 343 | /// Should be used on attributes and causes ReSharper to not mark symbols marked with such attributes 344 | /// as unused (as well as by other usage inspections) 345 | /// 346 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter)] 347 | public sealed class MeansImplicitUseAttribute : Attribute 348 | { 349 | public MeansImplicitUseAttribute() 350 | : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) 351 | { 352 | } 353 | 354 | public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) 355 | : this(useKindFlags, ImplicitUseTargetFlags.Default) 356 | { 357 | } 358 | 359 | public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) 360 | : this(ImplicitUseKindFlags.Default, targetFlags) 361 | { 362 | } 363 | 364 | public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) 365 | { 366 | UseKindFlags = useKindFlags; 367 | TargetFlags = targetFlags; 368 | } 369 | 370 | [UsedImplicitly] 371 | public ImplicitUseKindFlags UseKindFlags { get; private set; } 372 | 373 | [UsedImplicitly] 374 | public ImplicitUseTargetFlags TargetFlags { get; private set; } 375 | } 376 | 377 | [Flags] 378 | public enum ImplicitUseKindFlags 379 | { 380 | Default = Access | Assign | InstantiatedWithFixedConstructorSignature, 381 | 382 | /// Only entity marked with attribute considered used. 383 | Access = 1, 384 | 385 | /// Indicates implicit assignment to a member. 386 | Assign = 2, 387 | 388 | /// 389 | /// Indicates implicit instantiation of a type with fixed constructor signature. 390 | /// That means any unused constructor parameters won't be reported as such. 391 | /// 392 | InstantiatedWithFixedConstructorSignature = 4, 393 | 394 | /// Indicates implicit instantiation of a type. 395 | InstantiatedNoFixedConstructorSignature = 8 396 | } 397 | 398 | /// 399 | /// Specify what is considered used implicitly when marked 400 | /// with or . 401 | /// 402 | [Flags] 403 | public enum ImplicitUseTargetFlags 404 | { 405 | Default = Itself, 406 | Itself = 1, 407 | 408 | /// Members of entity marked with attribute are considered used. 409 | Members = 2, 410 | 411 | /// Entity marked with attribute and all its members considered used. 412 | WithMembers = Itself | Members 413 | } 414 | 415 | /// 416 | /// This attribute is intended to mark publicly available API 417 | /// which should not be removed and so is treated as used. 418 | /// 419 | [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] 420 | public sealed class PublicAPIAttribute : Attribute 421 | { 422 | public PublicAPIAttribute() 423 | { 424 | } 425 | 426 | public PublicAPIAttribute([NotNull] string comment) 427 | { 428 | Comment = comment; 429 | } 430 | 431 | public string Comment { get; private set; } 432 | } 433 | 434 | /// 435 | /// Tells code analysis engine if the parameter is completely handled when the invoked method is on stack. 436 | /// If the parameter is a delegate, indicates that delegate is executed while the method is executed. 437 | /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. 438 | /// 439 | [AttributeUsage(AttributeTargets.Parameter)] 440 | public sealed class InstantHandleAttribute : Attribute 441 | { 442 | } 443 | 444 | /// 445 | /// Indicates that a method does not make any observable state changes. 446 | /// The same as System.Diagnostics.Contracts.PureAttribute. 447 | /// 448 | /// 449 | /// [Pure] private int Multiply(int x, int y) { return x * y; } 450 | /// public void Foo() { 451 | /// const int a = 2, b = 2; 452 | /// Multiply(a, b); // Waring: Return value of pure method is not used 453 | /// } 454 | /// 455 | [AttributeUsage(AttributeTargets.Method)] 456 | public sealed class PureAttribute : Attribute 457 | { 458 | } 459 | 460 | /// 461 | /// Indicates that a parameter is a path to a file or a folder within a web project. 462 | /// Path can be relative or absolute, starting from web root (~). 463 | /// 464 | [AttributeUsage(AttributeTargets.Parameter)] 465 | public sealed class PathReferenceAttribute : Attribute 466 | { 467 | public PathReferenceAttribute() 468 | { 469 | } 470 | 471 | public PathReferenceAttribute([PathReference] string basePath) 472 | { 473 | BasePath = basePath; 474 | } 475 | 476 | public string BasePath { get; private set; } 477 | } 478 | 479 | /// 480 | /// An extension method marked with this attribute is processed by ReSharper code completion 481 | /// as a 'Source Template'. When extension method is completed over some expression, it's source code 482 | /// is automatically expanded like a template at call site. 483 | /// 484 | /// 485 | /// Template method body can contain valid source code and/or special comments starting with '$'. 486 | /// Text inside these comments is added as source code when the template is applied. Template parameters 487 | /// can be used either as additional method parameters or as identifiers wrapped in two '$' signs. 488 | /// Use the attribute to specify macros for parameters. 489 | /// 490 | /// 491 | /// In this example, the 'forEach' method is a source template available over all values 492 | /// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block: 493 | /// 494 | /// [SourceTemplate] 495 | /// public static void forEach<T>(this IEnumerable<T> xs) { 496 | /// foreach (var x in xs) { 497 | /// //$ $END$ 498 | /// } 499 | /// } 500 | /// 501 | /// 502 | [AttributeUsage(AttributeTargets.Method)] 503 | public sealed class SourceTemplateAttribute : Attribute 504 | { 505 | } 506 | 507 | /// 508 | /// Allows specifying a macro for a parameter of a source template. 509 | /// 510 | /// 511 | /// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression 512 | /// is defined in the property. When applied on a method, the target 513 | /// template parameter is defined in the property. To apply the macro silently 514 | /// for the parameter, set the property value = -1. 515 | /// 516 | /// 517 | /// Applying the attribute on a source template method: 518 | /// 519 | /// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")] 520 | /// public static void forEach<T>(this IEnumerable<T> collection) { 521 | /// foreach (var item in collection) { 522 | /// //$ $END$ 523 | /// } 524 | /// } 525 | /// 526 | /// Applying the attribute on a template method parameter: 527 | /// 528 | /// [SourceTemplate] 529 | /// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) { 530 | /// /*$ var $x$Id = "$newguid$" + x.ToString(); 531 | /// x.DoSomething($x$Id); */ 532 | /// } 533 | /// 534 | /// 535 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)] 536 | public sealed class MacroAttribute : Attribute 537 | { 538 | /// 539 | /// Allows specifying a macro that will be executed for a source template 540 | /// parameter when the template is expanded. 541 | /// 542 | public string Expression { get; set; } 543 | 544 | /// 545 | /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed. 546 | /// 547 | /// 548 | /// If the target parameter is used several times in the template, only one occurrence becomes editable; 549 | /// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence, 550 | /// use values >= 0. To make the parameter non-editable when the template is expanded, use -1. 551 | /// > 552 | public int Editable { get; set; } 553 | 554 | /// 555 | /// Identifies the target parameter of a source template if the 556 | /// is applied on a template method. 557 | /// 558 | public string Target { get; set; } 559 | } 560 | 561 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 562 | public sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute 563 | { 564 | public AspMvcAreaMasterLocationFormatAttribute(string format) 565 | { 566 | Format = format; 567 | } 568 | 569 | public string Format { get; private set; } 570 | } 571 | 572 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 573 | public sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute 574 | { 575 | public AspMvcAreaPartialViewLocationFormatAttribute(string format) 576 | { 577 | Format = format; 578 | } 579 | 580 | public string Format { get; private set; } 581 | } 582 | 583 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 584 | public sealed class AspMvcAreaViewLocationFormatAttribute : Attribute 585 | { 586 | public AspMvcAreaViewLocationFormatAttribute(string format) 587 | { 588 | Format = format; 589 | } 590 | 591 | public string Format { get; private set; } 592 | } 593 | 594 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 595 | public sealed class AspMvcMasterLocationFormatAttribute : Attribute 596 | { 597 | public AspMvcMasterLocationFormatAttribute(string format) 598 | { 599 | Format = format; 600 | } 601 | 602 | public string Format { get; private set; } 603 | } 604 | 605 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 606 | public sealed class AspMvcPartialViewLocationFormatAttribute : Attribute 607 | { 608 | public AspMvcPartialViewLocationFormatAttribute(string format) 609 | { 610 | Format = format; 611 | } 612 | 613 | public string Format { get; private set; } 614 | } 615 | 616 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 617 | public sealed class AspMvcViewLocationFormatAttribute : Attribute 618 | { 619 | public AspMvcViewLocationFormatAttribute(string format) 620 | { 621 | Format = format; 622 | } 623 | 624 | public string Format { get; private set; } 625 | } 626 | 627 | /// 628 | /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter 629 | /// is an MVC action. If applied to a method, the MVC action name is calculated 630 | /// implicitly from the context. Use this attribute for custom wrappers similar to 631 | /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). 632 | /// 633 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] 634 | public sealed class AspMvcActionAttribute : Attribute 635 | { 636 | public AspMvcActionAttribute() 637 | { 638 | } 639 | 640 | public AspMvcActionAttribute(string anonymousProperty) 641 | { 642 | AnonymousProperty = anonymousProperty; 643 | } 644 | 645 | public string AnonymousProperty { get; private set; } 646 | } 647 | 648 | /// 649 | /// ASP.NET MVC attribute. Indicates that a parameter is an MVC area. 650 | /// Use this attribute for custom wrappers similar to 651 | /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). 652 | /// 653 | [AttributeUsage(AttributeTargets.Parameter)] 654 | public sealed class AspMvcAreaAttribute : Attribute 655 | { 656 | public AspMvcAreaAttribute() 657 | { 658 | } 659 | 660 | public AspMvcAreaAttribute(string anonymousProperty) 661 | { 662 | AnonymousProperty = anonymousProperty; 663 | } 664 | 665 | public string AnonymousProperty { get; private set; } 666 | } 667 | 668 | /// 669 | /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is 670 | /// an MVC controller. If applied to a method, the MVC controller name is calculated 671 | /// implicitly from the context. Use this attribute for custom wrappers similar to 672 | /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String). 673 | /// 674 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] 675 | public sealed class AspMvcControllerAttribute : Attribute 676 | { 677 | public AspMvcControllerAttribute() 678 | { 679 | } 680 | 681 | public AspMvcControllerAttribute(string anonymousProperty) 682 | { 683 | AnonymousProperty = anonymousProperty; 684 | } 685 | 686 | public string AnonymousProperty { get; private set; } 687 | } 688 | 689 | /// 690 | /// ASP.NET MVC attribute. Indicates that a parameter is an MVC Master. Use this attribute 691 | /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, String). 692 | /// 693 | [AttributeUsage(AttributeTargets.Parameter)] 694 | public sealed class AspMvcMasterAttribute : Attribute 695 | { 696 | } 697 | 698 | /// 699 | /// ASP.NET MVC attribute. Indicates that a parameter is an MVC model type. Use this attribute 700 | /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, Object). 701 | /// 702 | [AttributeUsage(AttributeTargets.Parameter)] 703 | public sealed class AspMvcModelTypeAttribute : Attribute 704 | { 705 | } 706 | 707 | /// 708 | /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC 709 | /// partial view. If applied to a method, the MVC partial view name is calculated implicitly 710 | /// from the context. Use this attribute for custom wrappers similar to 711 | /// System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String). 712 | /// 713 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] 714 | public sealed class AspMvcPartialViewAttribute : Attribute 715 | { 716 | } 717 | 718 | /// 719 | /// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method. 720 | /// 721 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 722 | public sealed class AspMvcSupressViewErrorAttribute : Attribute 723 | { 724 | } 725 | 726 | /// 727 | /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. 728 | /// Use this attribute for custom wrappers similar to 729 | /// System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String). 730 | /// 731 | [AttributeUsage(AttributeTargets.Parameter)] 732 | public sealed class AspMvcDisplayTemplateAttribute : Attribute 733 | { 734 | } 735 | 736 | /// 737 | /// ASP.NET MVC attribute. Indicates that a parameter is an MVC editor template. 738 | /// Use this attribute for custom wrappers similar to 739 | /// System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String). 740 | /// 741 | [AttributeUsage(AttributeTargets.Parameter)] 742 | public sealed class AspMvcEditorTemplateAttribute : Attribute 743 | { 744 | } 745 | 746 | /// 747 | /// ASP.NET MVC attribute. Indicates that a parameter is an MVC template. 748 | /// Use this attribute for custom wrappers similar to 749 | /// System.ComponentModel.DataAnnotations.UIHintAttribute(System.String). 750 | /// 751 | [AttributeUsage(AttributeTargets.Parameter)] 752 | public sealed class AspMvcTemplateAttribute : Attribute 753 | { 754 | } 755 | 756 | /// 757 | /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter 758 | /// is an MVC view. If applied to a method, the MVC view name is calculated implicitly 759 | /// from the context. Use this attribute for custom wrappers similar to 760 | /// System.Web.Mvc.Controller.View(Object). 761 | /// 762 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] 763 | public sealed class AspMvcViewAttribute : Attribute 764 | { 765 | } 766 | 767 | /// 768 | /// ASP.NET MVC attribute. When applied to a parameter of an attribute, 769 | /// indicates that this parameter is an MVC action name. 770 | /// 771 | /// 772 | /// [ActionName("Foo")] 773 | /// public ActionResult Login(string returnUrl) { 774 | /// ViewBag.ReturnUrl = Url.Action("Foo"); // OK 775 | /// return RedirectToAction("Bar"); // Error: Cannot resolve action 776 | /// } 777 | /// 778 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] 779 | public sealed class AspMvcActionSelectorAttribute : Attribute 780 | { 781 | } 782 | 783 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] 784 | public sealed class HtmlElementAttributesAttribute : Attribute 785 | { 786 | public HtmlElementAttributesAttribute() 787 | { 788 | } 789 | 790 | public HtmlElementAttributesAttribute(string name) 791 | { 792 | Name = name; 793 | } 794 | 795 | public string Name { get; private set; } 796 | } 797 | 798 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] 799 | public sealed class HtmlAttributeValueAttribute : Attribute 800 | { 801 | public HtmlAttributeValueAttribute([NotNull] string name) 802 | { 803 | Name = name; 804 | } 805 | 806 | [NotNull] 807 | public string Name { get; private set; } 808 | } 809 | 810 | /// 811 | /// Razor attribute. Indicates that a parameter or a method is a Razor section. 812 | /// Use this attribute for custom wrappers similar to 813 | /// System.Web.WebPages.WebPageBase.RenderSection(String). 814 | /// 815 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] 816 | public sealed class RazorSectionAttribute : Attribute 817 | { 818 | } 819 | 820 | /// 821 | /// Indicates how method invocation affects content of the collection. 822 | /// 823 | [AttributeUsage(AttributeTargets.Method)] 824 | public sealed class CollectionAccessAttribute : Attribute 825 | { 826 | public CollectionAccessAttribute(CollectionAccessType collectionAccessType) 827 | { 828 | CollectionAccessType = collectionAccessType; 829 | } 830 | 831 | public CollectionAccessType CollectionAccessType { get; private set; } 832 | } 833 | 834 | [Flags] 835 | public enum CollectionAccessType 836 | { 837 | /// Method does not use or modify content of the collection. 838 | None = 0, 839 | 840 | /// Method only reads content of the collection but does not modify it. 841 | Read = 1, 842 | 843 | /// Method can change content of the collection but does not add new elements. 844 | ModifyExistingContent = 2, 845 | 846 | /// Method can add new elements to the collection. 847 | UpdatedContent = ModifyExistingContent | 4 848 | } 849 | 850 | /// 851 | /// Indicates that the marked method is assertion method, i.e. it halts control flow if 852 | /// one of the conditions is satisfied. To set the condition, mark one of the parameters with 853 | /// attribute. 854 | /// 855 | [AttributeUsage(AttributeTargets.Method)] 856 | public sealed class AssertionMethodAttribute : Attribute 857 | { 858 | } 859 | 860 | /// 861 | /// Indicates the condition parameter of the assertion method. The method itself should be 862 | /// marked by attribute. The mandatory argument of 863 | /// the attribute is the assertion type. 864 | /// 865 | [AttributeUsage(AttributeTargets.Parameter)] 866 | public sealed class AssertionConditionAttribute : Attribute 867 | { 868 | public AssertionConditionAttribute(AssertionConditionType conditionType) 869 | { 870 | ConditionType = conditionType; 871 | } 872 | 873 | public AssertionConditionType ConditionType { get; private set; } 874 | } 875 | 876 | /// 877 | /// Specifies assertion type. If the assertion method argument satisfies the condition, 878 | /// then the execution continues. Otherwise, execution is assumed to be halted. 879 | /// 880 | public enum AssertionConditionType 881 | { 882 | /// Marked parameter should be evaluated to true. 883 | IS_TRUE = 0, 884 | 885 | /// Marked parameter should be evaluated to false. 886 | IS_FALSE = 1, 887 | 888 | /// Marked parameter should be evaluated to null value. 889 | IS_NULL = 2, 890 | 891 | /// Marked parameter should be evaluated to not null value. 892 | IS_NOT_NULL = 3 893 | } 894 | 895 | /// 896 | /// Indicates that the marked method unconditionally terminates control flow execution. 897 | /// For example, it could unconditionally throw exception. 898 | /// 899 | [Obsolete("Use [ContractAnnotation('=> halt')] instead")] 900 | [AttributeUsage(AttributeTargets.Method)] 901 | public sealed class TerminatesProgramAttribute : Attribute 902 | { 903 | } 904 | 905 | /// 906 | /// Indicates that method is pure LINQ method, with postponed enumeration (like Enumerable.Select, 907 | /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters 908 | /// of delegate type by analyzing LINQ method chains. 909 | /// 910 | [AttributeUsage(AttributeTargets.Method)] 911 | public sealed class LinqTunnelAttribute : Attribute 912 | { 913 | } 914 | 915 | /// 916 | /// Indicates that IEnumerable, passed as parameter, is not enumerated. 917 | /// 918 | [AttributeUsage(AttributeTargets.Parameter)] 919 | public sealed class NoEnumerationAttribute : Attribute 920 | { 921 | } 922 | 923 | /// 924 | /// Indicates that parameter is regular expression pattern. 925 | /// 926 | [AttributeUsage(AttributeTargets.Parameter)] 927 | public sealed class RegexPatternAttribute : Attribute 928 | { 929 | } 930 | 931 | /// 932 | /// XAML attribute. Indicates the type that has ItemsSource property and should be treated 933 | /// as ItemsControl-derived type, to enable inner items DataContext type resolve. 934 | /// 935 | [AttributeUsage(AttributeTargets.Class)] 936 | public sealed class XamlItemsControlAttribute : Attribute 937 | { 938 | } 939 | 940 | /// 941 | /// XAML attibute. Indicates the property of some BindingBase-derived type, that 942 | /// is used to bind some item of ItemsControl-derived type. This annotation will 943 | /// enable the DataContext type resolve for XAML bindings for such properties. 944 | /// 945 | /// 946 | /// Property should have the tree ancestor of the ItemsControl type or 947 | /// marked with the attribute. 948 | /// 949 | [AttributeUsage(AttributeTargets.Property)] 950 | public sealed class XamlItemBindingOfItemsControlAttribute : Attribute 951 | { 952 | } 953 | 954 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 955 | public sealed class AspChildControlTypeAttribute : Attribute 956 | { 957 | public AspChildControlTypeAttribute(string tagName, Type controlType) 958 | { 959 | TagName = tagName; 960 | ControlType = controlType; 961 | } 962 | 963 | public string TagName { get; private set; } 964 | public Type ControlType { get; private set; } 965 | } 966 | 967 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] 968 | public sealed class AspDataFieldAttribute : Attribute 969 | { 970 | } 971 | 972 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] 973 | public sealed class AspDataFieldsAttribute : Attribute 974 | { 975 | } 976 | 977 | [AttributeUsage(AttributeTargets.Property)] 978 | public sealed class AspMethodPropertyAttribute : Attribute 979 | { 980 | } 981 | 982 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 983 | public sealed class AspRequiredAttributeAttribute : Attribute 984 | { 985 | public AspRequiredAttributeAttribute([NotNull] string attribute) 986 | { 987 | Attribute = attribute; 988 | } 989 | 990 | public string Attribute { get; private set; } 991 | } 992 | 993 | [AttributeUsage(AttributeTargets.Property)] 994 | public sealed class AspTypePropertyAttribute : Attribute 995 | { 996 | public AspTypePropertyAttribute(bool createConstructorReferences) 997 | { 998 | CreateConstructorReferences = createConstructorReferences; 999 | } 1000 | 1001 | public bool CreateConstructorReferences { get; private set; } 1002 | } 1003 | 1004 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 1005 | public sealed class RazorImportNamespaceAttribute : Attribute 1006 | { 1007 | public RazorImportNamespaceAttribute(string name) 1008 | { 1009 | Name = name; 1010 | } 1011 | 1012 | public string Name { get; private set; } 1013 | } 1014 | 1015 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 1016 | public sealed class RazorInjectionAttribute : Attribute 1017 | { 1018 | public RazorInjectionAttribute(string type, string fieldName) 1019 | { 1020 | Type = type; 1021 | FieldName = fieldName; 1022 | } 1023 | 1024 | public string Type { get; private set; } 1025 | public string FieldName { get; private set; } 1026 | } 1027 | 1028 | [AttributeUsage(AttributeTargets.Method)] 1029 | public sealed class RazorHelperCommonAttribute : Attribute 1030 | { 1031 | } 1032 | 1033 | [AttributeUsage(AttributeTargets.Property)] 1034 | public sealed class RazorLayoutAttribute : Attribute 1035 | { 1036 | } 1037 | 1038 | [AttributeUsage(AttributeTargets.Method)] 1039 | public sealed class RazorWriteLiteralMethodAttribute : Attribute 1040 | { 1041 | } 1042 | 1043 | [AttributeUsage(AttributeTargets.Method)] 1044 | public sealed class RazorWriteMethodAttribute : Attribute 1045 | { 1046 | } 1047 | 1048 | [AttributeUsage(AttributeTargets.Parameter)] 1049 | public sealed class RazorWriteMethodParameterAttribute : Attribute 1050 | { 1051 | } 1052 | 1053 | /// 1054 | /// Prevents the Member Reordering feature from tossing members of the marked class. 1055 | /// 1056 | /// 1057 | /// The attribute must be mentioned in your member reordering patterns 1058 | /// 1059 | [AttributeUsage(AttributeTargets.All)] 1060 | public sealed class NoReorder : Attribute 1061 | { 1062 | } 1063 | } -------------------------------------------------------------------------------- /UnityRefactorHelper/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | 8 | [assembly: AssemblyTitle("UnityRefactorHelper")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("UnityRefactorHelper")] 13 | [assembly: AssemblyCopyright("")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | 21 | [assembly: ComVisible(false)] 22 | 23 | // Version information for an assembly consists of the following four values: 24 | // 25 | // Major Version 26 | // Minor Version 27 | // Build Number 28 | // Revision 29 | // 30 | // You can specify all the values or you can default the Build and Revision Numbers 31 | // by using the '*' as shown below: 32 | // [assembly: AssemblyVersion("1.0.*")] 33 | 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /UnityRefactorHelper/Resources/CommandIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PDarkTemplar/UnityRefactorHelper/9b288bc7d6dff152f73bb7cc65424959d9bace13/UnityRefactorHelper/Resources/CommandIcon.png -------------------------------------------------------------------------------- /UnityRefactorHelper/Resources/MainIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PDarkTemplar/UnityRefactorHelper/9b288bc7d6dff152f73bb7cc65424959d9bace13/UnityRefactorHelper/Resources/MainIcon.png -------------------------------------------------------------------------------- /UnityRefactorHelper/Service/CommonService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.Design; 4 | using System.IO; 5 | using Newtonsoft.Json; 6 | using UnityRefactorHelper.Helpers; 7 | using UnityRefactorHelper.Model; 8 | using UnityRefactorHelper.ViewModel; 9 | 10 | namespace UnityRefactorHelper.Service 11 | { 12 | public static class CommonService 13 | { 14 | private const string SettingsFileName = "UnityRefactorSettings.json"; 15 | private const string SettingsCacheFileName = "UnityRefactorSettingsCache.bin"; 16 | 17 | public static void HandleMenuButton(bool enabled) 18 | { 19 | var mc = 20 | Cache.Instance.OleMenuCommandService.FindCommand(new CommandID(Constants.CommandSet, Constants.CommandId)); 21 | if (mc != null) 22 | mc.Enabled = enabled; 23 | ToolWindowViewModel.Instance.SolutionLoaded = enabled; 24 | } 25 | 26 | public static void SaveSettings() 27 | { 28 | var dte = Cache.Instance.Dte; 29 | 30 | var path = Path.Combine(Path.GetDirectoryName(dte.Solution.FullName), SettingsFileName); 31 | File.WriteAllText(path, JsonConvert.SerializeObject(Cache.Instance.Settings)); 32 | } 33 | 34 | public static void LoadSettings() 35 | { 36 | var dte = Cache.Instance.Dte; 37 | 38 | var path = Path.Combine(Path.GetDirectoryName(dte.Solution.FullName), SettingsFileName); 39 | if (File.Exists(path)) 40 | { 41 | var content = File.ReadAllText(path); 42 | Cache.Instance.Settings = JsonConvert.DeserializeObject(content); 43 | } 44 | } 45 | 46 | public static void SaveSyncProjectItemsToCache() 47 | { 48 | var dte = Cache.Instance.Dte; 49 | var scanProjects = Cache.Instance.ScanProjects; 50 | var path = Path.Combine(Path.GetDirectoryName(dte.Solution.FullName), SettingsCacheFileName); 51 | File.WriteAllText(path, JsonConvert.SerializeObject(scanProjects)); 52 | } 53 | 54 | public static void LoadSyncProjectsCache() 55 | { 56 | var dte = Cache.Instance.Dte; 57 | var path = Path.Combine(Path.GetDirectoryName(dte.Solution.FullName), SettingsCacheFileName); 58 | if (File.Exists(path)) 59 | { 60 | var content = File.ReadAllText(path); 61 | Cache.Instance.ScanProjects = JsonConvert.DeserializeObject>(content); 62 | } 63 | } 64 | 65 | public static void UpdateUnityProject(string vsProject) 66 | { 67 | if(!Cache.Instance.Settings.IsEnabled) return; 68 | var projectName = Path.GetFileNameWithoutExtension(vsProject); 69 | var replacer = new UnityProjectFileIdReplacer(projectName); 70 | replacer.Replace(); 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /UnityRefactorHelper/Service/ProjectService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using EnvDTE; 6 | using EnvDTE80; 7 | using Microsoft.VisualStudio; 8 | using Microsoft.VisualStudio.Shell.Interop; 9 | using UnityRefactorHelper.Helpers; 10 | using UnityRefactorHelper.Model; 11 | 12 | namespace UnityRefactorHelper.Service 13 | { 14 | public static class ProjectService 15 | { 16 | private const string PersistentFileGuidName = "PersistentFileGuid"; 17 | 18 | public static void ProcessProject(Project project) 19 | { 20 | var syncItem = Cache.Instance.ScanProjects.FirstOrDefault(x => x.ProjectName == project.Name); 21 | if (syncItem == null) return; 22 | 23 | IVsHierarchy projectHierarchy; 24 | 25 | if (Cache.Instance.SolutionService.GetProjectOfUniqueName(project.UniqueName, out projectHierarchy) == 26 | VSConstants.S_OK) 27 | { 28 | if (projectHierarchy != null) 29 | { 30 | var fileGuids = NavigateProjectItems(project.ProjectItems, syncItem, projectHierarchy); 31 | var notFound = syncItem.Files.Where(x => !fileGuids.Contains(x.ProjectFileId)).ToList(); 32 | 33 | foreach (var nf in notFound) 34 | { 35 | syncItem.Files.Remove(nf); 36 | } 37 | } 38 | } 39 | } 40 | public static void ProcessNewProject(ProjectSyncItem newItem) 41 | { 42 | var project = Projects().FirstOrDefault(x => x.Name == newItem.ProjectName); 43 | if (project != null) 44 | ProcessProject(project); 45 | } 46 | 47 | public static void DocumentSaved(Document document) 48 | { 49 | if (!Cache.Instance.Settings.IsEnabled) return; 50 | 51 | var item = document?.ProjectItem; 52 | 53 | var project = item?.ContainingProject; 54 | if (project != null) 55 | { 56 | var syncItem = Cache.Instance.ScanProjects.FirstOrDefault(x => x.ProjectName == project.Name); 57 | if (syncItem == null) return; 58 | 59 | IVsHierarchy projectHierarchy; 60 | 61 | if (Cache.Instance.SolutionService.GetProjectOfUniqueName(project.UniqueName, out projectHierarchy) == 62 | VSConstants.S_OK) 63 | { 64 | if (projectHierarchy != null) 65 | { 66 | ExamineProjectItem(item, syncItem, projectHierarchy); 67 | } 68 | } 69 | } 70 | } 71 | 72 | private static IEnumerable NavigateProjectItems(ProjectItems projectItems, ProjectScanItem projectScan, 73 | IVsHierarchy projectHierarchy) 74 | { 75 | var fileGuids = new List(); 76 | 77 | if (projectItems == null) 78 | return fileGuids; 79 | 80 | foreach (ProjectItem item in projectItems) 81 | { 82 | fileGuids.AddRange(NavigateProjectItems(item.ProjectItems, projectScan, projectHierarchy)); 83 | 84 | if (item.Kind != "{6BB5F8EE-4483-11D3-8BCF-00C04F8EC28C}") // VSConstants.GUID_ItemType_PhysicalFile 85 | continue; 86 | 87 | var fileGuid = ExamineProjectItem(item, projectScan, projectHierarchy); 88 | if (!string.IsNullOrEmpty(fileGuid)) 89 | fileGuids.Add(fileGuid); 90 | } 91 | 92 | return fileGuids; 93 | } 94 | 95 | private static string ExamineProjectItem(ProjectItem item, ProjectScanItem projectScan, 96 | IVsHierarchy projectHierarchy) 97 | { 98 | var fileName = item.FileNames[1]; 99 | if (Path.GetExtension(fileName) != ".cs") return null; 100 | 101 | var code = item.FileCodeModel; 102 | if (code == null) return null; 103 | 104 | var className = string.Empty; 105 | var namespaceName = string.Empty; 106 | 107 | foreach (CodeElement codeElement in code.CodeElements) 108 | { 109 | if (string.IsNullOrEmpty(className)) 110 | className = ExamineCodeElement(codeElement, vsCMElement.vsCMElementClass); 111 | if (string.IsNullOrEmpty(namespaceName)) 112 | namespaceName = ExamineCodeElement(codeElement, vsCMElement.vsCMElementNamespace); 113 | 114 | if (!string.IsNullOrEmpty(className) && !string.IsNullOrEmpty(namespaceName)) 115 | break; 116 | } 117 | 118 | if (string.IsNullOrEmpty(className)) return null; 119 | uint itemId; 120 | if (projectHierarchy.ParseCanonicalName(item.FileNames[0], out itemId) == VSConstants.S_OK) 121 | { 122 | var buildPropertyStorage = 123 | projectHierarchy as IVsBuildPropertyStorage; 124 | 125 | if (buildPropertyStorage == null) return null; 126 | string fileGuid; 127 | buildPropertyStorage.GetItemAttribute(itemId, PersistentFileGuidName, out fileGuid); 128 | if (string.IsNullOrEmpty(fileGuid)) 129 | { 130 | fileGuid = Guid.NewGuid().ToString(); 131 | buildPropertyStorage.SetItemAttribute(itemId, PersistentFileGuidName, fileGuid); 132 | item.ContainingProject.Save(); 133 | } 134 | 135 | if (string.IsNullOrEmpty(fileGuid)) return null; 136 | 137 | var file = projectScan.Files.FirstOrDefault(x => x.ProjectFileId == fileGuid); 138 | 139 | var fileId = FileIdGenerator.Compute(namespaceName, className); 140 | 141 | if (file == null) 142 | { 143 | projectScan.Files.Add(new FileScanItem { ProjectFileId = fileGuid, OldId = fileId }); 144 | } 145 | else 146 | { 147 | var newId = fileId; 148 | if (file.OldId != newId) 149 | file.NewId = newId; 150 | } 151 | 152 | return fileGuid; 153 | } 154 | 155 | return null; 156 | } 157 | 158 | private static string ExamineCodeElement(CodeElement codeElement, vsCMElement type) 159 | { 160 | return codeElement.Kind == type 161 | ? codeElement.Name 162 | : (from CodeElement childElement in codeElement.Children select ExamineCodeElement(childElement, type)) 163 | .FirstOrDefault(result => !string.IsNullOrEmpty(result)); 164 | } 165 | 166 | private static IEnumerable Projects() 167 | { 168 | var projects = Cache.Instance.Dte.Solution.Projects; 169 | var list = new List(); 170 | var item = projects.GetEnumerator(); 171 | while (item.MoveNext()) 172 | { 173 | var project = item.Current as Project; 174 | if (project == null) 175 | { 176 | continue; 177 | } 178 | 179 | if (project.Kind == ProjectKinds.vsProjectKindSolutionFolder) 180 | { 181 | list.AddRange(GetSolutionFolderProjects(project)); 182 | } 183 | else 184 | { 185 | list.Add(project); 186 | } 187 | } 188 | 189 | return list; 190 | } 191 | 192 | private static IEnumerable GetSolutionFolderProjects(Project solutionFolder) 193 | { 194 | var list = new List(); 195 | for (var i = 1; i <= solutionFolder.ProjectItems.Count; i++) 196 | { 197 | var subProject = solutionFolder.ProjectItems.Item(i).SubProject; 198 | if (subProject == null) 199 | { 200 | continue; 201 | } 202 | 203 | // If this is another solution folder, do a recursive call, otherwise add 204 | if (subProject.Kind == ProjectKinds.vsProjectKindSolutionFolder) 205 | { 206 | list.AddRange(GetSolutionFolderProjects(subProject)); 207 | } 208 | else 209 | { 210 | list.Add(subProject); 211 | } 212 | } 213 | return list; 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /UnityRefactorHelper/SettingsToolWindow.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using Microsoft.VisualStudio.Shell; 3 | using UnityRefactorHelper.View; 4 | 5 | namespace UnityRefactorHelper 6 | { 7 | [Guid("5825C503-1846-4B31-95ED-716E24E5E9E4")] 8 | public sealed class SettingsToolWindow : ToolWindowPane 9 | { 10 | public SettingsToolWindow() : 11 | base(null) 12 | { 13 | Caption = "Unity Refactor Helper"; 14 | 15 | Content = new UnityRefactorHelperSettingsControl(); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /UnityRefactorHelper/SolutionEvents.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using EnvDTE; 4 | using Microsoft.VisualStudio; 5 | using Microsoft.VisualStudio.Shell.Interop; 6 | using UnityRefactorHelper.Service; 7 | using UnityRefactorHelper.ViewModel; 8 | 9 | namespace UnityRefactorHelper 10 | { 11 | public class SolutionEvents : IVsSolutionEvents, IVsSolutionLoadEvents 12 | { 13 | public int OnAfterOpenSolution(object pUnkReserved, int fNewSolution) 14 | { 15 | ToolWindowViewModel.Instance.LoadSettings(); 16 | return VSConstants.S_OK; 17 | } 18 | 19 | public int OnAfterCloseSolution(object pUnkReserved) 20 | { 21 | CommonService.HandleMenuButton(false); 22 | return VSConstants.S_OK; 23 | } 24 | 25 | public int OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded) 26 | { 27 | var project = GetProjectFromHierarchy(pHierarchy); 28 | if (!string.IsNullOrEmpty(project?.FileName)) 29 | { 30 | ToolWindowViewModel.Instance.ProjectNames.Add(Path.GetFileNameWithoutExtension(project.FileName)); 31 | ProjectService.ProcessProject(project); 32 | } 33 | return VSConstants.S_OK; 34 | } 35 | 36 | public int OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved) 37 | { 38 | var project = GetProjectFromHierarchy(pHierarchy); 39 | if (project != null) 40 | { 41 | var name = Path.GetFileNameWithoutExtension(project.FileName); 42 | var item = ToolWindowViewModel.Instance.ProjectNames.FirstOrDefault(x => x == name); 43 | if (item != null) 44 | { 45 | ToolWindowViewModel.Instance.ProjectNames.Remove(item); 46 | } 47 | } 48 | return VSConstants.S_OK; 49 | } 50 | 51 | public int OnAfterBackgroundSolutionLoadComplete() 52 | { 53 | CommonService.HandleMenuButton(true); 54 | return VSConstants.S_OK; 55 | } 56 | 57 | private Project GetProjectFromHierarchy(IVsHierarchy pHierarchy) 58 | { 59 | const uint itemid = VSConstants.VSITEMID_ROOT; 60 | 61 | object objProj; 62 | pHierarchy.GetProperty(itemid, (int) __VSHPROPID.VSHPROPID_ExtObject, out objProj); 63 | var project = objProj as Project; 64 | return project; 65 | } 66 | 67 | #region Not used 68 | 69 | public int OnQueryCloseSolution(object pUnkReserved, ref int pfCancel) 70 | { 71 | return VSConstants.S_OK; 72 | } 73 | 74 | public int OnBeforeCloseSolution(object pUnkReserved) 75 | { 76 | return VSConstants.S_OK; 77 | } 78 | 79 | public int OnBeforeOpenSolution(string pszSolutionFilename) 80 | { 81 | return VSConstants.S_OK; 82 | } 83 | 84 | public int OnBeforeBackgroundSolutionLoadBegins() 85 | { 86 | return VSConstants.S_OK; 87 | } 88 | 89 | public int OnQueryBackgroundLoadProjectBatch(out bool pfShouldDelayLoadToNextIdle) 90 | { 91 | pfShouldDelayLoadToNextIdle = false; 92 | return VSConstants.S_OK; 93 | } 94 | 95 | public int OnBeforeLoadProjectBatch(bool fIsBackgroundIdleBatch) 96 | { 97 | return VSConstants.S_OK; 98 | } 99 | 100 | public int OnAfterLoadProjectBatch(bool fIsBackgroundIdleBatch) 101 | { 102 | return VSConstants.S_OK; 103 | } 104 | 105 | public int OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel) 106 | { 107 | return VSConstants.S_OK; 108 | } 109 | 110 | public int OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy) 111 | { 112 | return VSConstants.S_OK; 113 | } 114 | 115 | public int OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel) 116 | { 117 | return VSConstants.S_OK; 118 | } 119 | 120 | public int OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy) 121 | { 122 | return VSConstants.S_OK; 123 | } 124 | 125 | #endregion 126 | } 127 | } -------------------------------------------------------------------------------- /UnityRefactorHelper/UnityRefactorHelper.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 15.0 6 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 7 | 8 | 9 | true 10 | 11 | 12 | 13 | 14 | 14.0 15 | publish\ 16 | true 17 | Disk 18 | false 19 | Foreground 20 | 7 21 | Days 22 | false 23 | false 24 | true 25 | 0 26 | 1.0.0.%2a 27 | false 28 | false 29 | true 30 | 31 | 32 | true 33 | 34 | 35 | Key.snk 36 | 37 | 38 | 39 | Debug 40 | AnyCPU 41 | 2.0 42 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 43 | {D7D4741D-E917-4509-A620-ABE6A9573784} 44 | Library 45 | Properties 46 | UnityRefactorHelper 47 | UnityRefactorHelper 48 | v4.6 49 | true 50 | true 51 | true 52 | true 53 | true 54 | false 55 | 56 | 57 | true 58 | full 59 | false 60 | bin\Debug\ 61 | DEBUG;TRACE 62 | prompt 63 | 4 64 | 65 | 66 | pdbonly 67 | true 68 | bin\Release\ 69 | TRACE 70 | prompt 71 | 4 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | SyncProjectControl.xaml 90 | 91 | 92 | 93 | 94 | 95 | UnityRefactorHelperSettingsControl.xaml 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | Designer 105 | 106 | 107 | 108 | 109 | Designer 110 | MSBuild:Compile 111 | 112 | 113 | Designer 114 | MSBuild:Compile 115 | 116 | 117 | 118 | 119 | False 120 | 121 | 122 | False 123 | 124 | 125 | False 126 | 127 | 128 | False 129 | 130 | 131 | 132 | 133 | False 134 | 135 | 136 | ..\packages\Microsoft.VisualStudio.Imaging.15.0.26201\lib\net45\Microsoft.VisualStudio.Imaging.dll 137 | True 138 | 139 | 140 | ..\packages\Microsoft.VisualStudio.OLE.Interop.7.10.6070\lib\Microsoft.VisualStudio.OLE.Interop.dll 141 | True 142 | 143 | 144 | ..\packages\Microsoft.VisualStudio.Shell.14.0.14.3.25407\lib\Microsoft.VisualStudio.Shell.14.0.dll 145 | True 146 | 147 | 148 | ..\packages\Microsoft.VisualStudio.Shell.Immutable.10.0.10.0.30319\lib\net40\Microsoft.VisualStudio.Shell.Immutable.10.0.dll 149 | True 150 | 151 | 152 | ..\packages\Microsoft.VisualStudio.Shell.Immutable.11.0.11.0.50727\lib\net45\Microsoft.VisualStudio.Shell.Immutable.11.0.dll 153 | True 154 | 155 | 156 | ..\packages\Microsoft.VisualStudio.Shell.Immutable.12.0.12.0.21003\lib\net45\Microsoft.VisualStudio.Shell.Immutable.12.0.dll 157 | True 158 | 159 | 160 | ..\packages\Microsoft.VisualStudio.Shell.Immutable.14.0.14.3.25407\lib\net45\Microsoft.VisualStudio.Shell.Immutable.14.0.dll 161 | True 162 | 163 | 164 | ..\packages\Microsoft.VisualStudio.Shell.Interop.7.10.6071\lib\Microsoft.VisualStudio.Shell.Interop.dll 165 | True 166 | 167 | 168 | True 169 | ..\packages\Microsoft.VisualStudio.Shell.Interop.10.0.10.0.30319\lib\Microsoft.VisualStudio.Shell.Interop.10.0.dll 170 | True 171 | 172 | 173 | True 174 | ..\packages\Microsoft.VisualStudio.Shell.Interop.11.0.11.0.61030\lib\Microsoft.VisualStudio.Shell.Interop.11.0.dll 175 | True 176 | 177 | 178 | True 179 | ..\packages\Microsoft.VisualStudio.Shell.Interop.12.0.12.0.30110\lib\Microsoft.VisualStudio.Shell.Interop.12.0.dll 180 | True 181 | 182 | 183 | ..\packages\Microsoft.VisualStudio.Shell.Interop.8.0.8.0.50727\lib\Microsoft.VisualStudio.Shell.Interop.8.0.dll 184 | True 185 | 186 | 187 | ..\packages\Microsoft.VisualStudio.Shell.Interop.9.0.9.0.30729\lib\Microsoft.VisualStudio.Shell.Interop.9.0.dll 188 | True 189 | 190 | 191 | ..\packages\Microsoft.VisualStudio.TextManager.Interop.7.10.6070\lib\Microsoft.VisualStudio.TextManager.Interop.dll 192 | True 193 | 194 | 195 | ..\packages\Microsoft.VisualStudio.TextManager.Interop.8.0.8.0.50727\lib\Microsoft.VisualStudio.TextManager.Interop.8.0.dll 196 | True 197 | 198 | 199 | ..\packages\Microsoft.VisualStudio.Threading.15.0.240\lib\net45\Microsoft.VisualStudio.Threading.dll 200 | True 201 | 202 | 203 | ..\packages\Microsoft.VisualStudio.Utilities.15.0.26201\lib\net45\Microsoft.VisualStudio.Utilities.dll 204 | True 205 | 206 | 207 | ..\packages\Microsoft.VisualStudio.Validation.15.0.82\lib\net45\Microsoft.VisualStudio.Validation.dll 208 | True 209 | 210 | 211 | ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll 212 | 213 | 214 | 215 | 216 | False 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | Always 231 | true 232 | 233 | 234 | Menus.ctmenu 235 | 236 | 237 | 238 | 239 | true 240 | VSPackage 241 | Designer 242 | 243 | 244 | 245 | 246 | False 247 | Microsoft .NET Framework 4.6 %28x86 and x64%29 248 | true 249 | 250 | 251 | False 252 | .NET Framework 3.5 SP1 253 | false 254 | 255 | 256 | 257 | 258 | 259 | 260 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 261 | 262 | 263 | 264 | 265 | 266 | 273 | -------------------------------------------------------------------------------- /UnityRefactorHelper/UnityRefactorHelperSettingsCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Design; 3 | using Microsoft.VisualStudio; 4 | using Microsoft.VisualStudio.Shell; 5 | using Microsoft.VisualStudio.Shell.Interop; 6 | using UnityRefactorHelper.Helpers; 7 | 8 | namespace UnityRefactorHelper 9 | { 10 | /// 11 | /// Command handler 12 | /// 13 | internal sealed class UnityRefactorHelperSettingsCommand 14 | { 15 | /// 16 | /// Command ID. 17 | /// 18 | public const int CommandId = Constants.CommandId; 19 | 20 | /// 21 | /// Command menu group (command set GUID). 22 | /// 23 | public static readonly Guid CommandSet = Constants.CommandSet; 24 | 25 | /// 26 | /// VS Package that provides this command, not null. 27 | /// 28 | private readonly Package package; 29 | 30 | /// 31 | /// Initializes a new instance of the class. 32 | /// Adds our command handlers for menu (commands must exist in the command table file) 33 | /// 34 | /// Owner package, not null. 35 | private UnityRefactorHelperSettingsCommand(Package package) 36 | { 37 | if (package == null) 38 | { 39 | throw new ArgumentNullException("package"); 40 | } 41 | 42 | this.package = package; 43 | 44 | var commandService = ServiceProvider.GetService(typeof (IMenuCommandService)) as OleMenuCommandService; 45 | if (commandService != null) 46 | { 47 | var menuCommandID = new CommandID(CommandSet, CommandId); 48 | var menuItem = new MenuCommand(ShowToolWindow, menuCommandID); 49 | commandService.AddCommand(menuItem); 50 | } 51 | } 52 | 53 | /// 54 | /// Gets the instance of the command. 55 | /// 56 | public static UnityRefactorHelperSettingsCommand Instance { get; private set; } 57 | 58 | /// 59 | /// Gets the service provider from the owner package. 60 | /// 61 | private IServiceProvider ServiceProvider 62 | { 63 | get { return package; } 64 | } 65 | 66 | /// 67 | /// Initializes the singleton instance of the command. 68 | /// 69 | /// Owner package, not null. 70 | public static void Initialize(Package package) 71 | { 72 | Instance = new UnityRefactorHelperSettingsCommand(package); 73 | } 74 | 75 | /// 76 | /// Shows the tool window when the menu item is clicked. 77 | /// 78 | /// The event sender. 79 | /// The event args. 80 | private void ShowToolWindow(object sender, EventArgs e) 81 | { 82 | // Get the instance number 0 of this tool window. This window is single instance so this instance 83 | // is actually the only one. 84 | // The last flag is set to true so that if the tool window does not exists it will be created. 85 | var window = package.FindToolWindow(typeof (SettingsToolWindow), 0, true); 86 | if ((null == window) || (null == window.Frame)) 87 | { 88 | throw new NotSupportedException("Cannot create tool window"); 89 | } 90 | var windowFrame = (IVsWindowFrame) window.Frame; 91 | ErrorHandler.ThrowOnFailure(windowFrame.Show()); 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /UnityRefactorHelper/UnityRefactorHelperSettingsPackage.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Design; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Runtime.InteropServices; 4 | using EnvDTE; 5 | using Microsoft.VisualStudio; 6 | using Microsoft.VisualStudio.Shell; 7 | using Microsoft.VisualStudio.Shell.Interop; 8 | using UnityRefactorHelper.Helpers; 9 | using UnityRefactorHelper.Service; 10 | 11 | namespace UnityRefactorHelper 12 | { 13 | /// 14 | /// This is the class that implements the package exposed by this assembly. 15 | /// 16 | /// 17 | /// 18 | /// The minimum requirement for a class to be considered a valid package for Visual Studio 19 | /// is to implement the IVsPackage interface and register itself with the shell. 20 | /// This package uses the helper classes defined inside the Managed Package Framework (MPF) 21 | /// to do it: it derives from the Package class that provides the implementation of the 22 | /// IVsPackage interface and uses the registration attributes defined in the framework to 23 | /// register itself and its components with the shell. These attributes tell the pkgdef creation 24 | /// utility what data to put into .pkgdef file. 25 | /// 26 | /// 27 | /// To get loaded into VS, the package must be referred by <Asset Type="Microsoft.VisualStudio.VsPackage" ...> in .vsixmanifest file. 28 | /// 29 | /// 30 | [PackageRegistration(UseManagedResourcesOnly = true)] 31 | [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)] // Info on this package for Help/About 32 | [ProvideMenuResource("Menus.ctmenu", 1)] 33 | [ProvideToolWindow(typeof (SettingsToolWindow))] 34 | [Guid(PackageGuidString)] 35 | [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionExists_string)] 36 | [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", 37 | Justification = "pkgdef, VS and vsixmanifest are valid VS terms")] 38 | public sealed class UnityRefactorHelperSettingsPackage : Package 39 | { 40 | /// 41 | /// UnityRefactorHelperSettingsPackage GUID string. 42 | /// 43 | public const string PackageGuidString = "704c324b-a17d-45b6-bb8d-3aacff371f0a"; 44 | 45 | private BuildEvents _buildEvents; 46 | private DocumentEvents _documentEvents; 47 | private IVsSolutionEvents _solutionEvents; 48 | private IVsSolution _solution; 49 | private uint _solutionEventsCookie; 50 | 51 | #region Package Members 52 | 53 | /// 54 | /// Initialization of the package; this method is called right after the package is sited, so this is the place 55 | /// where you can put all the initialization code that rely on services provided by VisualStudio. 56 | /// 57 | protected override void Initialize() 58 | { 59 | UnityRefactorHelperSettingsCommand.Initialize(this); 60 | base.Initialize(); 61 | 62 | Cache.Instance.OleMenuCommandService = GetService(typeof (IMenuCommandService)) as OleMenuCommandService; 63 | Cache.Instance.Dte = (DTE) GetService(typeof (SDTE)); 64 | 65 | var events = Cache.Instance.Dte.Events; 66 | _documentEvents = events.DocumentEvents; 67 | _buildEvents = events.BuildEvents; 68 | 69 | Cache.Instance.SolutionService = GetService(typeof (IVsSolution)) as IVsSolution; 70 | 71 | _solutionEvents = new SolutionEvents(); 72 | _solution = GetService(typeof (SVsSolution)) as IVsSolution; 73 | _solution.AdviseSolutionEvents(_solutionEvents, out _solutionEventsCookie); 74 | 75 | _documentEvents.DocumentSaved += DocumentEventsOnDocumentSaved; 76 | _buildEvents.OnBuildProjConfigDone += BuildEventsOnOnBuildProjConfigDone; 77 | } 78 | 79 | private void BuildEventsOnOnBuildProjConfigDone(string project, string projectConfig, string platform, 80 | string solutionConfig, bool success) 81 | { 82 | if (success) 83 | CommonService.UpdateUnityProject(project); 84 | } 85 | 86 | protected override void Dispose(bool disposing) 87 | { 88 | UnadviseSolutionEvents(); 89 | Cache.Instance.Dispose(); 90 | _buildEvents = null; 91 | _documentEvents = null; 92 | _solutionEvents = null; 93 | base.Dispose(disposing); 94 | } 95 | 96 | private void UnadviseSolutionEvents() 97 | { 98 | if (_solution != null) 99 | { 100 | if (_solutionEventsCookie != uint.MaxValue) 101 | { 102 | _solution.UnadviseSolutionEvents(_solutionEventsCookie); 103 | _solutionEventsCookie = uint.MaxValue; 104 | } 105 | 106 | _solution = null; 107 | } 108 | } 109 | 110 | private void DocumentEventsOnDocumentSaved(Document document) 111 | { 112 | ProjectService.DocumentSaved(document); 113 | } 114 | 115 | #endregion 116 | } 117 | } -------------------------------------------------------------------------------- /UnityRefactorHelper/UnityRefactorHelperSettingsPackage.vsct: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 33 | 34 | 35 | 37 | 38 | 45 | 53 | 54 | 55 | 56 | 57 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /UnityRefactorHelper/VSPackage.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | UnityRefactorHelperSettings Extension 122 | 123 | 124 | UnityRefactorHelperSettings Visual Studio Extension Detailed Info 125 | 126 | -------------------------------------------------------------------------------- /UnityRefactorHelper/View/SyncProjectControl.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 32 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /UnityRefactorHelper/View/SyncProjectControl.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Runtime.CompilerServices; 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | using UnityRefactorHelper.Annotations; 7 | using UnityRefactorHelper.Model; 8 | 9 | namespace UnityRefactorHelper.View 10 | { 11 | /// 12 | /// Interaction logic for SyncProjectControl.xaml 13 | /// 14 | public partial class SyncProjectControl : UserControl, INotifyPropertyChanged 15 | { 16 | private ProjectSyncItem _item; 17 | 18 | public SyncProjectControl(ProjectSyncItem item) 19 | { 20 | Item = item; 21 | InitializeComponent(); 22 | } 23 | 24 | public ProjectSyncItem Item 25 | { 26 | get { return _item; } 27 | set 28 | { 29 | _item = value; 30 | OnPropertyChanged(nameof(LabelText)); 31 | } 32 | } 33 | 34 | public string LabelText => $"{Item.ProjectName} : {Item.ProjectGuid}"; 35 | public event PropertyChangedEventHandler PropertyChanged; 36 | public event EventHandler OnSyncItemDelete; 37 | public event EventHandler OnSyncItemClick; 38 | 39 | private void removeBtn_Click(object sender, RoutedEventArgs e) 40 | { 41 | var messageBoxResult = MessageBox.Show("Are you sure?", "Delete Confirmation", MessageBoxButton.YesNo); 42 | if (messageBoxResult != MessageBoxResult.Yes) return; 43 | OnSyncItemDelete?.Invoke(this, EventArgs.Empty); 44 | } 45 | 46 | private void syncBtn_Click(object sender, RoutedEventArgs e) 47 | { 48 | OnSyncItemClick?.Invoke(Item, EventArgs.Empty); 49 | } 50 | 51 | [NotifyPropertyChangedInvocator] 52 | protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 53 | { 54 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /UnityRefactorHelper/View/UnityRefactorHelperSettingsControl.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 15 | 19 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |