├── .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 |
14 |
Enable – start extension
15 |
Unity project folder - path to the Assets folder in Unity project
16 |
Projects - select monitored library
17 |
Project GUID – Get this GUID from the assembly Meta file in Unity folder
18 |
Watch projects - tracked libraries
19 |
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 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/UnityRefactorHelper/View/UnityRefactorHelperSettingsControl.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Controls;
3 | using UnityRefactorHelper.ViewModel;
4 |
5 | namespace UnityRefactorHelper.View
6 | {
7 | ///
8 | /// Interaction logic for UnityRefactorHelperSettingsControl.
9 | ///
10 | public partial class UnityRefactorHelperSettingsControl : UserControl
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | public UnityRefactorHelperSettingsControl()
16 | {
17 | InitializeComponent();
18 | }
19 |
20 | public ToolWindowViewModel ViewModel => ToolWindowViewModel.Instance;
21 |
22 | private void selectUnityProjectFolderBtn_Click(object sender, RoutedEventArgs e)
23 | {
24 | ViewModel.OpenUnityFolderDialog();
25 | }
26 |
27 | private void addBtn_Click(object sender, RoutedEventArgs e)
28 | {
29 | ViewModel.AddNewProjectSync();
30 | }
31 |
32 | private void saveBtn_Click(object sender, RoutedEventArgs e)
33 | {
34 | ViewModel.Save();
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/UnityRefactorHelper/ViewModel/ToolWindowViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.ObjectModel;
4 | using System.Collections.Specialized;
5 | using System.ComponentModel;
6 | using System.Linq;
7 | using System.Runtime.CompilerServices;
8 | using System.Windows.Forms;
9 | using UnityRefactorHelper.Annotations;
10 | using UnityRefactorHelper.Model;
11 | using UnityRefactorHelper.Service;
12 | using UnityRefactorHelper.View;
13 |
14 | namespace UnityRefactorHelper.ViewModel
15 | {
16 | public class ToolWindowViewModel : INotifyPropertyChanged
17 | {
18 | public static ToolWindowViewModel Instance;
19 | private string _errorText;
20 | private bool _isEnabled;
21 | private string _selectedProject;
22 | private string _selectedProjectGuid;
23 | private Guid _selectedProjectGuidParsed;
24 | private bool _solutionLoaded;
25 | private string _unityProjectPath;
26 |
27 | static ToolWindowViewModel()
28 | {
29 | Instance = new ToolWindowViewModel();
30 | }
31 |
32 | private ToolWindowViewModel()
33 | {
34 | ProjectNames = new ObservableCollection();
35 | SyncProjects = new ObservableCollection();
36 | SyncProjects.CollectionChanged += SyncProjectsOnCollectionChanged;
37 |
38 | SyncProjectControls = new ObservableCollection();
39 | }
40 |
41 | public ObservableCollection ProjectNames { get; set; }
42 |
43 | public bool SolutionLoaded
44 | {
45 | get { return _solutionLoaded; }
46 | set
47 | {
48 | _solutionLoaded = value;
49 | OnPropertyChanged();
50 | OnPropertyChanged(nameof(IsSaveEnabled));
51 | }
52 | }
53 |
54 | public string UnityProjectPath
55 | {
56 | get { return _unityProjectPath; }
57 | set
58 | {
59 | _unityProjectPath = value;
60 | OnPropertyChanged();
61 | OnPropertyChanged(nameof(IsSaveEnabled));
62 | }
63 | }
64 |
65 | public bool IsEnabled
66 | {
67 | get { return _isEnabled; }
68 | set
69 | {
70 | _isEnabled = value;
71 | OnPropertyChanged();
72 | OnPropertyChanged(nameof(IsSaveEnabled));
73 | }
74 | }
75 |
76 | public ObservableCollection SyncProjects { get; set; }
77 |
78 | public string SelectedProject
79 | {
80 | get { return _selectedProject; }
81 | set
82 | {
83 | _selectedProject = value;
84 | OnPropertyChanged();
85 | OnPropertyChanged(nameof(IsAddEnabled));
86 | }
87 | }
88 |
89 | public string SelectedProjectGuid
90 | {
91 | get { return _selectedProjectGuid; }
92 | set
93 | {
94 | _selectedProjectGuidParsed = Guid.Empty;
95 | _selectedProjectGuid = value;
96 | OnPropertyChanged();
97 | OnPropertyChanged(nameof(IsAddEnabled));
98 | }
99 | }
100 |
101 | public bool IsAddEnabled => !string.IsNullOrWhiteSpace(SelectedProject) &&
102 | Guid.TryParse(SelectedProjectGuid, out _selectedProjectGuidParsed);
103 |
104 | public bool IsSaveEnabled
105 | => ((!string.IsNullOrWhiteSpace(UnityProjectPath) && SyncProjects.Any()) || !IsEnabled) && SolutionLoaded;
106 |
107 | public string ErrorText
108 | {
109 | get { return _errorText; }
110 | set
111 | {
112 | _errorText = value;
113 | OnPropertyChanged();
114 | OnPropertyChanged(nameof(ShowError));
115 | }
116 | }
117 |
118 | public bool ShowError => !string.IsNullOrWhiteSpace(ErrorText);
119 | public ObservableCollection SyncProjectControls { get; set; }
120 |
121 | public void OpenUnityFolderDialog()
122 | {
123 | var dialog = new FolderBrowserDialog();
124 | if (dialog.ShowDialog() == DialogResult.OK)
125 | {
126 | UnityProjectPath = dialog.SelectedPath;
127 | }
128 | }
129 |
130 | public void AddNewProjectSync()
131 | {
132 | if (_selectedProjectGuidParsed == Guid.Empty) return;
133 | if (SyncProjects.All(x => x.ProjectGuid != _selectedProjectGuidParsed))
134 | {
135 | var existed = SyncProjects.FirstOrDefault(x => x.ProjectName == SelectedProject);
136 | if (existed != null)
137 | {
138 | existed.ProjectGuid = _selectedProjectGuidParsed;
139 | var control = SyncProjectControls.FirstOrDefault(x => x.Item.ProjectName == existed.ProjectName);
140 | if (control != null)
141 | control.Item = existed;
142 | }
143 | else
144 | {
145 | SyncProjects.Add(new ProjectSyncItem
146 | {
147 | ProjectGuid = _selectedProjectGuidParsed,
148 | ProjectName = SelectedProject
149 | });
150 | }
151 | }
152 |
153 | SelectedProject = null;
154 | SelectedProjectGuid = null;
155 | OnPropertyChanged(nameof(IsSaveEnabled));
156 | }
157 |
158 | public void Save()
159 | {
160 | var settings = new Settings();
161 | if (IsEnabled)
162 | {
163 | settings.IsEnabled = true;
164 | settings.UnityProjectPath = UnityProjectPath;
165 | settings.ProjectSyncItems = SyncProjects;
166 | }
167 |
168 | var oldSettings = Cache.Instance.Settings;
169 | try
170 | {
171 | Cache.Instance.Settings = settings;
172 | CommonService.SaveSettings();
173 | ErrorText = null;
174 | }
175 | catch (Exception)
176 | {
177 | Cache.Instance.Settings = oldSettings;
178 | ErrorText = "Can't save settings.";
179 | }
180 |
181 | SynchronizeProjects();
182 | CommonService.SaveSyncProjectItemsToCache();
183 | }
184 |
185 | private void SynchronizeProjects()
186 | {
187 | var existedItemsWithDifferentGuid =
188 | Cache.Instance.ScanProjects.Where(
189 | x => SyncProjects.Any(sp => sp.ProjectName == x.ProjectName && sp.ProjectGuid != x.ProjectGuid));
190 | var newItems =
191 | SyncProjects.Where(x => Cache.Instance.ScanProjects.All(sp => sp.ProjectName != x.ProjectName));
192 | var deleteItems =
193 | Cache.Instance.ScanProjects.Where(x => SyncProjects.All(sp => sp.ProjectName != x.ProjectName)).ToList();
194 |
195 | foreach (var deleteItem in deleteItems)
196 | {
197 | Cache.Instance.ScanProjects.Remove(deleteItem);
198 | }
199 |
200 | foreach (var existedItem in existedItemsWithDifferentGuid)
201 | {
202 | var syncItem = SyncProjects.FirstOrDefault(x => x.ProjectName == existedItem.ProjectName);
203 | if (syncItem != null)
204 | {
205 | existedItem.ProjectGuid = syncItem.ProjectGuid;
206 | }
207 | }
208 |
209 | foreach (var newItem in newItems)
210 | {
211 | Cache.Instance.ScanProjects.Add(new ProjectScanItem
212 | {
213 | ProjectName = newItem.ProjectName,
214 | ProjectGuid = newItem.ProjectGuid
215 | });
216 | ProjectService.ProcessNewProject(newItem);
217 | }
218 | }
219 |
220 | private void SyncProjectsOnCollectionChanged(object sender,
221 | NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
222 | {
223 | if (notifyCollectionChangedEventArgs.Action == NotifyCollectionChangedAction.Add)
224 | {
225 | var items = notifyCollectionChangedEventArgs.NewItems as IEnumerable;
226 | foreach (ProjectSyncItem item in items)
227 | {
228 | var control = new SyncProjectControl(item);
229 | control.OnSyncItemDelete += SyncProjectOnItemDelete;
230 | control.OnSyncItemClick += SyncProjectOnSyncItemClick;
231 | SyncProjectControls.Add(control);
232 | }
233 | }
234 | }
235 |
236 | private void SyncProjectOnSyncItemClick(object sender, EventArgs eventArgs)
237 | {
238 | var item = sender as ProjectSyncItem;
239 | if (item != null)
240 | {
241 | SelectedProject = item.ProjectName;
242 | SelectedProjectGuid = item.ProjectGuid.ToString();
243 | }
244 | }
245 |
246 | private void SyncProjectOnItemDelete(object sender, EventArgs eventArgs)
247 | {
248 | var item = sender as SyncProjectControl;
249 | if (item != null)
250 | {
251 | SyncProjectControls.Remove(item);
252 | SyncProjects.Remove(item.Item);
253 | OnPropertyChanged(nameof(IsSaveEnabled));
254 | }
255 | }
256 |
257 | public void LoadSettings()
258 | {
259 | try
260 | {
261 | CommonService.LoadSettings();
262 | }
263 | catch
264 | {
265 | return;
266 | }
267 |
268 | var settings = Cache.Instance.Settings;
269 | if (settings == null) return;
270 | MapSettings(settings);
271 | try
272 | {
273 | CommonService.LoadSyncProjectsCache();
274 | }
275 | catch
276 | {
277 | }
278 | }
279 |
280 | private void MapSettings(Settings settings)
281 | {
282 | IsEnabled = settings.IsEnabled;
283 | UnityProjectPath = settings.UnityProjectPath;
284 | foreach (var item in settings.ProjectSyncItems)
285 | {
286 | SyncProjects.Add(item);
287 | }
288 | Cache.Instance.ScanProjects.AddRange(SyncProjects.Select(x => new ProjectScanItem
289 | {
290 | ProjectGuid = x.ProjectGuid,
291 | ProjectName = x.ProjectName
292 | }));
293 |
294 | OnPropertyChanged(nameof(IsSaveEnabled));
295 | }
296 |
297 | #region INotifyPropertyChanged
298 |
299 | public event PropertyChangedEventHandler PropertyChanged;
300 |
301 | [NotifyPropertyChangedInvocator]
302 | protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
303 | {
304 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
305 | }
306 |
307 | #endregion
308 | }
309 | }
--------------------------------------------------------------------------------
/UnityRefactorHelper/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/UnityRefactorHelper/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/UnityRefactorHelper/source.extension.vsixmanifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Unity Refactor Helper
6 | Helper extension for Unity script refactoring
7 | https://github.com/PDarkTemplar/UnityRefactorHelper
8 | Resources\MainIcon.png
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------