├── CHANGELOG.md
├── CHANGELOG.md.meta
├── CONTRIBUTING.md
├── CONTRIBUTING.md.meta
├── Documentation~
├── Images
│ └── external-tools-tab.png
├── README.md
├── TableOfContents.md
├── index.md
└── using-visual-studio-editor.md
├── Editor.meta
├── Editor
├── AppleEventIntegration~
│ ├── AppleEventIntegration.xcodeproj
│ │ └── project.pbxproj
│ ├── AppleEventIntegration
│ │ ├── Info.plist
│ │ └── main.mm
│ └── howtobuild.txt
├── AssemblyInfo.cs
├── AssemblyInfo.cs.meta
├── AsyncOperation.cs
├── AsyncOperation.cs.meta
├── COMIntegration.meta
├── COMIntegration
│ ├── COMIntegration~
│ │ ├── BStrHolder.h
│ │ ├── CMakeLists.txt
│ │ ├── COMIntegration.cpp
│ │ ├── ComPtr.h
│ │ ├── dte80a.tlh
│ │ └── howtobuild.txt
│ ├── Release.meta
│ └── Release
│ │ ├── COMIntegration.exe
│ │ └── COMIntegration.exe.meta
├── Cli.cs
├── Cli.cs.meta
├── Discovery.cs
├── Discovery.cs.meta
├── FileUtility.cs
├── FileUtility.cs.meta
├── Image.cs
├── Image.cs.meta
├── KnownAssemblies.cs
├── KnownAssemblies.cs.meta
├── Messaging.meta
├── Messaging
│ ├── Deserializer.cs
│ ├── Deserializer.cs.meta
│ ├── ExceptionEventArgs.cs
│ ├── ExceptionEventArgs.cs.meta
│ ├── Message.cs
│ ├── Message.cs.meta
│ ├── MessageEventArgs.cs
│ ├── MessageEventArgs.cs.meta
│ ├── MessageType.cs
│ ├── MessageType.cs.meta
│ ├── Messenger.cs
│ ├── Messenger.cs.meta
│ ├── Serializer.cs
│ ├── Serializer.cs.meta
│ ├── TcpClient.cs
│ ├── TcpClient.cs.meta
│ ├── TcpListener.cs
│ ├── TcpListener.cs.meta
│ ├── UdpSocket.cs
│ └── UdpSocket.cs.meta
├── Plugins.meta
├── Plugins
│ ├── AppleEventIntegration.bundle.meta
│ └── AppleEventIntegration.bundle
│ │ ├── Contents.meta
│ │ └── Contents
│ │ ├── Info.plist
│ │ ├── Info.plist.meta
│ │ ├── MacOS.meta
│ │ ├── MacOS
│ │ ├── AppleEventIntegration
│ │ └── AppleEventIntegration.meta
│ │ ├── _CodeSignature.meta
│ │ └── _CodeSignature
│ │ ├── CodeResources
│ │ └── CodeResources.meta
├── ProcessRunner.cs
├── ProcessRunner.cs.meta
├── ProjectGeneration.meta
├── ProjectGeneration
│ ├── AssemblyNameProvider.cs
│ ├── AssemblyNameProvider.cs.meta
│ ├── FileIOProvider.cs
│ ├── FileIOProvider.cs.meta
│ ├── GUIDProvider.cs
│ ├── GUIDProvider.cs.meta
│ ├── LegacyStyleProjectGeneration.cs
│ ├── LegacyStyleProjectGeneration.cs.meta
│ ├── ProjectGeneration.cs
│ ├── ProjectGeneration.cs.meta
│ ├── ProjectGenerationFlag.cs
│ ├── ProjectGenerationFlag.cs.meta
│ ├── ProjectProperties.cs
│ ├── ProjectProperties.cs.meta
│ ├── SdkStyleProjectGeneration.cs
│ └── SdkStyleProjectGeneration.cs.meta
├── SimpleJSON.cs
├── SimpleJSON.cs.meta
├── Solution.cs
├── Solution.cs.meta
├── SolutionParser.cs
├── SolutionParser.cs.meta
├── SolutionProjectEntry.cs
├── SolutionProjectEntry.cs.meta
├── SolutionProperties.cs
├── SolutionProperties.cs.meta
├── Symbols.cs
├── Symbols.cs.meta
├── Testing.meta
├── Testing
│ ├── TestAdaptor.cs
│ ├── TestAdaptor.cs.meta
│ ├── TestResultAdaptor.cs
│ ├── TestResultAdaptor.cs.meta
│ ├── TestRunnerApiListener.cs
│ ├── TestRunnerApiListener.cs.meta
│ ├── TestRunnerCallbacks.cs
│ ├── TestRunnerCallbacks.cs.meta
│ ├── TestStatusAdaptor.cs
│ └── TestStatusAdaptor.cs.meta
├── UnityInstallation.cs
├── UnityInstallation.cs.meta
├── UsageUtility.cs
├── UsageUtility.cs.meta
├── VSWhere.meta
├── VSWhere
│ ├── vswhere.exe
│ └── vswhere.exe.meta
├── VersionPair.cs
├── VersionPair.cs.meta
├── VisualStudioCodiumInstallation.cs
├── VisualStudioCodiumInstallation.cs.meta
├── VisualStudioCursorInstallation.cs
├── VisualStudioCursorInstallation.cs.meta
├── VisualStudioEditor.cs
├── VisualStudioEditor.cs.meta
├── VisualStudioInstallation.cs
├── VisualStudioInstallation.cs.meta
├── VisualStudioIntegration.cs
├── VisualStudioIntegration.cs.meta
├── com.unity.ide.visualstudio.asmdef
└── com.unity.ide.visualstudio.asmdef.meta
├── LICENSE.md
├── LICENSE.md.meta
├── README.md
├── README.md.meta
├── ThirdPartyNotices.md
├── ThirdPartyNotices.md.meta
├── ValidationConfig.json
├── ValidationConfig.json.meta
├── ValidationExceptions.json
├── ValidationExceptions.json.meta
├── package.json
├── package.json.meta
└── rules
├── etrules.mdc
└── etrules.mdc.meta
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Code Editor Package for Visual Studio
2 |
3 | ## [2.0.22] - 2023-10-03
4 |
5 | Integration:
6 |
7 | - Add support for `XDG_DATA_DIRS` and `.desktop` files on Linux for `VS Code` discovery.
8 | - Use compile-time platform-specifics instead of using runtime conditions.
9 |
10 | Project generation:
11 |
12 | - Suppress `USG0001` warnings.
13 | - Mark referenced assemblies as private (to not copy extra files to output directory when building).
14 | - Add Unity capability to SDK-Style projects.
15 | - Prevent circular dependency errors with SDK-Style projects.
16 |
17 |
18 | ## [2.0.21] - 2023-09-05
19 |
20 | Integration:
21 |
22 | - Only disable the legacy `com.unity.ide.vscode` package going forward.
23 | - Fix json parsing issues with specific non-UTF code pages.
24 |
25 | Project generation:
26 |
27 | - Target `netstandard2.1` instead of `netstandard2.0`.
28 | - Set `defaultSolution` in `settings.json`.
29 | - Remove `files.exclude` entries for root `csproj` and `sln` files in `settings.json` when needed.
30 | - Add `vstuc` launch configuration to `launch.json` when needed.
31 | - Add `visualstudiotoolsforunity.vstuc` entry to `extensions.json` when needed.
32 | - You can prevent the package from patching those configuration files by creating a `.vscode/.vstupatchdisable` file.
33 |
34 | ## [2.0.20] - 2023-06-27
35 |
36 | Integration:
37 |
38 | - Internal API refactoring.
39 | - Add support for Visual Studio Code.
40 |
41 | Project generation:
42 |
43 | - Add support for Sdk Style project generation.
44 | - Fix an issue related to missing properties with 2021.3.
45 |
46 | ## [2.0.18] - 2023-03-17
47 |
48 | Integration:
49 |
50 | - Performance improvements with `EditorApplication.update` callbacks.
51 |
52 | Project generation:
53 |
54 | - Add extra compiler options for analyzers and source generators.
55 |
56 | ## [2.0.17] - 2022-12-06
57 |
58 | Integration:
59 |
60 | - Fix rare deadlocks while discovering or launching Visual Studio on Windows.
61 | - Improve launching Visual Studio on macOs.
62 |
63 | Project generation:
64 |
65 | - Include analyzers from response files.
66 | - Update supported C# versions.
67 | - Performance improvements.
68 |
69 | ## [2.0.16] - 2022-06-08
70 |
71 | Integration:
72 |
73 | - Prevent ADB Refresh while being in safe-mode with a URP project
74 | - Fixed an issue keeping the progress bar visible even after opening a script with Visual Studio.
75 |
76 | ## [2.0.15] - 2022-03-21
77 |
78 | Integration:
79 |
80 | - Improved project generation performance.
81 | - Added support for keeping file/folder structure when working with external packages.
82 | - Fixed project generation not being refreshed when selecting Visual Studio as the preferred external editor.
83 |
84 | ## [2.0.14] - 2022-01-14
85 |
86 | Integration:
87 |
88 | - Remove package version checking.
89 |
90 | ## [2.0.13] - 2022-01-12
91 |
92 | Integration:
93 |
94 | - Fixed wrong path to analyzers in generated projects when using external packages.
95 | - Fixed selective project generation not creating Analyzer/LangVersion nodes.
96 | - Fixed asmdef references with Player projects.
97 |
98 | Documentation:
99 |
100 | - Added new documentation including ToC, overview, how to use and images.
101 |
102 | ## [2.0.12] - 2021-10-20
103 |
104 | Integration:
105 |
106 | - Do not block asset opening when only a VS instance without a loaded solution is found.
107 | - Only check package version once per Unity session.
108 | - Improved support for Visual Studio For Mac 2022.
109 |
110 | ## [2.0.11] - 2021-07-01
111 |
112 | Integration:
113 |
114 | - Added support for Visual Studio and Visual Studio For Mac 2022.
115 | - Fixed an issue when the package was enabled for background processes.
116 |
117 | Project generation:
118 |
119 | - Use absolute paths for Analyzers and rulesets.
120 |
121 | ## [2.0.10] - 2021-06-10
122 |
123 | Project generation:
124 |
125 | - Improved project generation performance when a file is moved, deleted or modified.
126 |
127 | Integration:
128 |
129 | - Improved Inner-loop performance by avoiding to call the package manager when looking up `vswhere` utility.
130 | - Fixed a network issue preventing the communication between Visual Studio and Unity on Windows.
131 |
132 | ## [2.0.9] - 2021-05-04
133 |
134 | Project generation:
135 |
136 | - Added support for CLI.
137 |
138 | Integration:
139 |
140 | - Improved performance when discovering Visual Studio installations.
141 | - Warn when legacy assemblies are present in the project.
142 | - Warn when the package version is not up-to-date.
143 |
144 | ## [2.0.8] - 2021-04-09
145 |
146 | Project generation:
147 |
148 | - Improved generation performance (especially with DOTS enabled projects).
149 | - Improved stability.
150 | - Updated Analyzers lookup strategy.
151 | - Fixed .vsconfig file not generated when using "regenerate all".
152 |
153 | Integration:
154 |
155 | - Improved automation plugins.
156 |
157 | Documentation:
158 |
159 | - Open sourced automation plugins.
160 |
161 | ## [2.0.7] - 2021-02-02
162 |
163 | Integration:
164 |
165 | - Remove `com.unity.nuget.newtonsoft-json` dependency in favor of the built-in JsonUtility for the VS Test Runner.
166 |
167 | ## [2.0.6] - 2021-01-20
168 |
169 | Project generation:
170 |
171 | - Improved language version detection.
172 |
173 | Integration:
174 |
175 | - Added support for the VS Test Runner.
176 | - Added initial support for displaying asset usage.
177 | - Fixed remaining issues with special characters in file/path.
178 |
179 | ## [2.0.5] - 2020-10-30
180 |
181 | Integration:
182 |
183 | - Disable legacy pdb symbol checking for Unity packages.
184 |
185 | ## [2.0.4] - 2020-10-15
186 |
187 | Project generation:
188 |
189 | - Added support for embedded Roslyn analyzer DLLs and ruleset files.
190 | - Warn the user when the opened script is not part of the generation scope.
191 | - Warn the user when the selected Visual Studio installation is not found.
192 | - Generate a .vsconfig file to ensure Visual Studio installation is compatible.
193 |
194 | Integration:
195 |
196 | - Fix automation issues on MacOS, where a new Visual Studio instance is opened every time.
197 |
198 | ## [2.0.3] - 2020-09-09
199 |
200 | Project generation:
201 |
202 | - Added C#8 language support.
203 | - Added `UnityProjectGeneratorVersion` property.
204 | - Local and Embedded packages are now selected by default for generation.
205 | - Added support for asmdef root namespace.
206 |
207 | Integration:
208 |
209 | - When the user disabled auto-refresh in Unity, do not try to force refresh the Asset database.
210 | - Fix Visual Studio detection issues with languages using special characters.
211 |
212 |
213 | ## [2.0.2] - 2020-05-27
214 |
215 | - Added support for solution folders.
216 | - Only bind the messenger when the VS editor is selected.
217 | - Warn when unable to create the messenger.
218 | - Fixed an initialization issue triggering legacy code generation.
219 | - Allow package source in assembly to be generated when referenced from asmref.
220 |
221 |
222 | ## [2.0.1] - 2020-03-19
223 |
224 | - When Visual Studio installation is compatible with C# 8.0, setup the language version to not prompt the user with unsupported constructs. (So far Unity only supports C# 7.3).
225 | - Use Unity's `TypeCache` to improve project generation speed.
226 | - Properly check for a managed assembly before displaying a warning regarding legacy PDB usage.
227 | - Add support for selective project generation (embedded, local, registry, git, builtin, player).
228 |
229 | ## [2.0.0] - 2019-11-06
230 |
231 | - Improved Visual Studio and Visual Studio for Mac automatic discovery.
232 | - Added support for the VSTU messaging system (start/stop features from Visual Studio).
233 | - Added support for solution roundtrip (preserves references to external projects and solution properties).
234 | - Added support for VSTU Analyzers (requires Visual Studio 2019 16.3, Visual Studio for Mac 8.3).
235 | - Added a warning when using legacy pdb symbol files.
236 | - Fixed issues while Opening Visual Studio on Windows.
237 | - Fixed issues while Opening Visual Studio on Mac.
238 |
239 | ## [1.1.1] - 2019-05-29
240 |
241 | - Fix Bridge assembly loading with non VS2017 editors.
242 |
243 | ## [1.1.0] - 2019-05-27
244 |
245 | - Move internal extension handling to package.
246 |
247 | ## [1.0.11] - 2019-05-21
248 |
249 | - Fix detection of visual studio for mac installation.
250 |
251 | ## [1.0.10] - 2019-05-04
252 |
253 | - Fix ignored comintegration executable.
254 |
255 | ## [1.0.9] - 2019-03-05
256 |
257 | - Updated MonoDevelop support, to pass correct arguments, and not import VSTU plugin.
258 | - Use release build of COMIntegration for Visual Studio.
259 |
260 | ## [1.0.7] - 2019-04-30
261 |
262 | - Ensure asset database is refreshed when generating csproj and solution files.
263 |
264 | ## [1.0.6] - 2019-04-27
265 |
266 | - Add support for generating all csproj files.
267 |
268 | ## [1.0.5] - 2019-04-18
269 |
270 | - Fix relative package paths.
271 | - Fix opening editor on mac.
272 |
273 | ## [1.0.4] - 2019-04-12
274 |
275 | - Fixing null reference issue for callbacks to `AssetPostProcessor`.
276 | - Ensure `Path.GetFullPath` does not get an empty string.
277 |
278 | ## [1.0.3] - 2019-01-01
279 |
280 | ### This is the first release of *Unity Package visualstudio_editor*.
281 |
282 | - Using the newly created api to integrate Visual Studio with Unity.
283 |
--------------------------------------------------------------------------------
/CHANGELOG.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e3cceb39029f44e4d8c39cb2a333bed2
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## All contributions are subject to the [Unity Contribution Agreement(UCA)](https://unity3d.com/legal/licenses/Unity_Contribution_Agreement) and [Microsoft Contributor License Agreement (CLA)](https://cla.opensource.microsoft.com/)
4 | By making a pull request, you are confirming agreement to the terms and conditions of the UCA and CLA, including that your contributions are your original creation and that you have complete right and authority to make your contributions.
5 |
6 | ## Once you have a change ready following these ground rules. Simply make a pull request
--------------------------------------------------------------------------------
/CONTRIBUTING.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: eeba022f52d13c04e992f4d8b0624352
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Documentation~/Images/external-tools-tab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liyingsong99/com.unity.ide.cursor-et/2ff42a4c32bdbe24a12d24762bf43da7cdccc76a/Documentation~/Images/external-tools-tab.png
--------------------------------------------------------------------------------
/Documentation~/README.md:
--------------------------------------------------------------------------------
1 | # Code Editor Package for Visual Studio
2 |
3 | This package is not intended to be modified by users.
4 | Nor does it provide any api intended to be included in user projects.
--------------------------------------------------------------------------------
/Documentation~/TableOfContents.md:
--------------------------------------------------------------------------------
1 | * [About Visual Studio Editor](index.md)
2 | * [Using the Visual Studio Editor package](using-visual-studio-editor.md)
--------------------------------------------------------------------------------
/Documentation~/index.md:
--------------------------------------------------------------------------------
1 | # Code Editor Package for Visual Studio
2 |
3 | ## About Visual Studio Editor
4 |
5 | The Visual Studio Editor package provides the Unity Editor with support for Unity-specific features from the [Visual Studio Tools for Unity](https://docs.microsoft.com/en-us/visualstudio/gamedev/unity/get-started/visual-studio-tools-for-unity) extension in [Visual Studio](https://visualstudio.microsoft.com/) and [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/). These include IntelliSense auto-complete suggestions, C# editing, and debugging.
6 |
7 | ## Installation
8 |
9 | This package is a built-in package and installed by default.
10 |
11 | **Note**: If you’re using a version of the Unity Editor before 2019.4, you’ll need to install this package through the package manager.
12 |
13 | ## Requirements
14 |
15 | This version of the Visual Studio Editor package is compatible with the following versions of the Unity Editor:
16 |
17 | * 2019.4 and later
18 |
19 | To use this package, you must have the following third-party products installed:
20 |
21 | * **On Windows**: Visual Studio 2019 version 16.9 or newer with Visual Studio Tools for Unity 4.0.9 or newer.
22 | * **On macOS**: Visual Studio for Mac 2019 version 8.9 or newer with Visual Studio Tools for Unity 2.0.9 or newer.
23 |
24 | For more information about using Visual Studio with Unity, see [Microsoft’s Visual Studio Tools for Unity documentation](https://docs.microsoft.com/en-us/visualstudio/gamedev/unity/get-started/visual-studio-tools-for-unity).
25 |
26 | ## Submitting issues
27 |
28 | This package is maintained by Microsoft and Unity. Submit issues directly from Visual Studio and Visual Studio for Mac from the **Help** > **Submit Feedback** > **Report a Problem** menu. Unity will make this package accessible to the public on GitHub in the future.
--------------------------------------------------------------------------------
/Documentation~/using-visual-studio-editor.md:
--------------------------------------------------------------------------------
1 | # Using the Visual Studio Editor package
2 |
3 | To use the package, go to **Edit** > **Preferences** > **External Tools** > **External Script Editor** and select the version of **Visual Studio** you have installed. When you select this option, the window reloads and displays settings that control production of .csproj files.
4 |
5 | 
6 |
7 | ## Generate .csproj files
8 |
9 | Each setting in the table below enables or disables the production of .csproj files for a different type of package.When you click **Regenerate project files**, Unity updates the existing .csproj files and creates the necessary new ones based on the settings you choose.
10 |
11 |
12 | These settings control whether to generate .csproj files for any installed packages. For more information on how to install packages, see [Adding and removing packages](https://docs.unity3d.com/Manual/upm-ui-actions.html).
13 |
14 | | **Property** | **Description** |
15 | |---|---|
16 | | **Embedded packages** | Any package that appears under your project’s Packages folder is an embedded package. An embedded package is not necessarily built-in; you can create your own packages and embed them inside your project. This setting is enabled by default.
For more information on embedded packages, see [Embedded dependencies](https://docs.unity3d.com/Manual/upm-embed.html). |
17 | | **Local packages** | Any package that you install from a local repository stored on your machine, but from outside of your Unity project. This setting is enabled by default. |
18 | | **Registry packages** | Any package that you install from either the official Unity registry or a custom registry. Packages in the Unity registry are available to install directly from the Package Manager. For more information about the Unity package registry, see The Package Registry section of the [Unity Package Manager documentation](https://docs.unity3d.com/Packages/com.unity.package-manager-ui@1.8/manual/index.html#PackManRegistry).
For information on how to create and use custom registries in addition to the Unity registry, see [Scoped package registries](https://docs.unity3d.com/Manual/upm-scoped.html). |
19 | | **Git packages** | Any package you install directly from a Git repository using a URL. |
20 | | **Built-in packages** | Any package that is already installed as part of the default Unity installation. |
21 | | **Tarball packages** | Any package you install from a GZip tarball archive on the local machine, outside of your Unity project. |
22 | | **Unknown packages** | Any package which Unity cannot determine an origin for. This could be because the package doesn’t list its origin, or that Unity doesn’t recognize the origin listed. |
23 | | **Player projects** | For each player project, generate an additional .csproj file named ‘originalProjectName.Player.csproj’. This allows different project types to have their code included in Visual Studio’s systems, such as assembly definitions or testing suites. |
--------------------------------------------------------------------------------
/Editor.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: bf5103b4b65105d449e08afc5b0313be
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Editor/AppleEventIntegration~/AppleEventIntegration.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | E08E02FF236392D000A4B1BE /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = E08E02FE236392D000A4B1BE /* main.mm */; };
11 | E08E03022363933B00A4B1BE /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E08E03012363933B00A4B1BE /* AppKit.framework */; };
12 | /* End PBXBuildFile section */
13 |
14 | /* Begin PBXFileReference section */
15 | E08E02F5236392A300A4B1BE /* AppleEventIntegration.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppleEventIntegration.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
16 | E08E02F8236392A300A4B1BE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
17 | E08E02FE236392D000A4B1BE /* main.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; };
18 | E08E03012363933B00A4B1BE /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
19 | /* End PBXFileReference section */
20 |
21 | /* Begin PBXFrameworksBuildPhase section */
22 | E08E02F2236392A300A4B1BE /* Frameworks */ = {
23 | isa = PBXFrameworksBuildPhase;
24 | buildActionMask = 2147483647;
25 | files = (
26 | E08E03022363933B00A4B1BE /* AppKit.framework in Frameworks */,
27 | );
28 | runOnlyForDeploymentPostprocessing = 0;
29 | };
30 | /* End PBXFrameworksBuildPhase section */
31 |
32 | /* Begin PBXGroup section */
33 | E08E02EC236392A300A4B1BE = {
34 | isa = PBXGroup;
35 | children = (
36 | E08E02F7236392A300A4B1BE /* AppleEventIntegration */,
37 | E08E02F6236392A300A4B1BE /* Products */,
38 | E08E03002363933B00A4B1BE /* Frameworks */,
39 | );
40 | sourceTree = "";
41 | };
42 | E08E02F6236392A300A4B1BE /* Products */ = {
43 | isa = PBXGroup;
44 | children = (
45 | E08E02F5236392A300A4B1BE /* AppleEventIntegration.bundle */,
46 | );
47 | name = Products;
48 | sourceTree = "";
49 | };
50 | E08E02F7236392A300A4B1BE /* AppleEventIntegration */ = {
51 | isa = PBXGroup;
52 | children = (
53 | E08E02F8236392A300A4B1BE /* Info.plist */,
54 | E08E02FE236392D000A4B1BE /* main.mm */,
55 | );
56 | path = AppleEventIntegration;
57 | sourceTree = "";
58 | };
59 | E08E03002363933B00A4B1BE /* Frameworks */ = {
60 | isa = PBXGroup;
61 | children = (
62 | E08E03012363933B00A4B1BE /* AppKit.framework */,
63 | );
64 | name = Frameworks;
65 | sourceTree = "";
66 | };
67 | /* End PBXGroup section */
68 |
69 | /* Begin PBXNativeTarget section */
70 | E08E02F4236392A300A4B1BE /* AppleEventIntegration */ = {
71 | isa = PBXNativeTarget;
72 | buildConfigurationList = E08E02FB236392A300A4B1BE /* Build configuration list for PBXNativeTarget "AppleEventIntegration" */;
73 | buildPhases = (
74 | E08E02F1236392A300A4B1BE /* Sources */,
75 | E08E02F2236392A300A4B1BE /* Frameworks */,
76 | E08E02F3236392A300A4B1BE /* Resources */,
77 | );
78 | buildRules = (
79 | );
80 | dependencies = (
81 | );
82 | name = AppleEventIntegration;
83 | productName = AppleEventIntegration;
84 | productReference = E08E02F5236392A300A4B1BE /* AppleEventIntegration.bundle */;
85 | productType = "com.apple.product-type.bundle";
86 | };
87 | /* End PBXNativeTarget section */
88 |
89 | /* Begin PBXProject section */
90 | E08E02ED236392A300A4B1BE /* Project object */ = {
91 | isa = PBXProject;
92 | attributes = {
93 | LastUpgradeCheck = 1200;
94 | ORGANIZATIONNAME = Unity;
95 | TargetAttributes = {
96 | E08E02F4236392A300A4B1BE = {
97 | CreatedOnToolsVersion = 11.1;
98 | };
99 | };
100 | };
101 | buildConfigurationList = E08E02F0236392A300A4B1BE /* Build configuration list for PBXProject "AppleEventIntegration" */;
102 | compatibilityVersion = "Xcode 9.3";
103 | developmentRegion = en;
104 | hasScannedForEncodings = 0;
105 | knownRegions = (
106 | en,
107 | Base,
108 | );
109 | mainGroup = E08E02EC236392A300A4B1BE;
110 | productRefGroup = E08E02F6236392A300A4B1BE /* Products */;
111 | projectDirPath = "";
112 | projectRoot = "";
113 | targets = (
114 | E08E02F4236392A300A4B1BE /* AppleEventIntegration */,
115 | );
116 | };
117 | /* End PBXProject section */
118 |
119 | /* Begin PBXResourcesBuildPhase section */
120 | E08E02F3236392A300A4B1BE /* Resources */ = {
121 | isa = PBXResourcesBuildPhase;
122 | buildActionMask = 2147483647;
123 | files = (
124 | );
125 | runOnlyForDeploymentPostprocessing = 0;
126 | };
127 | /* End PBXResourcesBuildPhase section */
128 |
129 | /* Begin PBXSourcesBuildPhase section */
130 | E08E02F1236392A300A4B1BE /* Sources */ = {
131 | isa = PBXSourcesBuildPhase;
132 | buildActionMask = 2147483647;
133 | files = (
134 | E08E02FF236392D000A4B1BE /* main.mm in Sources */,
135 | );
136 | runOnlyForDeploymentPostprocessing = 0;
137 | };
138 | /* End PBXSourcesBuildPhase section */
139 |
140 | /* Begin XCBuildConfiguration section */
141 | E08E02F9236392A300A4B1BE /* Debug */ = {
142 | isa = XCBuildConfiguration;
143 | buildSettings = {
144 | ALWAYS_SEARCH_USER_PATHS = NO;
145 | CLANG_ANALYZER_NONNULL = YES;
146 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
147 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
148 | CLANG_CXX_LIBRARY = "libc++";
149 | CLANG_ENABLE_MODULES = YES;
150 | CLANG_ENABLE_OBJC_ARC = YES;
151 | CLANG_ENABLE_OBJC_WEAK = YES;
152 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
153 | CLANG_WARN_BOOL_CONVERSION = YES;
154 | CLANG_WARN_COMMA = YES;
155 | CLANG_WARN_CONSTANT_CONVERSION = YES;
156 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
157 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
158 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
159 | CLANG_WARN_EMPTY_BODY = YES;
160 | CLANG_WARN_ENUM_CONVERSION = YES;
161 | CLANG_WARN_INFINITE_RECURSION = YES;
162 | CLANG_WARN_INT_CONVERSION = YES;
163 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
164 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
165 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
166 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
167 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
168 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
169 | CLANG_WARN_STRICT_PROTOTYPES = YES;
170 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
171 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
172 | CLANG_WARN_UNREACHABLE_CODE = YES;
173 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
174 | COPY_PHASE_STRIP = NO;
175 | DEBUG_INFORMATION_FORMAT = dwarf;
176 | ENABLE_STRICT_OBJC_MSGSEND = YES;
177 | ENABLE_TESTABILITY = YES;
178 | GCC_C_LANGUAGE_STANDARD = gnu11;
179 | GCC_DYNAMIC_NO_PIC = NO;
180 | GCC_NO_COMMON_BLOCKS = YES;
181 | GCC_OPTIMIZATION_LEVEL = 0;
182 | GCC_PREPROCESSOR_DEFINITIONS = (
183 | "DEBUG=1",
184 | "$(inherited)",
185 | );
186 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
187 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
188 | GCC_WARN_UNDECLARED_SELECTOR = YES;
189 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
190 | GCC_WARN_UNUSED_FUNCTION = YES;
191 | GCC_WARN_UNUSED_VARIABLE = YES;
192 | MACOSX_DEPLOYMENT_TARGET = 10.13;
193 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
194 | MTL_FAST_MATH = YES;
195 | ONLY_ACTIVE_ARCH = YES;
196 | SDKROOT = macosx;
197 | };
198 | name = Debug;
199 | };
200 | E08E02FA236392A300A4B1BE /* Release */ = {
201 | isa = XCBuildConfiguration;
202 | buildSettings = {
203 | ALWAYS_SEARCH_USER_PATHS = NO;
204 | CLANG_ANALYZER_NONNULL = YES;
205 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
206 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
207 | CLANG_CXX_LIBRARY = "libc++";
208 | CLANG_ENABLE_MODULES = YES;
209 | CLANG_ENABLE_OBJC_ARC = YES;
210 | CLANG_ENABLE_OBJC_WEAK = YES;
211 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
212 | CLANG_WARN_BOOL_CONVERSION = YES;
213 | CLANG_WARN_COMMA = YES;
214 | CLANG_WARN_CONSTANT_CONVERSION = YES;
215 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
216 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
217 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
218 | CLANG_WARN_EMPTY_BODY = YES;
219 | CLANG_WARN_ENUM_CONVERSION = YES;
220 | CLANG_WARN_INFINITE_RECURSION = YES;
221 | CLANG_WARN_INT_CONVERSION = YES;
222 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
223 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
224 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
225 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
226 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
227 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
228 | CLANG_WARN_STRICT_PROTOTYPES = YES;
229 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
230 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
231 | CLANG_WARN_UNREACHABLE_CODE = YES;
232 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
233 | COPY_PHASE_STRIP = NO;
234 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
235 | ENABLE_NS_ASSERTIONS = NO;
236 | ENABLE_STRICT_OBJC_MSGSEND = YES;
237 | GCC_C_LANGUAGE_STANDARD = gnu11;
238 | GCC_NO_COMMON_BLOCKS = YES;
239 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
240 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
241 | GCC_WARN_UNDECLARED_SELECTOR = YES;
242 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
243 | GCC_WARN_UNUSED_FUNCTION = YES;
244 | GCC_WARN_UNUSED_VARIABLE = YES;
245 | MACOSX_DEPLOYMENT_TARGET = 10.13;
246 | MTL_ENABLE_DEBUG_INFO = NO;
247 | MTL_FAST_MATH = YES;
248 | SDKROOT = macosx;
249 | };
250 | name = Release;
251 | };
252 | E08E02FC236392A300A4B1BE /* Debug */ = {
253 | isa = XCBuildConfiguration;
254 | buildSettings = {
255 | CODE_SIGN_STYLE = Automatic;
256 | COMBINE_HIDPI_IMAGES = YES;
257 | DEVELOPMENT_TEAM = "";
258 | INFOPLIST_FILE = AppleEventIntegration/Info.plist;
259 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
260 | MACOSX_DEPLOYMENT_TARGET = 10.13;
261 | PRODUCT_BUNDLE_IDENTIFIER = com.unity.visualstudio.AppleEventIntegration;
262 | PRODUCT_NAME = "$(TARGET_NAME)";
263 | SKIP_INSTALL = YES;
264 | WRAPPER_EXTENSION = bundle;
265 | };
266 | name = Debug;
267 | };
268 | E08E02FD236392A300A4B1BE /* Release */ = {
269 | isa = XCBuildConfiguration;
270 | buildSettings = {
271 | CODE_SIGN_STYLE = Automatic;
272 | COMBINE_HIDPI_IMAGES = YES;
273 | DEVELOPMENT_TEAM = "";
274 | INFOPLIST_FILE = AppleEventIntegration/Info.plist;
275 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
276 | MACOSX_DEPLOYMENT_TARGET = 10.13;
277 | PRODUCT_BUNDLE_IDENTIFIER = com.unity.visualstudio.AppleEventIntegration;
278 | PRODUCT_NAME = "$(TARGET_NAME)";
279 | SKIP_INSTALL = YES;
280 | WRAPPER_EXTENSION = bundle;
281 | };
282 | name = Release;
283 | };
284 | /* End XCBuildConfiguration section */
285 |
286 | /* Begin XCConfigurationList section */
287 | E08E02F0236392A300A4B1BE /* Build configuration list for PBXProject "AppleEventIntegration" */ = {
288 | isa = XCConfigurationList;
289 | buildConfigurations = (
290 | E08E02F9236392A300A4B1BE /* Debug */,
291 | E08E02FA236392A300A4B1BE /* Release */,
292 | );
293 | defaultConfigurationIsVisible = 0;
294 | defaultConfigurationName = Release;
295 | };
296 | E08E02FB236392A300A4B1BE /* Build configuration list for PBXNativeTarget "AppleEventIntegration" */ = {
297 | isa = XCConfigurationList;
298 | buildConfigurations = (
299 | E08E02FC236392A300A4B1BE /* Debug */,
300 | E08E02FD236392A300A4B1BE /* Release */,
301 | );
302 | defaultConfigurationIsVisible = 0;
303 | defaultConfigurationName = Release;
304 | };
305 | /* End XCConfigurationList section */
306 | };
307 | rootObject = E08E02ED236392A300A4B1BE /* Project object */;
308 | }
309 |
--------------------------------------------------------------------------------
/Editor/AppleEventIntegration~/AppleEventIntegration/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | NSHumanReadableCopyright
22 | Copyright © 2019 Unity. All rights reserved.
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Editor/AppleEventIntegration~/AppleEventIntegration/main.mm:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Unity Technologies.
3 | * Copyright (c) Microsoft Corporation. All rights reserved.
4 | * Licensed under the MIT License. See License.txt in the project root for license information.
5 | *--------------------------------------------------------------------------------------------*/
6 |
7 | #import
8 | #import
9 |
10 | // 'FSnd' FourCC
11 | #define keyFileSender 1179872868
12 |
13 | // 16 bit aligned legacy struct - this should total 20 bytes
14 | typedef struct _SelectionRange
15 | {
16 | int16_t unused1; // 0 (not used)
17 | int16_t lineNum; // line to select (<0 to specify range)
18 | int32_t startRange; // start of selection range (if line < 0)
19 | int32_t endRange; // end of selection range (if line < 0)
20 | int32_t unused2; // 0 (not used)
21 | int32_t theDate; // modification date/time
22 | } __attribute__((packed)) SelectionRange;
23 |
24 | static NSString* MakeNSString(const char* str)
25 | {
26 | if (!str)
27 | return NULL;
28 |
29 | NSString* ret = [NSString stringWithUTF8String: str];
30 | return ret;
31 | }
32 |
33 | static UInt32 GetCreatorOfThisApp()
34 | {
35 | static UInt32 creator = 0;
36 | if (creator == 0)
37 | {
38 | UInt32 type;
39 | CFBundleGetPackageInfo(CFBundleGetMainBundle(), &type, &creator);
40 | }
41 | return creator;
42 | }
43 |
44 | static BOOL OpenFileAtLineWithAppleEvent(NSRunningApplication *runningApp, NSString* path, int line)
45 | {
46 | if (!runningApp)
47 | return NO;
48 |
49 | NSURL *pathUrl = [NSURL fileURLWithPath: path];
50 |
51 | NSAppleEventDescriptor* targetDescriptor = [NSAppleEventDescriptor
52 | descriptorWithProcessIdentifier: runningApp.processIdentifier];
53 |
54 | NSAppleEventDescriptor* appleEvent = [NSAppleEventDescriptor
55 | appleEventWithEventClass: kCoreEventClass
56 | eventID: kAEOpenDocuments
57 | targetDescriptor: targetDescriptor
58 | returnID: kAutoGenerateReturnID
59 | transactionID: kAnyTransactionID];
60 |
61 | [appleEvent
62 | setParamDescriptor: [NSAppleEventDescriptor
63 | descriptorWithDescriptorType: typeFileURL
64 | data: [[pathUrl absoluteString] dataUsingEncoding: NSUTF8StringEncoding]]
65 | forKeyword: keyDirectObject];
66 |
67 | UInt32 packageCreator = GetCreatorOfThisApp();
68 | if (packageCreator == kUnknownType) {
69 | [appleEvent
70 | setParamDescriptor: [NSAppleEventDescriptor
71 | descriptorWithDescriptorType: typeApplicationBundleID
72 | data: [[[NSBundle mainBundle] bundleIdentifier] dataUsingEncoding: NSUTF8StringEncoding]]
73 | forKeyword: keyFileSender];
74 | } else {
75 | [appleEvent
76 | setParamDescriptor: [NSAppleEventDescriptor descriptorWithTypeCode: packageCreator]
77 | forKeyword: keyFileSender];
78 | }
79 |
80 | if (line != -1) {
81 | // Add selection range to event
82 | SelectionRange range;
83 | range.unused1 = 0;
84 | range.lineNum = line - 1;
85 | range.startRange = -1;
86 | range.endRange = -1;
87 | range.unused2 = 0;
88 | range.theDate = -1;
89 |
90 | [appleEvent
91 | setParamDescriptor: [NSAppleEventDescriptor
92 | descriptorWithDescriptorType: typeChar
93 | bytes: &range
94 | length: sizeof(SelectionRange)]
95 | forKeyword: keyAEPosition];
96 | }
97 |
98 | AEDesc reply = { typeNull, NULL };
99 | OSErr err = AESendMessage(
100 | [appleEvent aeDesc],
101 | &reply,
102 | kAENoReply + kAENeverInteract,
103 | kAEDefaultTimeout);
104 |
105 | return err == noErr;
106 | }
107 |
108 | static BOOL ApplicationSupportsQueryOpenedSolution(NSString* appPath)
109 | {
110 | NSURL* appUrl = [NSURL fileURLWithPath: appPath];
111 | NSBundle* bundle = [NSBundle bundleWithURL: appUrl];
112 |
113 | if (!bundle)
114 | return NO;
115 |
116 | id versionValue = [bundle objectForInfoDictionaryKey: @"CFBundleVersion"];
117 | if (!versionValue || ![versionValue isKindOfClass: [NSString class]])
118 | return NO;
119 |
120 | NSString* version = (NSString*)versionValue;
121 | return [version compare:@"8.6" options:NSNumericSearch] != NSOrderedAscending;
122 | }
123 |
124 | static NSArray* QueryRunningInstances(NSString *appPath)
125 | {
126 | NSMutableArray* instances = [[NSMutableArray alloc] init];
127 | NSURL *appUrl = [NSURL fileURLWithPath: appPath];
128 |
129 | for (NSRunningApplication *runningApp in NSWorkspace.sharedWorkspace.runningApplications) {
130 | if (![runningApp isTerminated] && [runningApp.bundleURL isEqual: appUrl]) {
131 | [instances addObject: runningApp];
132 | }
133 | }
134 |
135 | return instances;
136 | }
137 |
138 | enum {
139 | kWorkspaceEventClass = 1448302419, /* 'VSWS' FourCC */
140 | kCurrentSelectedSolutionPathEventID = 1129534288 /* 'CSSP' FourCC */
141 | };
142 |
143 | static BOOL TryQueryCurrentSolutionPath(NSRunningApplication* runningApp, NSString** solutionPath)
144 | {
145 | NSAppleEventDescriptor* targetDescriptor = [NSAppleEventDescriptor
146 | descriptorWithProcessIdentifier: runningApp.processIdentifier];
147 |
148 | NSAppleEventDescriptor* appleEvent = [NSAppleEventDescriptor
149 | appleEventWithEventClass: kWorkspaceEventClass
150 | eventID: kCurrentSelectedSolutionPathEventID
151 | targetDescriptor: targetDescriptor
152 | returnID: kAutoGenerateReturnID
153 | transactionID: kAnyTransactionID];
154 |
155 | AEDesc aeReply = { 0, };
156 |
157 | OSErr sendResult = AESendMessage(
158 | [appleEvent aeDesc],
159 | &aeReply,
160 | kAEWaitReply | kAENeverInteract,
161 | kAEDefaultTimeout);
162 |
163 | if (sendResult != noErr) {
164 | return NO;
165 | }
166 |
167 | NSAppleEventDescriptor *reply = [[NSAppleEventDescriptor alloc] initWithAEDescNoCopy: &aeReply];
168 | *solutionPath = [[reply descriptorForKeyword: keyDirectObject] stringValue];
169 |
170 | return *solutionPath != NULL;
171 | }
172 |
173 | static NSRunningApplication* QueryRunningApplicationOpenedOnSolution(NSString* appPath, NSString* solutionPath)
174 | {
175 | BOOL supportsQueryOpenedSolution = ApplicationSupportsQueryOpenedSolution(appPath);
176 |
177 | for (NSRunningApplication *runningApp in QueryRunningInstances(appPath)) {
178 | // If the currently selected external editor does not support the opened solution apple event
179 | // then fallback to the previous behavior: take the first opened VSM and open the solution
180 | if (!supportsQueryOpenedSolution) {
181 | OpenFileAtLineWithAppleEvent(runningApp, solutionPath, -1);
182 | return runningApp;
183 | }
184 |
185 | NSString* currentSolutionPath;
186 | if (TryQueryCurrentSolutionPath(runningApp, ¤tSolutionPath)) {
187 | if ([solutionPath isEqual:currentSolutionPath]) {
188 | return runningApp;
189 | }
190 | } else {
191 | // If VSM doesn't respond to the query opened solution event
192 | // we fallback to the previous behavior too
193 | OpenFileAtLineWithAppleEvent(runningApp, solutionPath, -1);
194 | return runningApp;
195 | }
196 | }
197 |
198 | return NULL;
199 | }
200 |
201 | static NSRunningApplication* LaunchApplicationOnSolution(NSString* appPath, NSString* solutionPath)
202 | {
203 | return [[NSWorkspace sharedWorkspace]
204 | launchApplicationAtURL: [NSURL fileURLWithPath: appPath]
205 | options: NSWorkspaceLaunchDefault | NSWorkspaceLaunchNewInstance
206 | configuration: @{
207 | NSWorkspaceLaunchConfigurationArguments: @[ solutionPath ],
208 | }
209 | error: nil];
210 | }
211 |
212 | static NSRunningApplication* QueryOrLaunchApplication(NSString* appPath, NSString* solutionPath)
213 | {
214 | NSRunningApplication* runningApp = QueryRunningApplicationOpenedOnSolution(appPath, solutionPath);
215 |
216 | if (!runningApp)
217 | runningApp = LaunchApplicationOnSolution(appPath, solutionPath);
218 |
219 | if (runningApp)
220 | [runningApp activateWithOptions: 0];
221 |
222 | return runningApp;
223 | }
224 |
225 | BOOL LaunchOrReuseApp(NSString* appPath, NSString* solutionPath, NSRunningApplication** outApp)
226 | {
227 | NSRunningApplication* app = QueryOrLaunchApplication(appPath, solutionPath);
228 |
229 | if (outApp)
230 | *outApp = app;
231 |
232 | return app != NULL;
233 | }
234 |
235 | BOOL MonoDevelopOpenFile(NSString* appPath, NSString* solutionPath, NSString* filePath, int line)
236 | {
237 | NSRunningApplication* runningApp;
238 | if (!LaunchOrReuseApp(appPath, solutionPath, &runningApp)) {
239 | return FALSE;
240 | }
241 |
242 | if (filePath) {
243 | return OpenFileAtLineWithAppleEvent(runningApp, filePath, line);
244 | }
245 |
246 | return YES;
247 | }
248 |
249 | #if BUILD_APP
250 |
251 | int main(int argc, const char** argv)
252 | {
253 | if (argc != 5) {
254 | printf("Usage: AppleEventIntegration appPath solutionPath filePath lineNumber\n");
255 | return 1;
256 | }
257 |
258 | const char* appPath = argv[1];
259 | const char* solutionPath = argv[2];
260 | const char* filePath = argv[3];
261 | const int lineNumber = atoi(argv[4]);
262 |
263 | @autoreleasepool
264 | {
265 | MonoDevelopOpenFile(MakeNSString(appPath), MakeNSString(solutionPath), MakeNSString(filePath), lineNumber);
266 | }
267 |
268 | return 0;
269 | }
270 |
271 | #else
272 |
273 | extern "C"
274 | {
275 | BOOL OpenVisualStudio(const char* appPath, const char* solutionPath, const char* filePath, int line)
276 | {
277 | return MonoDevelopOpenFile(MakeNSString(appPath), MakeNSString(solutionPath), MakeNSString(filePath), line);
278 | }
279 | }
280 |
281 | #endif
282 |
--------------------------------------------------------------------------------
/Editor/AppleEventIntegration~/howtobuild.txt:
--------------------------------------------------------------------------------
1 | Bundle style (release)
2 | xcodebuild -configuration Release
3 |
4 | Standalone style (test)
5 | clang++ -D BUILD_APP -framework Foundation -framework AppKit main.mm
6 |
--------------------------------------------------------------------------------
/Editor/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | [assembly:InternalsVisibleTo("Unity.VisualStudio.EditorTests")]
4 | [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
5 |
--------------------------------------------------------------------------------
/Editor/AssemblyInfo.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e742243860d1a8f409a85e49176adc10
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/AsyncOperation.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Unity Technologies.
3 | * Copyright (c) Microsoft Corporation. All rights reserved.
4 | * Licensed under the MIT License. See License.txt in the project root for license information.
5 | *--------------------------------------------------------------------------------------------*/
6 | using System;
7 | using System.Threading;
8 |
9 | namespace Microsoft.Unity.VisualStudio.Editor
10 | {
11 | internal class AsyncOperation
12 | {
13 | private readonly Func _producer;
14 | private readonly Func _exceptionHandler;
15 | private readonly Action _finalHandler;
16 | private readonly ManualResetEventSlim _resetEvent;
17 |
18 | private T _result;
19 | private Exception _exception;
20 |
21 | private AsyncOperation(Func producer, Func exceptionHandler, Action finalHandler)
22 | {
23 | _producer = producer;
24 | _exceptionHandler = exceptionHandler;
25 | _finalHandler = finalHandler;
26 | _resetEvent = new ManualResetEventSlim(initialState: false);
27 | }
28 |
29 | public static AsyncOperation Run(Func producer, Func exceptionHandler = null, Action finalHandler = null)
30 | {
31 | var task = new AsyncOperation(producer, exceptionHandler, finalHandler);
32 | task.Run();
33 | return task;
34 | }
35 |
36 | private void Run()
37 | {
38 | ThreadPool.QueueUserWorkItem(_ =>
39 | {
40 | try
41 | {
42 | _result = _producer();
43 | }
44 | catch (Exception e)
45 | {
46 | _exception = e;
47 |
48 | if (_exceptionHandler != null)
49 | {
50 | _result = _exceptionHandler(e);
51 | }
52 | }
53 | finally
54 | {
55 | _finalHandler?.Invoke();
56 | _resetEvent.Set();
57 | }
58 | });
59 | }
60 |
61 | private void CheckCompletion()
62 | {
63 | if (!_resetEvent.IsSet)
64 | _resetEvent.Wait();
65 | }
66 |
67 |
68 | public T Result
69 | {
70 | get
71 | {
72 | CheckCompletion();
73 | return _result;
74 | }
75 | }
76 |
77 | public Exception Exception
78 | {
79 | get
80 | {
81 | CheckCompletion();
82 | return _exception;
83 | }
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/Editor/AsyncOperation.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 4feb2e7db71d34d40a7e4c0cf33765aa
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/COMIntegration.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 79136730e61b87043bff73e5d2be7151
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Editor/COMIntegration/COMIntegration~/BStrHolder.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | struct BStrHolder
5 | {
6 | BStrHolder() :
7 | m_Str(NULL)
8 | {
9 | }
10 |
11 | BStrHolder(const wchar_t* str) :
12 | m_Str(SysAllocString(str))
13 | {
14 | }
15 |
16 | ~BStrHolder()
17 | {
18 | if (m_Str != NULL)
19 | SysFreeString(m_Str);
20 | }
21 |
22 | operator BSTR() const
23 | {
24 | return m_Str;
25 | }
26 |
27 | BSTR* operator&()
28 | {
29 | if (m_Str != NULL)
30 | {
31 | SysFreeString(m_Str);
32 | m_Str = NULL;
33 | }
34 |
35 | return &m_Str;
36 | }
37 |
38 | private:
39 | BSTR m_Str;
40 | };
41 |
--------------------------------------------------------------------------------
/Editor/COMIntegration/COMIntegration~/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.15)
2 |
3 | project(com)
4 | set(SOURCES
5 | COMIntegration.cpp
6 | BStrHolder.h
7 | ComPtr.h
8 | dte80a.tlh
9 | )
10 | set(CMAKE_CXX_STANDARD 17)
11 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -Wall")
12 | add_executable(COMIntegration ${SOURCES})
13 | set_property(TARGET COMIntegration PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded")
14 | target_link_libraries(COMIntegration Shlwapi.lib)
15 |
--------------------------------------------------------------------------------
/Editor/COMIntegration/COMIntegration~/COMIntegration.cpp:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Unity Technologies.
3 | * Copyright (c) Microsoft Corporation. All rights reserved.
4 | * Licensed under the MIT License. See License.txt in the project root for license information.
5 | *--------------------------------------------------------------------------------------------*/
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include
15 | #include
16 |
17 | #include "BStrHolder.h"
18 | #include "ComPtr.h"
19 | #include "dte80a.tlh"
20 |
21 | constexpr int RETRY_INTERVAL_MS = 150;
22 | constexpr int TIMEOUT_MS = 10000;
23 |
24 | // Often a DTE call made to Visual Studio can fail after Visual Studio has just started. Usually the
25 | // return value will be RPC_E_CALL_REJECTED, meaning that Visual Studio is probably busy on another
26 | // thread. This types filter the RPC messages and retries to send the message until VS accepts it.
27 | class CRetryMessageFilter : public IMessageFilter
28 | {
29 | private:
30 | static bool ShouldRetryCall(DWORD dwTickCount, DWORD dwRejectType)
31 | {
32 | if (dwRejectType == SERVERCALL_RETRYLATER || dwRejectType == SERVERCALL_REJECTED) {
33 | return dwTickCount < TIMEOUT_MS;
34 | }
35 |
36 | return false;
37 | }
38 |
39 | win::ComPtr currentFilter;
40 |
41 | public:
42 | CRetryMessageFilter()
43 | {
44 | HRESULT hr = CoRegisterMessageFilter(this, ¤tFilter);
45 | _ASSERT(SUCCEEDED(hr));
46 | }
47 |
48 | ~CRetryMessageFilter()
49 | {
50 | win::ComPtr messageFilter;
51 | HRESULT hr = CoRegisterMessageFilter(currentFilter, &messageFilter);
52 | _ASSERT(SUCCEEDED(hr));
53 | }
54 |
55 | // IUnknown methods
56 | IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
57 | {
58 | static const QITAB qit[] =
59 | {
60 | QITABENT(CRetryMessageFilter, IMessageFilter),
61 | { 0 },
62 | };
63 | return QISearch(this, qit, riid, ppv);
64 | }
65 |
66 | IFACEMETHODIMP_(ULONG) AddRef()
67 | {
68 | return 0;
69 | }
70 |
71 | IFACEMETHODIMP_(ULONG) Release()
72 | {
73 | return 0;
74 | }
75 |
76 | DWORD STDMETHODCALLTYPE HandleInComingCall(DWORD dwCallType, HTASK htaskCaller, DWORD dwTickCount, LPINTERFACEINFO lpInterfaceInfo)
77 | {
78 | if (currentFilter)
79 | return currentFilter->HandleInComingCall(dwCallType, htaskCaller, dwTickCount, lpInterfaceInfo);
80 |
81 | return SERVERCALL_ISHANDLED;
82 | }
83 |
84 | DWORD STDMETHODCALLTYPE RetryRejectedCall(HTASK htaskCallee, DWORD dwTickCount, DWORD dwRejectType)
85 | {
86 | if (ShouldRetryCall(dwTickCount, dwRejectType))
87 | return RETRY_INTERVAL_MS;
88 |
89 | if (currentFilter)
90 | return currentFilter->RetryRejectedCall(htaskCallee, dwTickCount, dwRejectType);
91 |
92 | return (DWORD)-1;
93 | }
94 |
95 | DWORD STDMETHODCALLTYPE MessagePending(HTASK htaskCallee, DWORD dwTickCount, DWORD dwPendingType)
96 | {
97 | if (currentFilter)
98 | return currentFilter->MessagePending(htaskCallee, dwTickCount, dwPendingType);
99 |
100 | return PENDINGMSG_WAITDEFPROCESS;
101 | }
102 | };
103 |
104 | static void DisplayProgressbar() {
105 | std::wcout << "displayProgressBar" << std::endl;
106 | }
107 |
108 | static void ClearProgressbar() {
109 | std::wcout << "clearprogressbar" << std::endl;
110 | }
111 |
112 | inline const std::wstring QuoteString(const std::wstring& str)
113 | {
114 | return L"\"" + str + L"\"";
115 | }
116 |
117 | static std::wstring ErrorCodeToMsg(DWORD code)
118 | {
119 | LPWSTR msgBuf = nullptr;
120 | if (!FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
121 | nullptr, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&msgBuf, 0, nullptr))
122 | {
123 | return L"Unknown error";
124 | }
125 | else
126 | {
127 | return msgBuf;
128 | }
129 | }
130 |
131 | // Get an environment variable
132 | static std::wstring GetEnvironmentVariableValue(const std::wstring& variableName) {
133 | DWORD currentBufferSize = MAX_PATH;
134 | std::wstring variableValue;
135 | variableValue.resize(currentBufferSize);
136 |
137 | DWORD requiredBufferSize = GetEnvironmentVariableW(variableName.c_str(), variableValue.data(), currentBufferSize);
138 | if (requiredBufferSize == 0) {
139 | // Environment variable probably does not exist.
140 | return std::wstring();
141 | }
142 |
143 | if (currentBufferSize < requiredBufferSize) {
144 | variableValue.resize(requiredBufferSize);
145 | if (GetEnvironmentVariableW(variableName.c_str(), variableValue.data(), currentBufferSize) == 0)
146 | return std::wstring();
147 | }
148 |
149 | variableValue.resize(requiredBufferSize);
150 | return variableValue;
151 | }
152 |
153 | static bool StartVisualStudioProcess(
154 | const std::filesystem::path &visualStudioExecutablePath,
155 | const std::filesystem::path &solutionPath,
156 | DWORD *dwProcessId) {
157 |
158 | STARTUPINFOW si;
159 | PROCESS_INFORMATION pi;
160 | BOOL result;
161 |
162 | ZeroMemory(&si, sizeof(si));
163 | si.cb = sizeof(si);
164 | ZeroMemory(&pi, sizeof(pi));
165 |
166 | std::wstring startingDirectory = visualStudioExecutablePath.parent_path();
167 |
168 | // Build the command line that is passed as the argv of the VS process
169 | // argv[0] must be the quoted full path to the VS exe
170 | std::wstringstream commandLineStream;
171 | commandLineStream << QuoteString(visualStudioExecutablePath) << L" ";
172 |
173 | std::wstring vsArgsWide = GetEnvironmentVariableValue(L"UNITY_VS_ARGS");
174 | if (!vsArgsWide.empty())
175 | commandLineStream << vsArgsWide << L" ";
176 |
177 | commandLineStream << QuoteString(solutionPath);
178 |
179 | std::wstring commandLine = commandLineStream.str();
180 |
181 | std::wcout << "Starting Visual Studio process with: " << commandLine << std::endl;
182 |
183 | result = CreateProcessW(
184 | visualStudioExecutablePath.c_str(), // Full path to VS, must not be quoted
185 | commandLine.data(), // Command line, as passed as argv, separate arguments must be quoted if they contain spaces
186 | nullptr, // Process handle not inheritable
187 | nullptr, // Thread handle not inheritable
188 | false, // Set handle inheritance to FALSE
189 | 0, // No creation flags
190 | nullptr, // Use parent's environment block
191 | startingDirectory.c_str(), // starting directory set to the VS directory
192 | &si,
193 | &pi);
194 |
195 | if (!result) {
196 | DWORD error = GetLastError();
197 | std::wcout << "Starting Visual Studio process failed: " << ErrorCodeToMsg(error) << std::endl;
198 | return false;
199 | }
200 |
201 | *dwProcessId = pi.dwProcessId;
202 | CloseHandle(pi.hProcess);
203 | CloseHandle(pi.hThread);
204 |
205 | return true;
206 | }
207 |
208 | static bool
209 | MonikerIsVisualStudioProcess(const win::ComPtr &moniker, const win::ComPtr &bindCtx, const DWORD dwProcessId = 0) {
210 | LPOLESTR oleMonikerName;
211 | if (FAILED(moniker->GetDisplayName(bindCtx, nullptr, &oleMonikerName)))
212 | return false;
213 |
214 | std::wstring monikerName(oleMonikerName);
215 |
216 | // VisualStudio Moniker is "!VisualStudio.DTE.$Version:$PID"
217 | // Example "!VisualStudio.DTE.14.0:1234"
218 |
219 | if (monikerName.find(L"!VisualStudio.DTE") != 0)
220 | return false;
221 |
222 | if (dwProcessId == 0)
223 | return true;
224 |
225 | std::wstringstream suffixStream;
226 | suffixStream << ":";
227 | suffixStream << dwProcessId;
228 |
229 | std::wstring suffix(suffixStream.str());
230 |
231 | return monikerName.length() - suffix.length() == monikerName.find(suffix);
232 | }
233 |
234 | static win::ComPtr FindRunningVisualStudioWithSolution(
235 | const std::filesystem::path &visualStudioExecutablePath,
236 | const std::filesystem::path &solutionPath)
237 | {
238 | win::ComPtr punk = nullptr;
239 | win::ComPtr dte = nullptr;
240 |
241 | CRetryMessageFilter retryMessageFilter;
242 |
243 | // Search through the Running Object Table for an instance of Visual Studio
244 | // to use that either has the correct solution already open or does not have
245 | // any solution open.
246 | win::ComPtr ROT;
247 | if (FAILED(GetRunningObjectTable(0, &ROT)))
248 | return nullptr;
249 |
250 | win::ComPtr bindCtx;
251 | if (FAILED(CreateBindCtx(0, &bindCtx)))
252 | return nullptr;
253 |
254 | win::ComPtr enumMoniker;
255 | if (FAILED(ROT->EnumRunning(&enumMoniker)))
256 | return nullptr;
257 |
258 | win::ComPtr moniker;
259 | ULONG monikersFetched = 0;
260 | while (SUCCEEDED(enumMoniker->Next(1, &moniker, &monikersFetched)) && monikersFetched) {
261 | if (!MonikerIsVisualStudioProcess(moniker, bindCtx))
262 | continue;
263 |
264 | if (FAILED(ROT->GetObject(moniker, &punk)))
265 | continue;
266 |
267 | punk.As(&dte);
268 | if (!dte)
269 | continue;
270 |
271 | // Okay, so we found an actual running instance of Visual Studio.
272 |
273 | // Get the executable path of this running instance.
274 | BStrHolder visualStudioFullName;
275 | if (FAILED(dte->get_FullName(&visualStudioFullName)))
276 | continue;
277 |
278 | std::filesystem::path currentVisualStudioExecutablePath = std::wstring(visualStudioFullName);
279 |
280 | // Ask for its current solution.
281 | win::ComPtr solution;
282 | if (FAILED(dte->get_Solution(&solution)))
283 | continue;
284 |
285 | // Get the name of that solution.
286 | BStrHolder solutionFullName;
287 | if (FAILED(solution->get_FullName(&solutionFullName)))
288 | continue;
289 |
290 | std::filesystem::path currentSolutionPath = std::wstring(solutionFullName);
291 | if (currentSolutionPath.empty())
292 | continue;
293 |
294 | std::wcout << "Visual Studio opened on " << currentSolutionPath.wstring() << std::endl;
295 |
296 | // If the name matches the solution we want to open and we have a Visual Studio installation path to use and this one matches that path, then use it.
297 | // If we don't have a Visual Studio installation path to use, just use this solution.
298 | if (std::filesystem::equivalent(currentSolutionPath, solutionPath)) {
299 | std::wcout << "We found a running Visual Studio session with the solution open." << std::endl;
300 | if (!visualStudioExecutablePath.empty()) {
301 | if (std::filesystem::equivalent(currentVisualStudioExecutablePath, visualStudioExecutablePath)) {
302 | return dte;
303 | }
304 | else {
305 | std::wcout << "This running Visual Studio session does not seem to be the version requested in the user preferences. We will keep looking." << std::endl;
306 | }
307 | }
308 | else {
309 | std::wcout << "We're not sure which version of Visual Studio was requested in the user preferences. We will use this running session." << std::endl;
310 | return dte;
311 | }
312 | }
313 | }
314 | return nullptr;
315 | }
316 |
317 | static win::ComPtr FindRunningVisualStudioWithPID(const DWORD dwProcessId) {
318 | win::ComPtr punk = nullptr;
319 | win::ComPtr dte = nullptr;
320 |
321 | // Search through the Running Object Table for a Visual Studio
322 | // process with the process ID specified
323 | win::ComPtr ROT;
324 | if (FAILED(GetRunningObjectTable(0, &ROT)))
325 | return nullptr;
326 |
327 | win::ComPtr bindCtx;
328 | if (FAILED(CreateBindCtx(0, &bindCtx)))
329 | return nullptr;
330 |
331 | win::ComPtr enumMoniker;
332 | if (FAILED(ROT->EnumRunning(&enumMoniker)))
333 | return nullptr;
334 |
335 | win::ComPtr moniker;
336 | ULONG monikersFetched = 0;
337 | while (SUCCEEDED(enumMoniker->Next(1, &moniker, &monikersFetched)) && monikersFetched) {
338 | if (!MonikerIsVisualStudioProcess(moniker, bindCtx, dwProcessId))
339 | continue;
340 |
341 | if (FAILED(ROT->GetObject(moniker, &punk)))
342 | continue;
343 |
344 | punk.As(&dte);
345 | if (dte)
346 | return dte;
347 | }
348 |
349 | return nullptr;
350 | }
351 |
352 | static bool HaveRunningVisualStudioOpenFile(const win::ComPtr &dte, const std::filesystem::path &filename, int line) {
353 | BStrHolder bstrFileName(filename.c_str());
354 | BStrHolder bstrKind(L"{00000000-0000-0000-0000-000000000000}"); // EnvDTE::vsViewKindPrimary
355 | win::ComPtr window = nullptr;
356 |
357 | CRetryMessageFilter retryMessageFilter;
358 |
359 | if (!filename.empty()) {
360 | std::wcout << "Getting operations API from the Visual Studio session." << std::endl;
361 |
362 | win::ComPtr item_ops;
363 | if (FAILED(dte->get_ItemOperations(&item_ops)))
364 | return false;
365 |
366 | std::wcout << "Waiting for the Visual Studio session to open the file: " << filename.wstring() << "." << std::endl;
367 |
368 | if (FAILED(item_ops->OpenFile(bstrFileName, bstrKind, &window)))
369 | return false;
370 |
371 | if (line > 0) {
372 | win::ComPtr selection_dispatch;
373 | if (window && SUCCEEDED(window->get_Selection(&selection_dispatch))) {
374 | win::ComPtr selection;
375 | if (selection_dispatch &&
376 | SUCCEEDED(selection_dispatch->QueryInterface(__uuidof(EnvDTE::TextSelection), &selection)) &&
377 | selection) {
378 | selection->GotoLine(line, false);
379 | selection->EndOfLine(false);
380 | }
381 | }
382 | }
383 | }
384 |
385 | window = nullptr;
386 | if (SUCCEEDED(dte->get_MainWindow(&window))) {
387 | // Allow the DTE to make its main window the foreground
388 | HWND hWnd;
389 | window->get_HWnd((LONG *)&hWnd);
390 |
391 | DWORD processID;
392 | if (SUCCEEDED(GetWindowThreadProcessId(hWnd, &processID)))
393 | AllowSetForegroundWindow(processID);
394 |
395 | // Activate() set the window to visible and active (blinks in taskbar)
396 | window->Activate();
397 | }
398 |
399 | return true;
400 | }
401 |
402 | static bool VisualStudioOpenFile(
403 | const std::filesystem::path &visualStudioExecutablePath,
404 | const std::filesystem::path &solutionPath,
405 | const std::filesystem::path &filename,
406 | int line)
407 | {
408 | win::ComPtr dte = nullptr;
409 |
410 | std::wcout << "Looking for a running Visual Studio session." << std::endl;
411 |
412 | // TODO: If path does not exist pass empty, which will just try to match all windows with solution
413 | dte = FindRunningVisualStudioWithSolution(visualStudioExecutablePath, solutionPath);
414 |
415 | if (!dte) {
416 | std::wcout << "No appropriate running Visual Studio session not found, creating a new one." << std::endl;
417 |
418 | DisplayProgressbar();
419 |
420 | DWORD dwProcessId;
421 | if (!StartVisualStudioProcess(visualStudioExecutablePath, solutionPath, &dwProcessId)) {
422 | ClearProgressbar();
423 | return false;
424 | }
425 |
426 | int timeWaited = 0;
427 |
428 | while (timeWaited < TIMEOUT_MS) {
429 | dte = FindRunningVisualStudioWithPID(dwProcessId);
430 |
431 | if (dte)
432 | break;
433 |
434 | std::wcout << "Retrying to acquire DTE" << std::endl;
435 |
436 | Sleep(RETRY_INTERVAL_MS);
437 | timeWaited += RETRY_INTERVAL_MS;
438 | }
439 |
440 | ClearProgressbar();
441 |
442 | if (!dte)
443 | return false;
444 | }
445 | else {
446 | std::wcout << "Using the existing Visual Studio session." << std::endl;
447 | }
448 |
449 | return HaveRunningVisualStudioOpenFile(dte, filename, line);
450 | }
451 |
452 | int wmain(int argc, wchar_t* argv[]) {
453 |
454 | // We need this to properly display UTF16 text on the console
455 | _setmode(_fileno(stdout), _O_U16TEXT);
456 |
457 | if (argc != 3 && argc != 5) {
458 | std::wcerr << argc << ": wrong number of arguments\n" << "Usage: com.exe installationPath solutionPath [fileName lineNumber]" << std::endl;
459 | for (int i = 0; i < argc; i++) {
460 | std::wcerr << argv[i] << std::endl;
461 | }
462 | return EXIT_FAILURE;
463 | }
464 |
465 | if (FAILED(CoInitialize(nullptr))) {
466 | std::wcerr << "CoInitialize failed." << std::endl;
467 | return EXIT_FAILURE;
468 | }
469 |
470 | std::filesystem::path visualStudioExecutablePath = std::filesystem::absolute(argv[1]);
471 | std::filesystem::path solutionPath = std::filesystem::absolute(argv[2]);
472 |
473 | if (argc == 3) {
474 | VisualStudioOpenFile(visualStudioExecutablePath, solutionPath, L"", -1);
475 | return EXIT_SUCCESS;
476 | }
477 |
478 | std::filesystem::path fileName = std::filesystem::absolute(argv[3]);
479 | int lineNumber = std::stoi(argv[4]);
480 |
481 | VisualStudioOpenFile(visualStudioExecutablePath, solutionPath, fileName, lineNumber);
482 | return EXIT_SUCCESS;
483 | }
484 |
--------------------------------------------------------------------------------
/Editor/COMIntegration/COMIntegration~/ComPtr.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | namespace win
4 | {
5 | template
6 | class ComPtr;
7 |
8 | template
9 | class ComPtrRef
10 | {
11 | private:
12 | ComPtr& m_ComPtr;
13 |
14 | ComPtrRef(ComPtr& comPtr) :
15 | m_ComPtr(comPtr)
16 | {
17 | }
18 |
19 | friend class ComPtr;
20 |
21 | public:
22 | inline operator T**()
23 | {
24 | return m_ComPtr.ReleaseAndGetAddressOf();
25 | }
26 |
27 | inline operator void**()
28 | {
29 | return reinterpret_cast(m_ComPtr.ReleaseAndGetAddressOf());
30 | }
31 |
32 | inline T* operator*() throw ()
33 | {
34 | return m_ComPtr;
35 | }
36 |
37 | };
38 |
39 | template
40 | class ComPtr
41 | {
42 | private:
43 | T *ptr;
44 |
45 | public:
46 | inline ComPtr(void) : ptr(NULL) {}
47 | inline ~ComPtr(void) { this->Free(); }
48 |
49 | ComPtr(T *ptr)
50 | {
51 | if (NULL != (this->ptr = ptr))
52 | {
53 | this->ptr->AddRef();
54 | }
55 | }
56 |
57 | ComPtr(const ComPtr &ptr)
58 | {
59 | if (NULL != (this->ptr = ptr.ptr))
60 | {
61 | this->ptr->AddRef();
62 | }
63 | }
64 |
65 | inline bool operator!() const
66 | {
67 | return (NULL == this->ptr);
68 | }
69 |
70 | inline operator T*() const { return this->ptr; }
71 |
72 | inline T *operator->() const
73 | {
74 | //_assert(NULL != this->ptr);
75 | return this->ptr;
76 | }
77 |
78 | inline T &operator*()
79 | {
80 | //_assert(NULL != this->ptr);
81 | return *this->ptr;
82 | }
83 |
84 | inline ComPtrRef operator&()
85 | {
86 | return ComPtrRef(*this);
87 | }
88 |
89 | const ComPtr &operator=(T *ptr)
90 | {
91 | if (this->ptr != ptr)
92 | {
93 | this->Free();
94 |
95 | if (NULL != (this->ptr = ptr))
96 | {
97 | this->ptr->AddRef();
98 | }
99 | }
100 |
101 | return *this;
102 | }
103 |
104 | const ComPtr &operator=(const ComPtr &ptr)
105 | {
106 | if (this->ptr != ptr.ptr)
107 | {
108 | this->Free();
109 |
110 | if (NULL != (this->ptr = ptr.ptr))
111 | {
112 | this->ptr->AddRef();
113 | }
114 | }
115 |
116 | return *this;
117 | }
118 |
119 | void Free(void)
120 | {
121 | if (NULL != this->ptr)
122 | {
123 | this->ptr->Release();
124 | this->ptr = NULL;
125 | }
126 | }
127 |
128 | inline T** ReleaseAndGetAddressOf()
129 | {
130 | Free();
131 | return &ptr;
132 | }
133 |
134 | template
135 | inline HRESULT As(ComPtrRef p) const throw ()
136 | {
137 | return ptr->QueryInterface(__uuidof(U), p);
138 | }
139 |
140 | inline bool operator==(std::nullptr_t) const
141 | {
142 | return this->ptr == nullptr;
143 | }
144 |
145 | template
146 | inline bool operator==(U* other)
147 | {
148 | if (ptr == nullptr || other == nullptr)
149 | return ptr == other;
150 |
151 | ComPtr meUnknown;
152 | ComPtr otherUnknown;
153 |
154 | if (FAILED(this->ptr->QueryInterface(__uuidof(IUnknown), &meUnknown)))
155 | return false;
156 |
157 | if (FAILED(other->QueryInterface(__uuidof(IUnknown), &otherUnknown)))
158 | return false;
159 |
160 | return static_cast(meUnknown) == static_cast(otherUnknown);
161 | }
162 |
163 | template
164 | inline bool operator==(ComPtr& other)
165 | {
166 | return *this == static_cast(other);
167 | }
168 |
169 | inline bool operator!=(std::nullptr_t) const
170 | {
171 | return this->ptr != nullptr;
172 | }
173 |
174 | template
175 | inline bool operator!=(U* other)
176 | {
177 | return !(*this == other);
178 | }
179 |
180 | template
181 | inline bool operator!=(ComPtr& other)
182 | {
183 | return *this != static_cast(other);
184 | }
185 | };
186 | }
187 |
--------------------------------------------------------------------------------
/Editor/COMIntegration/COMIntegration~/howtobuild.txt:
--------------------------------------------------------------------------------
1 | Direct style:
2 | cl /EHsc /std:c++17 COMIntegration.cpp /link Shlwapi.lib /out:"..\Release\COMIntegration.exe"
3 |
4 | For a debug build with PDB:
5 | cl /EHsc /std:c++17 /Z7 /DEBUG:FULL COMIntegration.cpp /link Shlwapi.lib /out:"..\Release\COMIntegration.exe"
6 |
7 | CMake style:
8 | cmake ../COMIntegration~ -B ./build
9 | cmake --build ./build --config=release -- /p:OutDir=..
10 |
--------------------------------------------------------------------------------
/Editor/COMIntegration/Release.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a92aed9328d17a74d97b976bd0ba80da
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Editor/COMIntegration/Release/COMIntegration.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liyingsong99/com.unity.ide.cursor-et/2ff42a4c32bdbe24a12d24762bf43da7cdccc76a/Editor/COMIntegration/Release/COMIntegration.exe
--------------------------------------------------------------------------------
/Editor/COMIntegration/Release/COMIntegration.exe.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2d8ba14fe0a703a46a0112cb50c1f802
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Editor/Cli.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 | using System;
6 | using System.Linq;
7 | using Unity.CodeEditor;
8 |
9 | namespace Microsoft.Unity.VisualStudio.Editor
10 | {
11 | internal static class Cli
12 | {
13 | internal static void Log(string message)
14 | {
15 | // Use writeline here, instead of UnityEngine.Debug.Log to not include the stacktrace in the editor.log
16 | Console.WriteLine($"[VisualStudio.Editor.{nameof(Cli)}] {message}");
17 | }
18 |
19 | internal static string GetInstallationDetails(IVisualStudioInstallation installation)
20 | {
21 | return $"{installation.ToCodeEditorInstallation().Name} Path:{installation.Path}, LanguageVersionSupport:{installation.LatestLanguageVersionSupported} AnalyzersSupport:{installation.SupportsAnalyzers}";
22 | }
23 |
24 | internal static void GenerateSolutionWith(VisualStudioEditor vse, string installationPath)
25 | {
26 | if (vse != null && vse.TryGetVisualStudioInstallationForPath(installationPath, lookupDiscoveredInstallations: true, out var vsi))
27 | {
28 | Log($"Using {GetInstallationDetails(vsi)}");
29 | vse.SyncAll();
30 | }
31 | else
32 | {
33 | Log($"No Visual Studio installation found in ${installationPath}!");
34 | }
35 | }
36 |
37 | internal static void GenerateSolution()
38 | {
39 | if (CodeEditor.CurrentEditor is VisualStudioEditor vse)
40 | {
41 | Log($"Using default editor settings for Visual Studio installation");
42 | GenerateSolutionWith(vse, CodeEditor.CurrentEditorInstallation);
43 | }
44 | else
45 | {
46 | Log($"Visual Studio is not set as your default editor, looking for installations");
47 | try
48 | {
49 | var installations = Discovery
50 | .GetVisualStudioInstallations()
51 | .Cast()
52 | .OrderByDescending(vsi => !vsi.IsPrerelease)
53 | .ThenBy(vsi => vsi.Version)
54 | .ToArray();
55 |
56 | foreach(var vsi in installations)
57 | {
58 | Log($"Detected {GetInstallationDetails(vsi)}");
59 | }
60 |
61 | var installation = installations
62 | .FirstOrDefault();
63 |
64 | if (installation != null)
65 | {
66 | var current = CodeEditor.CurrentEditorInstallation;
67 | try
68 | {
69 | CodeEditor.SetExternalScriptEditor(installation.Path);
70 | GenerateSolutionWith(CodeEditor.CurrentEditor as VisualStudioEditor, installation.Path);
71 | }
72 | finally
73 | {
74 | CodeEditor.SetExternalScriptEditor(current);
75 | }
76 | } else
77 | {
78 | Log($"No Visual Studio installation found!");
79 | }
80 | }
81 | catch (Exception ex)
82 | {
83 | Log($"Error detecting Visual Studio installations: {ex}");
84 | }
85 | }
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/Editor/Cli.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 119670827e2bdfb4c8bab8ae43a91bf6
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Discovery.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Unity Technologies.
3 | * Copyright (c) Microsoft Corporation. All rights reserved.
4 | * Licensed under the MIT License. See License.txt in the project root for license information.
5 | *--------------------------------------------------------------------------------------------*/
6 |
7 | using System.Collections.Generic;
8 | using System.IO;
9 |
10 | namespace Microsoft.Unity.VisualStudio.Editor
11 | {
12 | internal static class Discovery
13 | {
14 | public static IEnumerable GetVisualStudioInstallations()
15 | {
16 | foreach (var installation in VisualStudioCursorInstallation.GetVisualStudioInstallations())
17 | yield return installation;
18 | foreach (var installation in VisualStudioCodiumInstallation.GetVisualStudioInstallations())
19 | yield return installation;
20 | }
21 |
22 | public static bool TryDiscoverInstallation(string editorPath, out IVisualStudioInstallation installation)
23 | {
24 | try
25 | {
26 | if (VisualStudioCursorInstallation.TryDiscoverInstallation(editorPath, out installation))
27 | return true;
28 | if (VisualStudioCodiumInstallation.TryDiscoverInstallation(editorPath, out installation))
29 | return true;
30 | }
31 | catch (IOException)
32 | {
33 | installation = null;
34 | }
35 |
36 | return false;
37 | }
38 |
39 | public static void Initialize()
40 | {
41 | VisualStudioCursorInstallation.Initialize();
42 | VisualStudioCodiumInstallation.Initialize();
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Editor/Discovery.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b9e6525873b60214bac98a2611001065
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/FileUtility.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Unity Technologies.
3 | * Copyright (c) Microsoft Corporation. All rights reserved.
4 | * Licensed under the MIT License. See License.txt in the project root for license information.
5 | *--------------------------------------------------------------------------------------------*/
6 | using System;
7 | using System.IO;
8 | using UnityEngine;
9 |
10 | namespace Microsoft.Unity.VisualStudio.Editor
11 | {
12 | internal static class FileUtility
13 | {
14 | public const char WinSeparator = '\\';
15 | public const char UnixSeparator = '/';
16 |
17 | public static string GetPackageAssetFullPath(params string[] components)
18 | {
19 | // Unity has special IO handling of Packages and will resolve those path to the right package location
20 | return Path.GetFullPath(Path.Combine("Packages", "com.unity.ide.visualstudio", Path.Combine(components)));
21 | }
22 |
23 | public static string GetAssetFullPath(string asset)
24 | {
25 | var basePath = Path.GetFullPath(Path.Combine(Application.dataPath, ".."));
26 | return Path.GetFullPath(Path.Combine(basePath, NormalizePathSeparators(asset)));
27 | }
28 |
29 | public static string NormalizePathSeparators(this string path)
30 | {
31 | if (string.IsNullOrEmpty(path))
32 | return path;
33 |
34 | if (Path.DirectorySeparatorChar == WinSeparator)
35 | path = path.Replace(UnixSeparator, WinSeparator);
36 | if (Path.DirectorySeparatorChar == UnixSeparator)
37 | path = path.Replace(WinSeparator, UnixSeparator);
38 |
39 | return path.Replace(string.Concat(WinSeparator, WinSeparator), WinSeparator.ToString());
40 | }
41 |
42 | public static string NormalizeWindowsToUnix(this string path)
43 | {
44 | if (string.IsNullOrEmpty(path))
45 | return path;
46 |
47 | return path.Replace(WinSeparator, UnixSeparator);
48 | }
49 |
50 | internal static bool IsFileInProjectRootDirectory(string fileName)
51 | {
52 | var relative = MakeRelativeToProjectPath(fileName);
53 | if (string.IsNullOrEmpty(relative))
54 | return false;
55 |
56 | return relative == Path.GetFileName(relative);
57 | }
58 |
59 | public static string MakeAbsolutePath(this string path)
60 | {
61 | if (string.IsNullOrEmpty(path)) { return string.Empty; }
62 | return Path.IsPathRooted(path) ? path : Path.GetFullPath(path);
63 | }
64 |
65 | // returns null if outside of the project scope
66 | internal static string MakeRelativeToProjectPath(string fileName)
67 | {
68 | var basePath = Path.GetFullPath(Path.Combine(Application.dataPath, ".."));
69 | fileName = NormalizePathSeparators(fileName);
70 |
71 | if (!Path.IsPathRooted(fileName))
72 | fileName = Path.Combine(basePath, fileName);
73 |
74 | if (!fileName.StartsWith(basePath, StringComparison.OrdinalIgnoreCase))
75 | return null;
76 |
77 | return fileName
78 | .Substring(basePath.Length)
79 | .Trim(Path.DirectorySeparatorChar);
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Editor/FileUtility.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: fa3cfc2997f49b943b10f66c511ab9b7
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Image.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 | using System;
6 | using System.IO;
7 |
8 | namespace Microsoft.Unity.VisualStudio.Editor
9 | {
10 | public sealed class Image : IDisposable
11 | {
12 |
13 | long position;
14 | Stream stream;
15 |
16 | Image(Stream stream)
17 | {
18 | this.stream = stream;
19 | this.position = stream.Position;
20 | this.stream.Position = 0;
21 | }
22 |
23 | bool Advance(int length)
24 | {
25 | if (stream.Position + length >= stream.Length)
26 | return false;
27 |
28 | stream.Seek(length, SeekOrigin.Current);
29 | return true;
30 | }
31 |
32 | bool MoveTo(uint position)
33 | {
34 | if (position >= stream.Length)
35 | return false;
36 |
37 | stream.Position = position;
38 | return true;
39 | }
40 |
41 | void IDisposable.Dispose()
42 | {
43 | stream.Position = position;
44 | }
45 |
46 | ushort ReadUInt16()
47 | {
48 | return (ushort)(stream.ReadByte()
49 | | (stream.ReadByte() << 8));
50 | }
51 |
52 | uint ReadUInt32()
53 | {
54 | return (uint)(stream.ReadByte()
55 | | (stream.ReadByte() << 8)
56 | | (stream.ReadByte() << 16)
57 | | (stream.ReadByte() << 24));
58 | }
59 |
60 | bool IsManagedAssembly()
61 | {
62 | if (stream.Length < 318)
63 | return false;
64 | if (ReadUInt16() != 0x5a4d)
65 | return false;
66 | if (!Advance(58))
67 | return false;
68 | if (!MoveTo(ReadUInt32()))
69 | return false;
70 | if (ReadUInt32() != 0x00004550)
71 | return false;
72 | if (!Advance(20))
73 | return false;
74 | if (!Advance(ReadUInt16() == 0x20b ? 222 : 206))
75 | return false;
76 |
77 | return ReadUInt32() != 0;
78 | }
79 |
80 | public static bool IsAssembly(string file)
81 | {
82 | if (file == null)
83 | throw new ArgumentNullException("file");
84 |
85 | using (var stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
86 | return IsAssembly(stream);
87 | }
88 |
89 | public static bool IsAssembly(Stream stream)
90 | {
91 | if (stream == null)
92 | throw new ArgumentNullException(nameof(stream));
93 | if (!stream.CanRead)
94 | throw new ArgumentException(nameof(stream));
95 | if (!stream.CanSeek)
96 | throw new ArgumentException(nameof(stream));
97 |
98 | using (var image = new Image(stream))
99 | return image.IsManagedAssembly();
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/Editor/Image.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 8c2650c3659765147bb1ecb95c04f931
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/KnownAssemblies.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 |
6 | namespace Microsoft.Unity.VisualStudio.Editor
7 | {
8 | internal static class KnownAssemblies
9 | {
10 | public const string Bridge = "SyntaxTree.VisualStudio.Unity.Bridge";
11 | public const string Messaging = "SyntaxTree.VisualStudio.Unity.Messaging";
12 | public const string UnityVS = "UnityVS.VersionSpecific";
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Editor/KnownAssemblies.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 876a40e26248b4d49bb090978f2428d8
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Messaging.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: edf1e631e73499647a7b28951b9910ef
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Editor/Messaging/Deserializer.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 | using System.IO;
6 | using System.Text;
7 |
8 | namespace Microsoft.Unity.VisualStudio.Editor.Messaging
9 | {
10 | internal class Deserializer
11 | {
12 | private readonly BinaryReader _reader;
13 |
14 | public Deserializer(byte[] buffer)
15 | {
16 | _reader = new BinaryReader(new MemoryStream(buffer));
17 | }
18 |
19 | public int ReadInt32()
20 | {
21 | return _reader.ReadInt32();
22 | }
23 |
24 | public string ReadString()
25 | {
26 | var length = ReadInt32();
27 | return length > 0
28 | ? Encoding.UTF8.GetString(_reader.ReadBytes(length))
29 | : "";
30 | }
31 |
32 | public bool CanReadMore()
33 | {
34 | return _reader.BaseStream.Position < _reader.BaseStream.Length;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Editor/Messaging/Deserializer.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 4158afe0f3ba24543b5573829095c090
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Messaging/ExceptionEventArgs.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 | using System;
6 |
7 | namespace Microsoft.Unity.VisualStudio.Editor.Messaging
8 | {
9 | internal class ExceptionEventArgs
10 | {
11 | public Exception Exception { get; }
12 |
13 | public ExceptionEventArgs(Exception exception)
14 | {
15 | Exception = exception;
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Editor/Messaging/ExceptionEventArgs.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a60ca3635cca04b42a8b5b83471bbb2a
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Messaging/Message.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 | using System.Globalization;
6 | using System.Net;
7 |
8 | namespace Microsoft.Unity.VisualStudio.Editor.Messaging
9 | {
10 | internal class Message
11 | {
12 | public MessageType Type { get; set; }
13 |
14 | public string Value { get; set; }
15 |
16 | public IPEndPoint Origin { get; set; }
17 |
18 | public override string ToString()
19 | {
20 | return string.Format(CultureInfo.InvariantCulture, "", Type, Value);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Editor/Messaging/Message.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b76af5429092a58459912042d3474e74
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Messaging/MessageEventArgs.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 | namespace Microsoft.Unity.VisualStudio.Editor.Messaging
6 | {
7 | internal class MessageEventArgs
8 | {
9 | public Message Message
10 | {
11 | get;
12 | }
13 |
14 | public MessageEventArgs(Message message)
15 | {
16 | Message = message;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Editor/Messaging/MessageEventArgs.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 53f72b24585bb564ebdf15944b1a9a78
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Messaging/MessageType.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 | namespace Microsoft.Unity.VisualStudio.Editor.Messaging
6 | {
7 | internal enum MessageType
8 | {
9 | None = 0,
10 |
11 | Ping,
12 | Pong,
13 |
14 | Play,
15 | Stop,
16 | Pause,
17 | Unpause,
18 |
19 | Build,
20 | Refresh,
21 |
22 | Info,
23 | Error,
24 | Warning,
25 |
26 | Open,
27 | Opened,
28 |
29 | Version,
30 | UpdatePackage,
31 |
32 | ProjectPath,
33 |
34 | // This message is a technical one for big messages, not intended to be used directly
35 | Tcp,
36 |
37 | RunStarted,
38 | RunFinished,
39 | TestStarted,
40 | TestFinished,
41 | TestListRetrieved,
42 |
43 | RetrieveTestList,
44 | ExecuteTests,
45 |
46 | ShowUsage
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Editor/Messaging/MessageType.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: bca6d6cb5c976544d83d550e80d0ab10
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Messaging/Messenger.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 | using System;
6 | using System.Net;
7 | using System.Net.Sockets;
8 |
9 | namespace Microsoft.Unity.VisualStudio.Editor.Messaging
10 | {
11 | internal class Messager : IDisposable
12 | {
13 | public event EventHandler ReceiveMessage;
14 | public event EventHandler MessagerException;
15 |
16 | private readonly UdpSocket _socket;
17 | private readonly object _disposeLock = new object();
18 | private bool _disposed;
19 |
20 | #if UNITY_EDITOR_WIN
21 | [System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)]
22 | private static extern bool SetHandleInformation(IntPtr hObject, HandleFlags dwMask, HandleFlags dwFlags);
23 |
24 | [Flags]
25 | private enum HandleFlags: uint
26 | {
27 | None = 0,
28 | Inherit = 1,
29 | ProtectFromClose = 2
30 | }
31 | #endif
32 |
33 | protected Messager(int port)
34 | {
35 | _socket = new UdpSocket();
36 | _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse, false);
37 | _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
38 |
39 | #if UNITY_EDITOR_WIN
40 | // Explicitely disable inheritance for our UDP socket handle
41 | // We found that Unity is creating a fork when importing new assets that can clone our socket
42 | SetHandleInformation(_socket.Handle, HandleFlags.Inherit, HandleFlags.None);
43 | #endif
44 |
45 | _socket.Bind(IPAddress.Any, port);
46 |
47 | BeginReceiveMessage();
48 | }
49 |
50 | private void BeginReceiveMessage()
51 | {
52 | var buffer = new byte[UdpSocket.BufferSize];
53 | var any = UdpSocket.Any();
54 |
55 | try
56 | {
57 | lock (_disposeLock)
58 | {
59 | if (_disposed)
60 | return;
61 |
62 | _socket.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref any, ReceiveMessageCallback, buffer);
63 | }
64 | }
65 | catch (SocketException se)
66 | {
67 | MessagerException?.Invoke(this, new ExceptionEventArgs(se));
68 |
69 | BeginReceiveMessage();
70 | }
71 | catch (ObjectDisposedException)
72 | {
73 | }
74 | }
75 |
76 | private void ReceiveMessageCallback(IAsyncResult result)
77 | {
78 | try
79 | {
80 | var endPoint = UdpSocket.Any();
81 |
82 | lock (_disposeLock)
83 | {
84 | if (_disposed)
85 | return;
86 |
87 | _socket.EndReceiveFrom(result, ref endPoint);
88 | }
89 |
90 | var message = DeserializeMessage(UdpSocket.BufferFor(result));
91 | if (message != null)
92 | {
93 | message.Origin = (IPEndPoint)endPoint;
94 |
95 | if (IsValidTcpMessage(message, out var port, out var bufferSize))
96 | {
97 | // switch to TCP mode to handle big messages
98 | TcpClient.Queue(message.Origin.Address, port, bufferSize, buffer =>
99 | {
100 | var originalMessage = DeserializeMessage(buffer);
101 | originalMessage.Origin = message.Origin;
102 | ReceiveMessage?.Invoke(this, new MessageEventArgs(originalMessage));
103 | });
104 | }
105 | else
106 | {
107 | ReceiveMessage?.Invoke(this, new MessageEventArgs(message));
108 | }
109 | }
110 | }
111 | catch (ObjectDisposedException)
112 | {
113 | return;
114 | }
115 | catch (Exception e)
116 | {
117 | RaiseMessagerException(e);
118 | }
119 |
120 | BeginReceiveMessage();
121 | }
122 |
123 | private static bool IsValidTcpMessage(Message message, out int port, out int bufferSize)
124 | {
125 | port = 0;
126 | bufferSize = 0;
127 | if (message.Value == null)
128 | return false;
129 | if (message.Type != MessageType.Tcp)
130 | return false;
131 | var parts = message.Value.Split(':');
132 | if (parts.Length != 2)
133 | return false;
134 | if (!int.TryParse(parts[0], out port))
135 | return false;
136 | return int.TryParse(parts[1], out bufferSize);
137 | }
138 |
139 | private void RaiseMessagerException(Exception e)
140 | {
141 | MessagerException?.Invoke(this, new ExceptionEventArgs(e));
142 | }
143 |
144 | private static Message MessageFor(MessageType type, string value)
145 | {
146 | return new Message { Type = type, Value = value };
147 | }
148 |
149 | public void SendMessage(IPEndPoint target, MessageType type, string value = "")
150 | {
151 | var message = MessageFor(type, value);
152 | var buffer = SerializeMessage(message);
153 |
154 | try
155 | {
156 | lock (_disposeLock)
157 | {
158 | if (_disposed)
159 | return;
160 |
161 | if (buffer.Length >= UdpSocket.BufferSize)
162 | {
163 | // switch to TCP mode to handle big messages
164 | var port = TcpListener.Queue(buffer);
165 | if (port > 0)
166 | {
167 | // success, replace original message with "switch to tcp" marker + port information + buffer length
168 | message = MessageFor(MessageType.Tcp, string.Concat(port, ':', buffer.Length));
169 | buffer = SerializeMessage(message);
170 | }
171 | }
172 |
173 | _socket.BeginSendTo(buffer, 0, Math.Min(buffer.Length, UdpSocket.BufferSize), SocketFlags.None, target, SendMessageCallback, null);
174 | }
175 | }
176 | catch (SocketException se)
177 | {
178 | MessagerException?.Invoke(this, new ExceptionEventArgs(se));
179 | }
180 | }
181 |
182 | private void SendMessageCallback(IAsyncResult result)
183 | {
184 | try
185 | {
186 | lock (_disposeLock)
187 | {
188 | if (_disposed)
189 | return;
190 |
191 | _socket.EndSendTo(result);
192 | }
193 | }
194 | catch (SocketException se)
195 | {
196 | MessagerException?.Invoke(this, new ExceptionEventArgs(se));
197 | }
198 | catch (ObjectDisposedException)
199 | {
200 | }
201 | }
202 |
203 | private static byte[] SerializeMessage(Message message)
204 | {
205 | var serializer = new Serializer();
206 | serializer.WriteInt32((int)message.Type);
207 | serializer.WriteString(message.Value);
208 |
209 | return serializer.Buffer();
210 | }
211 |
212 | private static Message DeserializeMessage(byte[] buffer)
213 | {
214 | if (buffer.Length < 4)
215 | return null;
216 |
217 | var deserializer = new Deserializer(buffer);
218 | var type = (MessageType)deserializer.ReadInt32();
219 | var value = deserializer.ReadString();
220 |
221 | return new Message { Type = type, Value = value };
222 | }
223 |
224 | public static Messager BindTo(int port)
225 | {
226 | return new Messager(port);
227 | }
228 |
229 | public void Dispose()
230 | {
231 | lock (_disposeLock)
232 | {
233 | _disposed = true;
234 | _socket.Close();
235 | }
236 | }
237 | }
238 | }
239 |
--------------------------------------------------------------------------------
/Editor/Messaging/Messenger.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 66e2b0fc0f2800148be21743edd56c07
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Messaging/Serializer.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 | using System.IO;
6 | using System.Text;
7 |
8 | namespace Microsoft.Unity.VisualStudio.Editor.Messaging
9 | {
10 | internal class Serializer
11 | {
12 | private readonly MemoryStream _stream;
13 | private readonly BinaryWriter _writer;
14 |
15 | public Serializer()
16 | {
17 | _stream = new MemoryStream();
18 | _writer = new BinaryWriter(_stream);
19 | }
20 |
21 | public void WriteInt32(int i)
22 | {
23 | _writer.Write(i);
24 | }
25 |
26 | public void WriteString(string s)
27 | {
28 | var bytes = Encoding.UTF8.GetBytes(s ?? "");
29 | if (bytes.Length > 0)
30 | {
31 | _writer.Write(bytes.Length);
32 | _writer.Write(bytes);
33 | }
34 | else
35 | _writer.Write(0);
36 | }
37 |
38 | public byte[] Buffer()
39 | {
40 | return _stream.ToArray();
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Editor/Messaging/Serializer.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1903487170519b940925e4d60121d63b
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Messaging/TcpClient.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 | using System;
6 | using System.Net;
7 | using System.Net.Sockets;
8 | using System.Threading;
9 |
10 | namespace Microsoft.Unity.VisualStudio.Editor.Messaging
11 | {
12 | internal class TcpClient
13 | {
14 | private const int ConnectOrReadTimeoutMilliseconds = 5000;
15 |
16 | private class State
17 | {
18 | public System.Net.Sockets.TcpClient TcpClient;
19 | public NetworkStream NetworkStream;
20 | public byte[] Buffer;
21 | public Action OnBufferAvailable;
22 | }
23 |
24 | public static void Queue(IPAddress address, int port, int bufferSize, Action onBufferAvailable)
25 | {
26 | var client = new System.Net.Sockets.TcpClient();
27 | var state = new State {OnBufferAvailable = onBufferAvailable, TcpClient = client, Buffer = new byte[bufferSize]};
28 |
29 | try
30 | {
31 | ThreadPool.QueueUserWorkItem(_ =>
32 | {
33 | var handle = client.BeginConnect(address, port, OnClientConnected, state);
34 | if (!handle.AsyncWaitHandle.WaitOne(ConnectOrReadTimeoutMilliseconds))
35 | Cleanup(state);
36 | });
37 | }
38 | catch (Exception)
39 | {
40 | Cleanup(state);
41 | }
42 | }
43 |
44 | private static void OnClientConnected(IAsyncResult result)
45 | {
46 | var state = (State)result.AsyncState;
47 |
48 | try
49 | {
50 | state.TcpClient.EndConnect(result);
51 | state.NetworkStream = state.TcpClient.GetStream();
52 | var handle = state.NetworkStream.BeginRead(state.Buffer, 0, state.Buffer.Length, OnEndRead, state);
53 | if (!handle.AsyncWaitHandle.WaitOne(ConnectOrReadTimeoutMilliseconds))
54 | Cleanup(state);
55 | }
56 | catch (Exception)
57 | {
58 | Cleanup(state);
59 | }
60 | }
61 |
62 | private static void OnEndRead(IAsyncResult result)
63 | {
64 | var state = (State)result.AsyncState;
65 |
66 | try
67 | {
68 | var count = state.NetworkStream.EndRead(result);
69 | if (count == state.Buffer.Length)
70 | state.OnBufferAvailable?.Invoke(state.Buffer);
71 | }
72 | catch (Exception)
73 | {
74 | // Ignore and cleanup
75 | }
76 | finally
77 | {
78 | Cleanup(state);
79 | }
80 | }
81 |
82 | private static void Cleanup(State state)
83 | {
84 | state.NetworkStream?.Dispose();
85 | state.TcpClient?.Close();
86 |
87 | state.NetworkStream = null;
88 | state.TcpClient = null;
89 | state.Buffer = null;
90 | state.OnBufferAvailable = null;
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/Editor/Messaging/TcpClient.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e1eb5180dca1e6e4c80f16fe1e89ab5f
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Messaging/TcpListener.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 | using System;
6 | using System.Net;
7 | using System.Threading;
8 |
9 | namespace Microsoft.Unity.VisualStudio.Editor.Messaging
10 | {
11 | internal class TcpListener
12 | {
13 | private const int ListenTimeoutMilliseconds = 5000;
14 |
15 | private class State
16 | {
17 | public System.Net.Sockets.TcpListener TcpListener;
18 | public byte[] Buffer;
19 | }
20 |
21 | public static int Queue(byte[] buffer)
22 | {
23 | var tcpListener = new System.Net.Sockets.TcpListener(IPAddress.Any, 0);
24 | var state = new State {Buffer = buffer, TcpListener = tcpListener};
25 |
26 | try
27 | {
28 | tcpListener.Start();
29 |
30 | int port = ((IPEndPoint)tcpListener.LocalEndpoint).Port;
31 |
32 | ThreadPool.QueueUserWorkItem(_ =>
33 | {
34 | bool listening = true;
35 |
36 | while (listening)
37 | {
38 | var handle = tcpListener.BeginAcceptTcpClient(OnIncomingConnection, state);
39 | listening = handle.AsyncWaitHandle.WaitOne(ListenTimeoutMilliseconds);
40 | }
41 |
42 | Cleanup(state);
43 | });
44 |
45 | return port;
46 | }
47 | catch (Exception)
48 | {
49 | Cleanup(state);
50 | return -1;
51 | }
52 | }
53 |
54 | private static void OnIncomingConnection(IAsyncResult result)
55 | {
56 | var state = (State)result.AsyncState;
57 |
58 | try
59 | {
60 | using (var client = state.TcpListener.EndAcceptTcpClient(result))
61 | {
62 | using (var stream = client.GetStream())
63 | {
64 | stream.Write(state.Buffer, 0, state.Buffer.Length);
65 | }
66 | }
67 | }
68 | catch (Exception)
69 | {
70 | // Ignore and cleanup
71 | }
72 | }
73 |
74 | private static void Cleanup(State state)
75 | {
76 | state.TcpListener?.Stop();
77 |
78 | state.TcpListener = null;
79 | state.Buffer = null;
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Editor/Messaging/TcpListener.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 4aac9ec2e87e1df429254b46ea93ee54
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Messaging/UdpSocket.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 | using System;
6 | using System.Net;
7 | using System.Net.Sockets;
8 |
9 | namespace Microsoft.Unity.VisualStudio.Editor.Messaging
10 | {
11 | internal class UdpSocket : Socket
12 | {
13 | // Maximum UDP payload is 65507 bytes.
14 | // TCP mode will be used when the payload is bigger than this BufferSize
15 | public const int BufferSize = 1024 * 8;
16 |
17 | internal UdpSocket()
18 | : base(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
19 | {
20 | SetIOControl();
21 | }
22 |
23 | public void Bind(IPAddress address, int port = 0)
24 | {
25 | Bind(new IPEndPoint(address ?? IPAddress.Any, port));
26 | }
27 |
28 | private void SetIOControl()
29 | {
30 | #if UNITY_EDITOR_WIN
31 | try
32 | {
33 | const int SIO_UDP_CONNRESET = -1744830452;
34 |
35 | IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, new byte[0]);
36 | }
37 | catch
38 | {
39 | // fallback
40 | }
41 | #endif
42 | }
43 |
44 | public static byte[] BufferFor(IAsyncResult result)
45 | {
46 | return (byte[])result.AsyncState;
47 | }
48 |
49 | public static EndPoint Any()
50 | {
51 | return new IPEndPoint(IPAddress.Any, 0);
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Editor/Messaging/UdpSocket.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 982947b2e8aa7aa439af7f60a14251b8
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Plugins.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2267e5a8629ec3e4f954d2e5112e3294
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Editor/Plugins/AppleEventIntegration.bundle.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: acefa48680f354de1bfab8daa3d26916
3 | folderAsset: yes
4 | PluginImporter:
5 | externalObjects: {}
6 | serializedVersion: 2
7 | iconMap: {}
8 | executionOrder: {}
9 | defineConstraints: []
10 | isPreloaded: 0
11 | isOverridable: 0
12 | isExplicitlyReferenced: 0
13 | validateReferences: 1
14 | platformData:
15 | - first:
16 | Any:
17 | second:
18 | enabled: 0
19 | settings: {}
20 | - first:
21 | Editor: Editor
22 | second:
23 | enabled: 1
24 | settings:
25 | DefaultValueInitialized: true
26 | - first:
27 | Standalone: OSXUniversal
28 | second:
29 | enabled: 1
30 | settings:
31 | CPU: AnyCPU
32 | userData:
33 | assetBundleName:
34 | assetBundleVariant:
35 |
--------------------------------------------------------------------------------
/Editor/Plugins/AppleEventIntegration.bundle/Contents.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 79aba80d0afc348ce868931a58e6b8f6
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Editor/Plugins/AppleEventIntegration.bundle/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildMachineOSBuild
6 | 19G2021
7 | CFBundleDevelopmentRegion
8 | en
9 | CFBundleExecutable
10 | AppleEventIntegration
11 | CFBundleIdentifier
12 | com.unity.visualstudio.AppleEventIntegration
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | AppleEventIntegration
17 | CFBundlePackageType
18 | BNDL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSupportedPlatforms
22 |
23 | MacOSX
24 |
25 | CFBundleVersion
26 | 1
27 | DTCompiler
28 | com.apple.compilers.llvm.clang.1_0
29 | DTPlatformBuild
30 | 12B45b
31 | DTPlatformName
32 | macosx
33 | DTPlatformVersion
34 | 11.0
35 | DTSDKBuild
36 | 20A2408
37 | DTSDKName
38 | macosx11.0
39 | DTXcode
40 | 1220
41 | DTXcodeBuild
42 | 12B45b
43 | LSMinimumSystemVersion
44 | 10.13
45 | NSHumanReadableCopyright
46 | Copyright © 2019 Unity. All rights reserved.
47 |
48 |
49 |
--------------------------------------------------------------------------------
/Editor/Plugins/AppleEventIntegration.bundle/Contents/Info.plist.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e62045032126d4c01b7e654924b1225b
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Editor/Plugins/AppleEventIntegration.bundle/Contents/MacOS.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 6093efd10ea204d6eaa82bac5462c5c1
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Editor/Plugins/AppleEventIntegration.bundle/Contents/MacOS/AppleEventIntegration:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liyingsong99/com.unity.ide.cursor-et/2ff42a4c32bdbe24a12d24762bf43da7cdccc76a/Editor/Plugins/AppleEventIntegration.bundle/Contents/MacOS/AppleEventIntegration
--------------------------------------------------------------------------------
/Editor/Plugins/AppleEventIntegration.bundle/Contents/MacOS/AppleEventIntegration.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 0bdddcfce7aa54eeb95d4b1710a978a7
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Editor/Plugins/AppleEventIntegration.bundle/Contents/_CodeSignature.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 67a21ddb5bbef4b868123014886593ee
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Editor/Plugins/AppleEventIntegration.bundle/Contents/_CodeSignature/CodeResources:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | files
6 |
7 | files2
8 |
9 | rules
10 |
11 | ^Resources/
12 |
13 | ^Resources/.*\.lproj/
14 |
15 | optional
16 |
17 | weight
18 | 1000
19 |
20 | ^Resources/.*\.lproj/locversion.plist$
21 |
22 | omit
23 |
24 | weight
25 | 1100
26 |
27 | ^Resources/Base\.lproj/
28 |
29 | weight
30 | 1010
31 |
32 | ^version.plist$
33 |
34 |
35 | rules2
36 |
37 | .*\.dSYM($|/)
38 |
39 | weight
40 | 11
41 |
42 | ^(.*/)?\.DS_Store$
43 |
44 | omit
45 |
46 | weight
47 | 2000
48 |
49 | ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/
50 |
51 | nested
52 |
53 | weight
54 | 10
55 |
56 | ^.*
57 |
58 | ^Info\.plist$
59 |
60 | omit
61 |
62 | weight
63 | 20
64 |
65 | ^PkgInfo$
66 |
67 | omit
68 |
69 | weight
70 | 20
71 |
72 | ^Resources/
73 |
74 | weight
75 | 20
76 |
77 | ^Resources/.*\.lproj/
78 |
79 | optional
80 |
81 | weight
82 | 1000
83 |
84 | ^Resources/.*\.lproj/locversion.plist$
85 |
86 | omit
87 |
88 | weight
89 | 1100
90 |
91 | ^Resources/Base\.lproj/
92 |
93 | weight
94 | 1010
95 |
96 | ^[^/]+$
97 |
98 | nested
99 |
100 | weight
101 | 10
102 |
103 | ^embedded\.provisionprofile$
104 |
105 | weight
106 | 20
107 |
108 | ^version\.plist$
109 |
110 | weight
111 | 20
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/Editor/Plugins/AppleEventIntegration.bundle/Contents/_CodeSignature/CodeResources.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ab18804e7a0584c00a8a9134901a725a
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Editor/ProcessRunner.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 |
6 | using System;
7 | using System.Diagnostics;
8 | using System.Text;
9 | using System.Threading;
10 | using System.Threading.Tasks;
11 | using System.Runtime.InteropServices;
12 | using System.ComponentModel;
13 | using Debug = UnityEngine.Debug;
14 | using System.IO;
15 | using SimpleJSON;
16 | using System.Collections.Generic;
17 | using System.Linq;
18 |
19 | namespace Microsoft.Unity.VisualStudio.Editor
20 | {
21 | internal class ProcessRunnerResult
22 | {
23 | public bool Success { get; set; }
24 | public string Output { get; set; }
25 | public string Error { get; set; }
26 | }
27 |
28 | internal static class ProcessRunner
29 | {
30 | public const int DefaultTimeoutInMilliseconds = 300000;
31 |
32 | public static ProcessStartInfo ProcessStartInfoFor(string filename, string arguments, bool redirect = true, bool shell = false)
33 | {
34 | return new ProcessStartInfo
35 | {
36 | UseShellExecute = shell,
37 | CreateNoWindow = true,
38 | RedirectStandardOutput = redirect,
39 | RedirectStandardError = redirect,
40 | FileName = filename,
41 | Arguments = arguments
42 | };
43 | }
44 |
45 | public static void Start(string filename, string arguments)
46 | {
47 | Start(ProcessStartInfoFor(filename, arguments, false));
48 | }
49 |
50 | public static void Start(ProcessStartInfo processStartInfo)
51 | {
52 | var process = new Process { StartInfo = processStartInfo };
53 |
54 | using (process)
55 | {
56 | process.Start();
57 | }
58 | }
59 |
60 | public static ProcessRunnerResult StartAndWaitForExit(string filename, string arguments, int timeoutms = DefaultTimeoutInMilliseconds, Action onOutputReceived = null)
61 | {
62 | return StartAndWaitForExit(ProcessStartInfoFor(filename, arguments), timeoutms, onOutputReceived);
63 | }
64 |
65 | public static ProcessRunnerResult StartAndWaitForExit(ProcessStartInfo processStartInfo, int timeoutms = DefaultTimeoutInMilliseconds, Action onOutputReceived = null)
66 | {
67 | var process = new Process { StartInfo = processStartInfo };
68 |
69 | using (process)
70 | {
71 | var sbOutput = new StringBuilder();
72 | var sbError = new StringBuilder();
73 |
74 | var outputSource = new TaskCompletionSource();
75 | var errorSource = new TaskCompletionSource();
76 |
77 | process.OutputDataReceived += (_, e) =>
78 | {
79 | Append(sbOutput, e.Data, outputSource);
80 | if (onOutputReceived != null && e.Data != null)
81 | onOutputReceived(e.Data);
82 | };
83 | process.ErrorDataReceived += (_, e) => Append(sbError, e.Data, errorSource);
84 |
85 | process.Start();
86 | process.BeginOutputReadLine();
87 | process.BeginErrorReadLine();
88 |
89 | var run = Task.Run(() => process.WaitForExit(timeoutms));
90 | var processTask = Task.WhenAll(run, outputSource.Task, errorSource.Task);
91 |
92 | if (Task.WhenAny(Task.Delay(timeoutms), processTask).Result == processTask && run.Result)
93 | return new ProcessRunnerResult {Success = true, Error = sbError.ToString(), Output = sbOutput.ToString()};
94 |
95 | try
96 | {
97 | process.Kill();
98 | }
99 | catch
100 | {
101 | /* ignore */
102 | }
103 |
104 | return new ProcessRunnerResult {Success = false, Error = sbError.ToString(), Output = sbOutput.ToString()};
105 | }
106 | }
107 |
108 | private static void Append(StringBuilder sb, string data, TaskCompletionSource taskSource)
109 | {
110 | if (data == null)
111 | {
112 | taskSource.SetResult(true);
113 | return;
114 | }
115 |
116 | sb?.Append(data);
117 | }
118 |
119 | public static string[] GetProcessWorkspaces(Process process)
120 | {
121 | if (process == null)
122 | return null;
123 |
124 | try
125 | {
126 | var workspaces = new List();
127 | var userProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
128 | var cursorStoragePath = Path.Combine(userProfile, "AppData", "Roaming", "cursor", "User", "workspaceStorage");
129 |
130 | if (Directory.Exists(cursorStoragePath))
131 | {
132 | foreach (var workspaceDir in Directory.GetDirectories(cursorStoragePath))
133 | {
134 | try
135 | {
136 | var workspaceStatePath = Path.Combine(workspaceDir, "workspace.json");
137 | if (File.Exists(workspaceStatePath))
138 | {
139 | var content = File.ReadAllText(workspaceStatePath);
140 | if (!string.IsNullOrEmpty(content))
141 | {
142 | var workspace = JSONNode.Parse(content);
143 | if (workspace != null)
144 | {
145 | var folder = workspace["folder"];
146 | if (folder != null && !string.IsNullOrEmpty(folder.Value))
147 | {
148 | var workspacePath = folder.Value;
149 | if (workspacePath.StartsWith("file:///"))
150 | {
151 | workspacePath = Uri.UnescapeDataString(workspacePath.Substring(8));
152 | workspaces.Add(workspacePath);
153 | }
154 | }
155 | }
156 | }
157 | }
158 |
159 | var windowStatePath = Path.Combine(workspaceDir, "window.json");
160 | if (File.Exists(windowStatePath))
161 | {
162 | var content = File.ReadAllText(windowStatePath);
163 | if (!string.IsNullOrEmpty(content))
164 | {
165 | var windowState = JSONNode.Parse(content);
166 | if (windowState != null)
167 | {
168 | var workspace = windowState["workspace"];
169 | if (workspace != null && !string.IsNullOrEmpty(workspace.Value))
170 | {
171 | var workspacePath = workspace.Value;
172 | if (workspacePath.StartsWith("file:///"))
173 | {
174 | workspacePath = Uri.UnescapeDataString(workspacePath.Substring(8));
175 | workspaces.Add(workspacePath);
176 | }
177 | }
178 | }
179 | }
180 | }
181 | }
182 | catch (Exception ex)
183 | {
184 | Debug.LogWarning($"[Cursor] Error reading workspace state file: {ex.Message}");
185 | continue;
186 | }
187 | }
188 | }
189 |
190 | return workspaces.Distinct().ToArray();
191 | }
192 | catch (Exception ex)
193 | {
194 | Debug.LogError($"[Cursor] Error getting workspace directory: {ex.Message}");
195 | return null;
196 | }
197 | }
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/Editor/ProcessRunner.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 35f2ac7e588f7f0488890c2eb8eb713b
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/ProjectGeneration.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 39f6c5735f4896746a1bcfa95716cdfb
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Editor/ProjectGeneration/AssemblyNameProvider.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Unity Technologies.
3 | * Copyright (c) Microsoft Corporation. All rights reserved.
4 | * Licensed under the MIT License. See License.txt in the project root for license information.
5 | *--------------------------------------------------------------------------------------------*/
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 | using UnityEditor;
10 | using UnityEditor.Compilation;
11 | using UnityEditor.PackageManager;
12 |
13 | namespace Microsoft.Unity.VisualStudio.Editor
14 | {
15 | public interface IAssemblyNameProvider
16 | {
17 | string[] ProjectSupportedExtensions { get; }
18 | string ProjectGenerationRootNamespace { get; }
19 | ProjectGenerationFlag ProjectGenerationFlag { get; }
20 |
21 | string GetAssemblyNameFromScriptPath(string path);
22 | string GetAssemblyName(string assemblyOutputPath, string assemblyName);
23 | bool IsInternalizedPackagePath(string path);
24 | IEnumerable GetAssemblies(Func shouldFileBePartOfSolution);
25 | IEnumerable GetAllAssetPaths();
26 | UnityEditor.PackageManager.PackageInfo FindForAssetPath(string assetPath);
27 | ResponseFileData ParseResponseFile(string responseFilePath, string projectDirectory, string[] systemReferenceDirectories);
28 | void ToggleProjectGeneration(ProjectGenerationFlag preference);
29 | }
30 |
31 | public class AssemblyNameProvider : IAssemblyNameProvider
32 | {
33 | private readonly Dictionary m_PackageInfoCache = new Dictionary();
34 |
35 | ProjectGenerationFlag m_ProjectGenerationFlag = (ProjectGenerationFlag)EditorPrefs.GetInt(
36 | "unity_project_generation_flag",
37 | (int)(ProjectGenerationFlag.Local | ProjectGenerationFlag.Embedded));
38 |
39 | public string[] ProjectSupportedExtensions => EditorSettings.projectGenerationUserExtensions;
40 |
41 | public string ProjectGenerationRootNamespace => EditorSettings.projectGenerationRootNamespace;
42 |
43 | public ProjectGenerationFlag ProjectGenerationFlag
44 | {
45 | get { return ProjectGenerationFlagImpl; }
46 | private set { ProjectGenerationFlagImpl = value;}
47 | }
48 |
49 | internal virtual ProjectGenerationFlag ProjectGenerationFlagImpl
50 | {
51 | get => m_ProjectGenerationFlag;
52 | private set
53 | {
54 | EditorPrefs.SetInt("unity_project_generation_flag", (int)value);
55 | m_ProjectGenerationFlag = value;
56 | }
57 | }
58 |
59 | public string GetAssemblyNameFromScriptPath(string path)
60 | {
61 | return CompilationPipeline.GetAssemblyNameFromScriptPath(path);
62 | }
63 |
64 | internal static readonly string AssemblyOutput = @"Temp\bin\Debug\".NormalizePathSeparators();
65 | internal static readonly string PlayerAssemblyOutput = @"Temp\bin\Debug\Player\".NormalizePathSeparators();
66 |
67 | public IEnumerable GetAssemblies(Func shouldFileBePartOfSolution)
68 | {
69 | IEnumerable assemblies = GetAssembliesByType(AssembliesType.Editor, shouldFileBePartOfSolution, AssemblyOutput);
70 |
71 | if (!ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.PlayerAssemblies))
72 | {
73 | return assemblies;
74 | }
75 | var playerAssemblies = GetAssembliesByType(AssembliesType.Player, shouldFileBePartOfSolution, PlayerAssemblyOutput);
76 | return assemblies.Concat(playerAssemblies);
77 | }
78 |
79 | private static IEnumerable GetAssembliesByType(AssembliesType type, Func shouldFileBePartOfSolution, string outputPath)
80 | {
81 | foreach (var assembly in CompilationPipeline.GetAssemblies(type))
82 | {
83 | if (assembly.sourceFiles.Any(shouldFileBePartOfSolution))
84 | {
85 | yield return new Assembly(
86 | assembly.name,
87 | outputPath,
88 | assembly.sourceFiles,
89 | assembly.defines,
90 | assembly.assemblyReferences,
91 | assembly.compiledAssemblyReferences,
92 | assembly.flags,
93 | assembly.compilerOptions
94 | #if UNITY_2020_2_OR_NEWER
95 | , assembly.rootNamespace
96 | #endif
97 | );
98 | }
99 | }
100 | }
101 |
102 | public string GetCompileOutputPath(string assemblyName)
103 | {
104 | // We need to keep this one for API surface check (AssemblyNameProvider is public), but not used anymore
105 | throw new NotImplementedException();
106 | }
107 |
108 | public IEnumerable GetAllAssetPaths()
109 | {
110 | return AssetDatabase.GetAllAssetPaths();
111 | }
112 |
113 | private static string ResolvePotentialParentPackageAssetPath(string assetPath)
114 | {
115 | const string packagesPrefix = "packages/";
116 | if (!assetPath.StartsWith(packagesPrefix, StringComparison.OrdinalIgnoreCase))
117 | {
118 | return null;
119 | }
120 |
121 | var followupSeparator = assetPath.IndexOf('/', packagesPrefix.Length);
122 | if (followupSeparator == -1)
123 | {
124 | return assetPath.ToLowerInvariant();
125 | }
126 |
127 | return assetPath.Substring(0, followupSeparator).ToLowerInvariant();
128 | }
129 |
130 | public UnityEditor.PackageManager.PackageInfo FindForAssetPath(string assetPath)
131 | {
132 | var parentPackageAssetPath = ResolvePotentialParentPackageAssetPath(assetPath);
133 | if (parentPackageAssetPath == null)
134 | {
135 | return null;
136 | }
137 |
138 | if (m_PackageInfoCache.TryGetValue(parentPackageAssetPath, out var cachedPackageInfo))
139 | {
140 | return cachedPackageInfo;
141 | }
142 |
143 | var result = UnityEditor.PackageManager.PackageInfo.FindForAssetPath(parentPackageAssetPath);
144 | m_PackageInfoCache[parentPackageAssetPath] = result;
145 | return result;
146 | }
147 |
148 | public bool IsInternalizedPackagePath(string path)
149 | {
150 | if (string.IsNullOrEmpty(path.Trim()))
151 | {
152 | return false;
153 | }
154 | var packageInfo = FindForAssetPath(path);
155 | if (packageInfo == null)
156 | {
157 | return false;
158 | }
159 | var packageSource = packageInfo.source;
160 | switch (packageSource)
161 | {
162 | case PackageSource.Embedded:
163 | return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.Embedded);
164 | case PackageSource.Registry:
165 | return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.Registry);
166 | case PackageSource.BuiltIn:
167 | return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.BuiltIn);
168 | case PackageSource.Unknown:
169 | return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.Unknown);
170 | case PackageSource.Local:
171 | return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.Local);
172 | case PackageSource.Git:
173 | return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.Git);
174 | case PackageSource.LocalTarball:
175 | return !ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.LocalTarBall);
176 | }
177 |
178 | return false;
179 | }
180 |
181 | public ResponseFileData ParseResponseFile(string responseFilePath, string projectDirectory, string[] systemReferenceDirectories)
182 | {
183 | return CompilationPipeline.ParseResponseFile(
184 | responseFilePath,
185 | projectDirectory,
186 | systemReferenceDirectories
187 | );
188 | }
189 |
190 | public void ToggleProjectGeneration(ProjectGenerationFlag preference)
191 | {
192 | if (ProjectGenerationFlag.HasFlag(preference))
193 | {
194 | ProjectGenerationFlag ^= preference;
195 | }
196 | else
197 | {
198 | ProjectGenerationFlag |= preference;
199 | }
200 | }
201 |
202 | internal void ResetPackageInfoCache()
203 | {
204 | m_PackageInfoCache.Clear();
205 | }
206 |
207 | public void ResetProjectGenerationFlag()
208 | {
209 | ProjectGenerationFlag = ProjectGenerationFlag.None;
210 | }
211 |
212 | public string GetAssemblyName(string assemblyOutputPath, string assemblyName)
213 | {
214 | if (assemblyOutputPath == PlayerAssemblyOutput)
215 | return assemblyName + ".Player";
216 |
217 | return assemblyName;
218 | }
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/Editor/ProjectGeneration/AssemblyNameProvider.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 7c09dc04e9f01884b939d6c1087f95ed
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/ProjectGeneration/FileIOProvider.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Unity Technologies.
3 | * Copyright (c) Microsoft Corporation. All rights reserved.
4 | * Licensed under the MIT License. See License.txt in the project root for license information.
5 | *--------------------------------------------------------------------------------------------*/
6 | using System.IO;
7 | using System.Text;
8 |
9 | namespace Microsoft.Unity.VisualStudio.Editor
10 | {
11 | public interface IFileIO
12 | {
13 | bool Exists(string fileName);
14 |
15 | string ReadAllText(string fileName);
16 | void WriteAllText(string fileName, string content);
17 | }
18 |
19 | class FileIOProvider : IFileIO
20 | {
21 | public bool Exists(string fileName)
22 | {
23 | return File.Exists(fileName);
24 | }
25 |
26 | public string ReadAllText(string fileName)
27 | {
28 | return File.ReadAllText(fileName);
29 | }
30 |
31 | public void WriteAllText(string fileName, string content)
32 | {
33 | File.WriteAllText(fileName, content, Encoding.UTF8);
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Editor/ProjectGeneration/FileIOProvider.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a78bf80420cfff243be0a089839e1214
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/ProjectGeneration/GUIDProvider.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Unity Technologies.
3 | * Copyright (c) Microsoft Corporation. All rights reserved.
4 | * Licensed under the MIT License. See License.txt in the project root for license information.
5 | *--------------------------------------------------------------------------------------------*/
6 | namespace Microsoft.Unity.VisualStudio.Editor
7 | {
8 | public interface IGUIDGenerator
9 | {
10 | string ProjectGuid(string projectName, string assemblyName);
11 | string SolutionGuid(string projectName, ScriptingLanguage scriptingLanguage);
12 | }
13 |
14 | class GUIDProvider : IGUIDGenerator
15 | {
16 | public string ProjectGuid(string projectName, string assemblyName)
17 | {
18 | return SolutionGuidGenerator.GuidForProject(projectName + assemblyName);
19 | }
20 |
21 | public string SolutionGuid(string projectName, ScriptingLanguage scriptingLanguage)
22 | {
23 | return SolutionGuidGenerator.GuidForSolution(projectName, scriptingLanguage);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Editor/ProjectGeneration/GUIDProvider.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 10c92ccf16d421947abfe28eb5247869
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/ProjectGeneration/LegacyStyleProjectGeneration.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Unity Technologies.
3 | * Copyright (c) Microsoft Corporation. All rights reserved.
4 | * Licensed under the MIT License. See License.txt in the project root for license information.
5 | *--------------------------------------------------------------------------------------------*/
6 |
7 | using System.Text;
8 | using UnityEditor.Compilation;
9 |
10 | namespace Microsoft.Unity.VisualStudio.Editor
11 | {
12 |
13 | internal class LegacyStyleProjectGeneration : ProjectGeneration
14 | {
15 | internal override string StyleName => "Legacy";
16 |
17 | public LegacyStyleProjectGeneration(string tempDirectory, IAssemblyNameProvider assemblyNameProvider, IFileIO fileIoProvider, IGUIDGenerator guidGenerator) : base(tempDirectory, assemblyNameProvider, fileIoProvider, guidGenerator)
18 | {
19 | }
20 |
21 | public LegacyStyleProjectGeneration(string tempDirectory) : base(tempDirectory)
22 | {
23 | }
24 |
25 | public LegacyStyleProjectGeneration()
26 | {
27 | }
28 |
29 | internal override void GetProjectHeader(ProjectProperties properties, out StringBuilder headerBuilder)
30 | {
31 | headerBuilder = new StringBuilder();
32 |
33 | //Header
34 | headerBuilder.Append(@"").Append(k_WindowsNewline);
35 | headerBuilder.Append($@"").Append(k_WindowsNewline);
36 | headerBuilder.Append(@" ").Append(k_WindowsNewline);
37 | headerBuilder.Append(@" ").Append(k_WindowsNewline);
38 | headerBuilder.Append(@" ").Append(properties.LangVersion).Append(@"").Append(k_WindowsNewline);
39 | headerBuilder.Append(@" ").Append(k_WindowsNewline);
40 | headerBuilder.Append(@" ").Append(k_WindowsNewline);
41 | headerBuilder.Append(@" Debug").Append(k_WindowsNewline);
42 | headerBuilder.Append(@" AnyCPU").Append(k_WindowsNewline);
43 | headerBuilder.Append(@" 10.0.20506").Append(k_WindowsNewline);
44 | headerBuilder.Append(@" 2.0").Append(k_WindowsNewline);
45 | headerBuilder.Append(@" ").Append(properties.RootNamespace).Append(@"").Append(k_WindowsNewline);
46 | headerBuilder.Append(@" {").Append(properties.ProjectGuid).Append(@"}").Append(k_WindowsNewline);
47 | headerBuilder.Append(@" Library").Append(k_WindowsNewline);
48 | headerBuilder.Append(@" Properties").Append(k_WindowsNewline);
49 | headerBuilder.Append(@" ").Append(properties.AssemblyName).Append(@"").Append(k_WindowsNewline);
50 | // In the end, given we use NoConfig/NoStdLib (see below), hardcoding the target framework version with the legacy format will have no impact, even when targeting netstandard/net48 from Unity.
51 | // And VSTU/Unity Game workload has a dependency towards net471 reference assemblies, so IDE will not complain that this specific SDK is not available.
52 | // Unity already selected proper API surface through referenced DLLs for us.
53 | headerBuilder.Append(@" v4.7.1").Append(k_WindowsNewline);
54 | headerBuilder.Append(@" 512").Append(k_WindowsNewline);
55 | headerBuilder.Append(@" .").Append(k_WindowsNewline);
56 | headerBuilder.Append(@" ").Append(k_WindowsNewline);
57 |
58 | GetProjectHeaderConfigurations(properties, headerBuilder);
59 |
60 | // Explicit references
61 | headerBuilder.Append(@" ").Append(k_WindowsNewline);
62 | headerBuilder.Append(@" true").Append(k_WindowsNewline);
63 | headerBuilder.Append(@" true").Append(k_WindowsNewline);
64 | headerBuilder.Append(@" false").Append(k_WindowsNewline);
65 | headerBuilder.Append(@" false").Append(k_WindowsNewline);
66 | headerBuilder.Append(@" false").Append(k_WindowsNewline);
67 | headerBuilder.Append(@" ").Append(k_WindowsNewline);
68 |
69 | GetProjectHeaderVstuFlavoring(properties, headerBuilder);
70 | GetProjectHeaderAnalyzers(properties, headerBuilder);
71 | }
72 |
73 | internal override void AppendProjectReference(Assembly assembly, Assembly reference, StringBuilder projectBuilder)
74 | {
75 | // If the current assembly is a Player project, we want to project-reference the corresponding Player project
76 | var referenceName = m_AssemblyNameProvider.GetAssemblyName(assembly.outputPath, reference.name);
77 |
78 | projectBuilder.Append(@" ").Append(k_WindowsNewline);
79 | projectBuilder.Append(" {").Append(ProjectGuid(referenceName)).Append("}").Append(k_WindowsNewline);
80 | projectBuilder.Append(" ").Append(referenceName).Append("").Append(k_WindowsNewline);
81 | projectBuilder.Append(" ").Append(k_WindowsNewline);
82 | }
83 |
84 | internal override void GetProjectFooter(StringBuilder footerBuilder)
85 | {
86 | footerBuilder.Append(string.Join(k_WindowsNewline,
87 | $" ",
88 | @" ",
89 | @" ",
96 | @"",
97 | @""));
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/Editor/ProjectGeneration/LegacyStyleProjectGeneration.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d5201e27b238df341abb6ede328238d8
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/ProjectGeneration/ProjectGeneration.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e43e4f00a4b35e741b8d658b792c38e7
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/ProjectGeneration/ProjectGenerationFlag.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Unity Technologies.
3 | * Copyright (c) Microsoft Corporation. All rights reserved.
4 | * Licensed under the MIT License. See License.txt in the project root for license information.
5 | *--------------------------------------------------------------------------------------------*/
6 | using System;
7 |
8 | namespace Microsoft.Unity.VisualStudio.Editor
9 | {
10 | [Flags]
11 | public enum ProjectGenerationFlag
12 | {
13 | None = 0,
14 | Embedded = 1,
15 | Local = 2,
16 | Registry = 4,
17 | Git = 8,
18 | BuiltIn = 16,
19 | Unknown = 32,
20 | PlayerAssemblies = 64,
21 | LocalTarBall = 128,
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Editor/ProjectGeneration/ProjectGenerationFlag.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 56544889635e6154d8f3b295bdfeca38
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/ProjectGeneration/ProjectProperties.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Microsoft.Unity.VisualStudio.Editor
4 | {
5 | internal class ProjectProperties
6 | {
7 | public string ProjectGuid { get; set; } = string.Empty;
8 | public string LangVersion { get; set; } = "latest";
9 | public string AssemblyName { get; set; } = string.Empty;
10 | public string RootNamespace { get; set; } = string.Empty;
11 | public string OutputPath { get; set; } = string.Empty;
12 |
13 | // Analyzers
14 | public string[] Analyzers { get; set; } = Array.Empty();
15 | public string RulesetPath { get; set; } = string.Empty;
16 | public string AnalyzerConfigPath { get; set; } = string.Empty;
17 | // Source generators
18 | public string[] AdditionalFilePaths { get; set; } = Array.Empty();
19 |
20 | // RSP alterable
21 | public string[] Defines { get; set; } = Array.Empty();
22 | public bool Unsafe { get; set; } = false;
23 |
24 | // VSTU Flavouring
25 | public string FlavoringProjectType { get; set; } = string.Empty;
26 | public string FlavoringBuildTarget { get; set; } = string.Empty;
27 | public string FlavoringUnityVersion { get; set; } = string.Empty;
28 | public string FlavoringPackageVersion { get; set; } = string.Empty;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Editor/ProjectGeneration/ProjectProperties.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 124f613b45b45af49bc986c5cf48f93e
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/ProjectGeneration/SdkStyleProjectGeneration.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Unity Technologies.
3 | * Copyright (c) Microsoft Corporation. All rights reserved.
4 | * Licensed under the MIT License. See License.txt in the project root for license information.
5 | *--------------------------------------------------------------------------------------------*/
6 |
7 | using System;
8 | using System.IO;
9 | using System.Text;
10 | using UnityEditor.Compilation;
11 | using UnityEngine;
12 |
13 | namespace Microsoft.Unity.VisualStudio.Editor
14 | {
15 | internal class SdkStyleProjectGeneration : ProjectGeneration
16 | {
17 | internal override string StyleName => "SDK";
18 |
19 | internal class SdkStyleAssemblyNameProvider : AssemblyNameProvider
20 | {
21 | // disable PlayerGeneration with SdkStyle projects
22 | internal override ProjectGenerationFlag ProjectGenerationFlagImpl => base.ProjectGenerationFlagImpl & ~ProjectGenerationFlag.PlayerAssemblies;
23 | }
24 |
25 | public SdkStyleProjectGeneration() : base(
26 | Directory.GetParent(Application.dataPath)?.FullName,
27 | new SdkStyleAssemblyNameProvider(),
28 | new FileIOProvider(),
29 | new GUIDProvider())
30 | {
31 | }
32 |
33 | internal static readonly string[] SupportedCapabilities = new string[]
34 | {
35 | "Unity",
36 | };
37 |
38 | internal static readonly string[] UnsupportedCapabilities = new string[]
39 | {
40 | "LaunchProfiles",
41 | "SharedProjectReferences",
42 | "ReferenceManagerSharedProjects",
43 | "ProjectReferences",
44 | "ReferenceManagerProjects",
45 | "COMReferences",
46 | "ReferenceManagerCOM",
47 | "AssemblyReferences",
48 | "ReferenceManagerAssemblies",
49 | };
50 |
51 | internal override void GetProjectHeader(ProjectProperties properties, out StringBuilder headerBuilder)
52 | {
53 | headerBuilder = new StringBuilder();
54 |
55 | headerBuilder.Append(@"").Append(k_WindowsNewline);
56 | headerBuilder.Append(@" ").Append(k_WindowsNewline);
57 |
58 | // Prevent circular dependency issues see https://github.com/microsoft/vscode-dotnettools/issues/401
59 | // We need a dedicated subfolder for each project in obj, else depending on the build order, nuget cache files could be overwritten
60 | // We need to do this before common.props, else we'll have a MSB3539 The value of the property "BaseIntermediateOutputPath" was modified after it was used by MSBuild
61 | headerBuilder.Append(@" ").Append(k_WindowsNewline);
62 | headerBuilder.Append($" {@"Temp\obj\$(Configuration)\$(MSBuildProjectName)".NormalizePathSeparators()}").Append(k_WindowsNewline);
63 | headerBuilder.Append(@" $(BaseIntermediateOutputPath)").Append(k_WindowsNewline);
64 | headerBuilder.Append(@" ").Append(k_WindowsNewline);
65 |
66 | // Supported capabilities
67 | GetCapabilityBlock(headerBuilder, "Sdk.props", "Include", SupportedCapabilities);
68 |
69 | headerBuilder.Append(@" ").Append(k_WindowsNewline);
70 | headerBuilder.Append(@" false").Append(k_WindowsNewline);
71 | headerBuilder.Append(@" false").Append(k_WindowsNewline);
72 | headerBuilder.Append(@" false").Append(k_WindowsNewline);
73 | headerBuilder.Append(@" ").Append(properties.LangVersion).Append(@"").Append(k_WindowsNewline);
74 | headerBuilder.Append(@" Debug;Release").Append(k_WindowsNewline);
75 | headerBuilder.Append(@" Debug").Append(k_WindowsNewline);
76 | headerBuilder.Append(@" AnyCPU").Append(k_WindowsNewline);
77 | headerBuilder.Append(@" ").Append(properties.RootNamespace).Append(@"").Append(k_WindowsNewline);
78 | headerBuilder.Append(@" Library").Append(k_WindowsNewline);
79 | headerBuilder.Append(@" Properties").Append(k_WindowsNewline);
80 | headerBuilder.Append(@" ").Append(properties.AssemblyName).Append(@"").Append(k_WindowsNewline);
81 | // In the end, given we use NoConfig/NoStdLib (see below), hardcoding the target framework version will have no impact, even when targeting netstandard/net48 from Unity.
82 | // But with SDK style we use netstandard2.1 (net471 for legacy), so 3rd party tools will not fail to work when .NETFW reference assemblies are not installed.
83 | // Unity already selected proper API surface through referenced DLLs for us.
84 | headerBuilder.Append(@" netstandard2.1").Append(k_WindowsNewline);
85 | headerBuilder.Append(@" .").Append(k_WindowsNewline);
86 | headerBuilder.Append(@" ").Append(k_WindowsNewline);
87 |
88 | GetProjectHeaderConfigurations(properties, headerBuilder);
89 |
90 | // Explicit references
91 | headerBuilder.Append(@" ").Append(k_WindowsNewline);
92 | headerBuilder.Append(@" true").Append(k_WindowsNewline);
93 | headerBuilder.Append(@" true").Append(k_WindowsNewline);
94 | headerBuilder.Append(@" true").Append(k_WindowsNewline);
95 | headerBuilder.Append(@" true").Append(k_WindowsNewline);
96 | headerBuilder.Append(@" MSB3277").Append(k_WindowsNewline);
97 | headerBuilder.Append(@" ").Append(k_WindowsNewline);
98 |
99 | GetProjectHeaderVstuFlavoring(properties, headerBuilder, false);
100 | GetProjectHeaderAnalyzers(properties, headerBuilder);
101 | }
102 |
103 | internal override void AppendProjectReference(Assembly assembly, Assembly reference, StringBuilder projectBuilder)
104 | {
105 | // If the current assembly is a Player project, we want to project-reference the corresponding Player project
106 | var referenceName = m_AssemblyNameProvider.GetAssemblyName(assembly.outputPath, reference.name);
107 | projectBuilder.Append(@" ").Append(k_WindowsNewline);
108 | }
109 |
110 | internal override void GetProjectFooter(StringBuilder footerBuilder)
111 | {
112 | // Unsupported capabilities
113 | GetCapabilityBlock(footerBuilder, "Sdk.targets", "Remove", UnsupportedCapabilities);
114 |
115 | footerBuilder.Append("").Append(k_WindowsNewline);
116 | }
117 |
118 | internal static void GetCapabilityBlock(StringBuilder footerBuilder, string import, string attribute, string[] capabilities)
119 | {
120 | footerBuilder.Append($@" ").Append(k_WindowsNewline);
121 | footerBuilder.Append(@" ").Append(k_WindowsNewline);
122 | foreach (var capability in capabilities)
123 | {
124 | footerBuilder.Append($@" ").Append(k_WindowsNewline);
125 | }
126 | footerBuilder.Append(@" ").Append(k_WindowsNewline);
127 | }
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/Editor/ProjectGeneration/SdkStyleProjectGeneration.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c449403aa776a1d4daca10ff808d30de
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/SimpleJSON.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f14a73997ecfc3a49a8b5e46a49c4f4f
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Solution.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 | namespace Microsoft.Unity.VisualStudio.Editor
6 | {
7 | internal class Solution
8 | {
9 | public SolutionProjectEntry[] Projects { get; set; }
10 | public SolutionProperties[] Properties { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Editor/Solution.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 27bc8a57b00ccec44945a7db9acf9b3b
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/SolutionParser.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 | using System.Collections.Generic;
6 | using System.Text.RegularExpressions;
7 |
8 | namespace Microsoft.Unity.VisualStudio.Editor
9 | {
10 | internal static class SolutionParser
11 | {
12 | // Compared to the bridge implementation, we are not returning "{" "}" from Guids
13 | private static readonly Regex ProjectDeclaration = new Regex(@"Project\(\""{(?.*?)}\""\)\s+=\s+\""(?.*?)\"",\s+\""(?.*?)\"",\s+\""{(?.*?)}\""(?.*?)\bEndProject\b", RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.Compiled);
14 | private static readonly Regex PropertiesDeclaration = new Regex(@"GlobalSection\((?([\w]+Properties|NestedProjects))\)\s+=\s+(?(?:post|pre)Solution)(?.*?)EndGlobalSection", RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.Compiled);
15 | private static readonly Regex PropertiesEntryDeclaration = new Regex(@"^\s*(?.*?)=(?.*?)$", RegexOptions.Multiline | RegexOptions.ExplicitCapture | RegexOptions.Compiled);
16 |
17 | public static Solution ParseSolutionFile(string filename, IFileIO fileIO)
18 | {
19 | return ParseSolutionContent(fileIO.ReadAllText(filename));
20 | }
21 |
22 | public static Solution ParseSolutionContent(string content)
23 | {
24 | return new Solution
25 | {
26 | Projects = ParseSolutionProjects(content),
27 | Properties = ParseSolutionProperties(content)
28 | };
29 | }
30 |
31 | private static SolutionProjectEntry[] ParseSolutionProjects(string content)
32 | {
33 | var projects = new List();
34 | var mc = ProjectDeclaration.Matches(content);
35 |
36 | foreach (Match match in mc)
37 | {
38 | projects.Add(new SolutionProjectEntry
39 | {
40 | ProjectFactoryGuid = match.Groups["projectFactoryGuid"].Value,
41 | Name = match.Groups["name"].Value,
42 | FileName = match.Groups["fileName"].Value,
43 | ProjectGuid = match.Groups["projectGuid"].Value,
44 | Metadata = match.Groups["metadata"].Value
45 | });
46 | }
47 |
48 | return projects.ToArray();
49 | }
50 |
51 | private static SolutionProperties[] ParseSolutionProperties(string content)
52 | {
53 | var properties = new List();
54 | var mc = PropertiesDeclaration.Matches(content);
55 |
56 | foreach (Match match in mc)
57 | {
58 | var sp = new SolutionProperties
59 | {
60 | Entries = new List>(),
61 | Name = match.Groups["name"].Value,
62 | Type = match.Groups["type"].Value
63 | };
64 |
65 | var entries = match.Groups["entries"].Value;
66 | var mec = PropertiesEntryDeclaration.Matches(entries);
67 | foreach (Match entry in mec)
68 | {
69 | var key = entry.Groups["key"].Value.Trim();
70 | var value = entry.Groups["value"].Value.Trim();
71 | sp.Entries.Add(new KeyValuePair(key, value));
72 | }
73 |
74 | properties.Add(sp);
75 | }
76 |
77 | return properties.ToArray();
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Editor/SolutionParser.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2f5aa66aee329a04d9fc7805f908ae6a
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/SolutionProjectEntry.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 | using System;
6 |
7 | namespace Microsoft.Unity.VisualStudio.Editor
8 | {
9 | internal class SolutionProjectEntry
10 | {
11 | public string ProjectFactoryGuid { get; set; }
12 | public string Name { get; set; }
13 | public string FileName { get; set; }
14 | public string ProjectGuid { get; set; }
15 | public string Metadata { get; set; }
16 |
17 | public bool IsSolutionFolderProjectFactory()
18 | {
19 | return ProjectFactoryGuid != null && ProjectFactoryGuid.Equals("2150E333-8FDC-42A3-9474-1A3956D46DE8", StringComparison.OrdinalIgnoreCase);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Editor/SolutionProjectEntry.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 34cd2d7b80a45a344b72e4c60d1c22ba
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/SolutionProperties.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 | using System.Collections.Generic;
6 |
7 | namespace Microsoft.Unity.VisualStudio.Editor
8 | {
9 | internal class SolutionProperties
10 | {
11 | public string Name { get; set; }
12 | public IList> Entries { get; set; }
13 | public string Type { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Editor/SolutionProperties.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 53157d8310cfca940bb22bf101b2c275
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Symbols.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 | using System;
6 | using System.IO;
7 |
8 | namespace Microsoft.Unity.VisualStudio.Editor
9 | {
10 | internal static class Symbols
11 | {
12 | public static bool IsPortableSymbolFile(string pdbFile)
13 | {
14 | try
15 | {
16 | using (var stream = File.OpenRead(pdbFile))
17 | {
18 | return stream.ReadByte() == 'B'
19 | && stream.ReadByte() == 'S'
20 | && stream.ReadByte() == 'J'
21 | && stream.ReadByte() == 'B';
22 | }
23 | }
24 | catch (Exception)
25 | {
26 | return false;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Editor/Symbols.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 4f11f75003f55a8408825edd475b9204
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Testing.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d83b86f0bc6c70b4ba02a9c9f24c865e
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Editor/Testing/TestAdaptor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using UnityEditor.TestTools.TestRunner.Api;
4 |
5 | namespace Microsoft.Unity.VisualStudio.Editor.Testing
6 | {
7 | [Serializable]
8 | internal class TestAdaptorContainer
9 | {
10 | public TestAdaptor[] TestAdaptors;
11 | }
12 |
13 | [Serializable]
14 | internal class TestAdaptor
15 | {
16 | public string Id;
17 | public string Name;
18 | public string FullName;
19 |
20 | public string Type;
21 | public string Method;
22 | public string Assembly;
23 |
24 | public int Parent;
25 |
26 | public TestAdaptor(ITestAdaptor testAdaptor, int parent)
27 | {
28 | Id = testAdaptor.Id;
29 | Name = testAdaptor.Name;
30 | FullName = testAdaptor.FullName;
31 |
32 | Type = testAdaptor.TypeInfo?.FullName;
33 | Method = testAdaptor.Method?.Name;
34 | Assembly = testAdaptor.TypeInfo?.Assembly?.Location;
35 |
36 | Parent = parent;
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Editor/Testing/TestAdaptor.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 606e2ff16916e884c904be431b4311f3
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Testing/TestResultAdaptor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using UnityEditor.TestTools.TestRunner.Api;
4 |
5 | namespace Microsoft.Unity.VisualStudio.Editor.Testing
6 | {
7 | [Serializable]
8 | internal class TestResultAdaptorContainer
9 | {
10 | public TestResultAdaptor[] TestResultAdaptors;
11 | }
12 |
13 | [Serializable]
14 | internal class TestResultAdaptor
15 | {
16 | public string Name;
17 | public string FullName;
18 |
19 | public int PassCount;
20 | public int FailCount;
21 | public int InconclusiveCount;
22 | public int SkipCount;
23 |
24 | public string ResultState;
25 | public string StackTrace;
26 |
27 | public TestStatusAdaptor TestStatus;
28 |
29 | public int Parent;
30 |
31 | public TestResultAdaptor(ITestResultAdaptor testResultAdaptor, int parent)
32 | {
33 | Name = testResultAdaptor.Name;
34 | FullName = testResultAdaptor.FullName;
35 |
36 | PassCount = testResultAdaptor.PassCount;
37 | FailCount = testResultAdaptor.FailCount;
38 | InconclusiveCount = testResultAdaptor.InconclusiveCount;
39 | SkipCount = testResultAdaptor.SkipCount;
40 |
41 | switch (testResultAdaptor.TestStatus)
42 | {
43 | case UnityEditor.TestTools.TestRunner.Api.TestStatus.Passed:
44 | TestStatus = TestStatusAdaptor.Passed;
45 | break;
46 | case UnityEditor.TestTools.TestRunner.Api.TestStatus.Skipped:
47 | TestStatus = TestStatusAdaptor.Skipped;
48 | break;
49 | case UnityEditor.TestTools.TestRunner.Api.TestStatus.Inconclusive:
50 | TestStatus = TestStatusAdaptor.Inconclusive;
51 | break;
52 | case UnityEditor.TestTools.TestRunner.Api.TestStatus.Failed:
53 | TestStatus = TestStatusAdaptor.Failed;
54 | break;
55 | }
56 |
57 | Parent = parent;
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Editor/Testing/TestResultAdaptor.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: cd85aa8281e57264ebb6fc4ec8abae25
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Testing/TestRunnerApiListener.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEditor;
3 | using UnityEditor.TestTools.TestRunner.Api;
4 | using UnityEngine;
5 |
6 | namespace Microsoft.Unity.VisualStudio.Editor.Testing
7 | {
8 | [InitializeOnLoad]
9 | internal class TestRunnerApiListener
10 | {
11 | private static readonly TestRunnerApi _testRunnerApi;
12 | private static readonly TestRunnerCallbacks _testRunnerCallbacks;
13 |
14 | static TestRunnerApiListener()
15 | {
16 | if (!VisualStudioEditor.IsEnabled)
17 | return;
18 |
19 | _testRunnerApi = ScriptableObject.CreateInstance();
20 | _testRunnerCallbacks = new TestRunnerCallbacks();
21 |
22 | _testRunnerApi.RegisterCallbacks(_testRunnerCallbacks);
23 | }
24 |
25 | public static void RetrieveTestList(string mode)
26 | {
27 | RetrieveTestList((TestMode) Enum.Parse(typeof(TestMode), mode));
28 | }
29 |
30 | private static void RetrieveTestList(TestMode mode)
31 | {
32 | _testRunnerApi?.RetrieveTestList(mode, ta => _testRunnerCallbacks.TestListRetrieved(mode, ta));
33 | }
34 |
35 | public static void ExecuteTests(string command)
36 | {
37 | // ExecuteTests format:
38 | // TestMode:FullName
39 |
40 | var index = command.IndexOf(':');
41 | if (index < 0)
42 | return;
43 |
44 | var testMode = (TestMode)Enum.Parse(typeof(TestMode), command.Substring(0, index));
45 | var filter = command.Substring(index + 1);
46 |
47 | ExecuteTests(new Filter { testMode = testMode, testNames = new [] { filter } });
48 | }
49 |
50 | private static void ExecuteTests(Filter filter)
51 | {
52 | _testRunnerApi?.Execute(new ExecutionSettings(filter));
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Editor/Testing/TestRunnerApiListener.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a5f5f778f126f0542a5a9c3eb6ce9be4
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Testing/TestRunnerCallbacks.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using UnityEditor.TestTools.TestRunner.Api;
4 | using UnityEngine;
5 |
6 | namespace Microsoft.Unity.VisualStudio.Editor.Testing
7 | {
8 | internal class TestRunnerCallbacks : ICallbacks
9 | {
10 | private string Serialize(
11 | TSource source,
12 | Func createAdaptor,
13 | Func> children,
14 | Func container)
15 | {
16 | var adaptors = new List();
17 |
18 | void AddAdaptor(TSource item, int parentIndex)
19 | {
20 | var index = adaptors.Count;
21 | adaptors.Add(createAdaptor(item, parentIndex));
22 | foreach (var child in children(item))
23 | AddAdaptor(child, index);
24 | }
25 |
26 | AddAdaptor(source, -1);
27 |
28 | return JsonUtility.ToJson(container(adaptors.ToArray()));
29 | }
30 |
31 | private string Serialize(ITestAdaptor testAdaptor)
32 | {
33 | return Serialize(
34 | testAdaptor,
35 | (a, parentIndex) => new TestAdaptor(a, parentIndex),
36 | (a) => a.Children,
37 | (r) => new TestAdaptorContainer { TestAdaptors = r });
38 | }
39 |
40 | private string Serialize(ITestResultAdaptor testResultAdaptor)
41 | {
42 | return Serialize(
43 | testResultAdaptor,
44 | (a, parentIndex) => new TestResultAdaptor(a, parentIndex),
45 | (a) => a.Children,
46 | (r) => new TestResultAdaptorContainer { TestResultAdaptors = r });
47 | }
48 |
49 | public void RunFinished(ITestResultAdaptor testResultAdaptor)
50 | {
51 | VisualStudioIntegration.BroadcastMessage(Messaging.MessageType.RunFinished, Serialize(testResultAdaptor));
52 | }
53 |
54 | public void RunStarted(ITestAdaptor testAdaptor)
55 | {
56 | VisualStudioIntegration.BroadcastMessage(Messaging.MessageType.RunStarted, Serialize(testAdaptor));
57 | }
58 |
59 | public void TestFinished(ITestResultAdaptor testResultAdaptor)
60 | {
61 | VisualStudioIntegration.BroadcastMessage(Messaging.MessageType.TestFinished, Serialize(testResultAdaptor));
62 | }
63 |
64 | public void TestStarted(ITestAdaptor testAdaptor)
65 | {
66 | VisualStudioIntegration.BroadcastMessage(Messaging.MessageType.TestStarted, Serialize(testAdaptor));
67 | }
68 |
69 | private static string TestModeName(TestMode testMode)
70 | {
71 | switch (testMode)
72 | {
73 | case TestMode.EditMode: return "EditMode";
74 | case TestMode.PlayMode: return "PlayMode";
75 | }
76 |
77 | throw new ArgumentOutOfRangeException();
78 | }
79 |
80 |
81 | internal void TestListRetrieved(TestMode testMode, ITestAdaptor testAdaptor)
82 | {
83 | // TestListRetrieved format:
84 | // TestMode:Json
85 |
86 | var value = TestModeName(testMode) + ":" + Serialize(testAdaptor);
87 | VisualStudioIntegration.BroadcastMessage(Messaging.MessageType.TestListRetrieved, value);
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/Editor/Testing/TestRunnerCallbacks.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a2d33327b38d0dd45a27a726a560ab0f
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/Testing/TestStatusAdaptor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Microsoft.Unity.VisualStudio.Editor.Testing
4 | {
5 | [Serializable]
6 | internal enum TestStatusAdaptor
7 | {
8 | Passed,
9 | Skipped,
10 | Inconclusive,
11 | Failed,
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Editor/Testing/TestStatusAdaptor.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 14de30b850ab54040b195d012eef71fc
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/UnityInstallation.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 |
6 | using System;
7 | using UnityEditor;
8 | using UnityEditor.Compilation;
9 |
10 | namespace Microsoft.Unity.VisualStudio.Editor
11 | {
12 | internal static class UnityInstallation
13 | {
14 | public static bool IsMainUnityEditorProcess
15 | {
16 | get
17 | {
18 | #if UNITY_2020_2_OR_NEWER
19 | if (UnityEditor.AssetDatabase.IsAssetImportWorkerProcess())
20 | return false;
21 | #elif UNITY_2019_3_OR_NEWER
22 | if (UnityEditor.Experimental.AssetDatabaseExperimental.IsAssetImportWorkerProcess())
23 | return false;
24 | #endif
25 |
26 | #if UNITY_2021_1_OR_NEWER
27 | if (UnityEditor.MPE.ProcessService.level == UnityEditor.MPE.ProcessLevel.Secondary)
28 | return false;
29 | #elif UNITY_2020_2_OR_NEWER
30 | if (UnityEditor.MPE.ProcessService.level == UnityEditor.MPE.ProcessLevel.Slave)
31 | return false;
32 | #elif UNITY_2020_1_OR_NEWER
33 | if (global::Unity.MPE.ProcessService.level == global::Unity.MPE.ProcessLevel.UMP_SLAVE)
34 | return false;
35 | #endif
36 |
37 | return true;
38 | }
39 | }
40 |
41 | private static readonly Lazy _lazyIsInSafeMode = new Lazy(() =>
42 | {
43 | // internal static extern bool isInSafeMode { get {} }
44 | var ieu = typeof(EditorUtility);
45 | var pinfo = ieu.GetProperty("isInSafeMode", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
46 | if (pinfo == null)
47 | return false;
48 |
49 | return Convert.ToBoolean(pinfo.GetValue(null));
50 | });
51 | public static bool IsInSafeMode => _lazyIsInSafeMode.Value;
52 | public static Version LatestLanguageVersionSupported(Assembly assembly)
53 | {
54 | #if UNITY_2020_2_OR_NEWER
55 | if (assembly?.compilerOptions != null && Version.TryParse(assembly.compilerOptions.LanguageVersion, out var result))
56 | return result;
57 |
58 | // if parsing fails, we know at least we have support for 8.0
59 | return new Version(8, 0);
60 | #else
61 | return new Version(7, 3);
62 | #endif
63 | }
64 |
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Editor/UnityInstallation.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 795c5c262a77f1849970ef8abb2aea55
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/UsageUtility.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 | using System;
6 | using System.IO;
7 | using System.Linq;
8 |
9 | using UnityEngine;
10 | using UnityEditor;
11 | using UnityEditor.SceneManagement;
12 | using UnityEngine.SceneManagement;
13 |
14 | namespace Microsoft.Unity.VisualStudio.Editor
15 | {
16 | [Serializable]
17 | internal class FileUsage
18 | {
19 | public string Path;
20 | public string[] GameObjectPath;
21 | }
22 |
23 | internal static class UsageUtility
24 | {
25 | internal static void ShowUsage(string json)
26 | {
27 | try
28 | {
29 | var usage = JsonUtility.FromJson(json);
30 | ShowUsage(usage.Path, usage.GameObjectPath);
31 | }
32 | catch (Exception)
33 | {
34 | // ignore malformed request
35 | }
36 | }
37 |
38 | internal static void ShowUsage(string path, string[] gameObjectPath)
39 | {
40 | path = FileUtility.MakeRelativeToProjectPath(path);
41 | if (path == null)
42 | return;
43 |
44 | path = FileUtility.NormalizeWindowsToUnix(path);
45 | var extension = Path.GetExtension(path).ToLower();
46 |
47 | EditorUtility.FocusProjectWindow();
48 |
49 | switch (extension)
50 | {
51 | case ".unity":
52 | ShowSceneUsage(path, gameObjectPath);
53 | break;
54 | default:
55 | var asset = AssetDatabase.LoadMainAssetAtPath(path);
56 | Selection.activeObject = asset;
57 | EditorGUIUtility.PingObject(asset);
58 | break;
59 | }
60 | }
61 |
62 | private static void ShowSceneUsage(string scenePath, string[] gameObjectPath)
63 | {
64 | var scene = SceneManager.GetSceneByPath(scenePath.Replace(Path.DirectorySeparatorChar, '/'));
65 | if (!scene.isLoaded)
66 | {
67 | var result = EditorUtility.DisplayDialogComplex("Show Usage",
68 | $"Do you want to open \"{Path.GetFileName(scenePath)}\"?",
69 | "Open Scene",
70 | "Cancel",
71 | "Open Scene (additive)");
72 |
73 | switch (result)
74 | {
75 | case 0:
76 | EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
77 | scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single);
78 | break;
79 | case 1:
80 | return;
81 | case 2:
82 | scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Additive);
83 | break;
84 | }
85 | }
86 |
87 | ShowSceneUsage(scene, gameObjectPath);
88 | }
89 |
90 | private static void ShowSceneUsage(Scene scene, string[] gameObjectPath)
91 | {
92 | if (gameObjectPath == null || gameObjectPath.Length == 0)
93 | return;
94 |
95 | var go = scene.GetRootGameObjects().FirstOrDefault(g => g.name == gameObjectPath[0]);
96 | if (go == null)
97 | return;
98 |
99 | for (var ni = 1; ni < gameObjectPath.Length; ni++)
100 | {
101 | var transform = go.transform;
102 | for (var i = 0; i < transform.childCount; i++)
103 | {
104 | var child = transform.GetChild(i);
105 | var childgo = child.gameObject;
106 | if (childgo.name == gameObjectPath[ni])
107 | {
108 | go = childgo;
109 | break;
110 | }
111 | }
112 | }
113 |
114 | Selection.activeObject = go;
115 | EditorGUIUtility.PingObject(go);
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/Editor/UsageUtility.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2b920fd559dbf4a4ba9d960411e3414e
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/VSWhere.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ab378124d52ce0f498e46271d915f588
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Editor/VSWhere/vswhere.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liyingsong99/com.unity.ide.cursor-et/2ff42a4c32bdbe24a12d24762bf43da7cdccc76a/Editor/VSWhere/vswhere.exe
--------------------------------------------------------------------------------
/Editor/VSWhere/vswhere.exe.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 0cc03a84149fbf24987d62822ee55a04
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Editor/VersionPair.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Microsoft.Unity.VisualStudio.Editor
4 | {
5 | internal struct VersionPair
6 | {
7 | public Version IdeVersion;
8 | public Version LanguageVersion;
9 |
10 | public VersionPair(int idemajor, int ideminor, int languageMajor, int languageMinor)
11 | {
12 | IdeVersion = new Version(idemajor, ideminor);
13 | LanguageVersion = new Version(languageMajor, languageMinor);
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Editor/VersionPair.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 75ebc31926260bd4ab99ffc2dc72b327
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/VisualStudioCodiumInstallation.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 67fb09553bf91c34cb2e7b383a9907ba
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/VisualStudioCursorInstallation.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d83ff2bd8039e2f41a5147034ed5d28e
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/VisualStudioEditor.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Unity Technologies.
3 | * Copyright (c) Microsoft Corporation. All rights reserved.
4 | * Licensed under the MIT License. See License.txt in the project root for license information.
5 | *--------------------------------------------------------------------------------------------*/
6 | using System;
7 | using System.Collections.Generic;
8 | using System.IO;
9 | using System.Linq;
10 | using System.Runtime.CompilerServices;
11 | using UnityEditor;
12 | using UnityEngine;
13 | using Unity.CodeEditor;
14 |
15 | [assembly: InternalsVisibleTo("Unity.VisualStudio.EditorTests")]
16 | [assembly: InternalsVisibleTo("Unity.VisualStudio.Standalone.EditorTests")]
17 | [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
18 |
19 | namespace Microsoft.Unity.VisualStudio.Editor
20 | {
21 | [InitializeOnLoad]
22 | public class VisualStudioEditor : IExternalCodeEditor
23 | {
24 | CodeEditor.Installation[] IExternalCodeEditor.Installations => _discoverInstallations
25 | .Result
26 | .Values
27 | .Select(v => v.ToCodeEditorInstallation())
28 | .ToArray();
29 |
30 | private static readonly AsyncOperation> _discoverInstallations;
31 |
32 | static VisualStudioEditor()
33 | {
34 | if (!UnityInstallation.IsMainUnityEditorProcess)
35 | return;
36 |
37 | Discovery.Initialize();
38 | CodeEditor.Register(new VisualStudioEditor());
39 |
40 | _discoverInstallations = AsyncOperation>.Run(DiscoverInstallations);
41 | }
42 |
43 | #if UNITY_2019_4_OR_NEWER && !UNITY_2020
44 | [InitializeOnLoadMethod]
45 | static void LegacyVisualStudioCodePackageDisabler()
46 | {
47 | // disable legacy Visual Studio Code packages
48 | var editor = CodeEditor.Editor.GetCodeEditorForPath("code.cmd");
49 | if (editor == null)
50 | return;
51 |
52 | if (editor is VisualStudioEditor)
53 | return;
54 |
55 | // only disable the com.unity.ide.vscode package
56 | var assembly = editor.GetType().Assembly;
57 | var assemblyName = assembly.GetName().Name;
58 | if (assemblyName != "Unity.VSCode.Editor")
59 | return;
60 |
61 | CodeEditor.Unregister(editor);
62 | }
63 | #endif
64 |
65 | private static Dictionary DiscoverInstallations()
66 | {
67 | try
68 | {
69 | return Discovery
70 | .GetVisualStudioInstallations()
71 | .ToDictionary(i => Path.GetFullPath(i.Path), i => i);
72 | }
73 | catch (Exception ex)
74 | {
75 | Debug.LogError($"Error detecting Visual Studio installations: {ex}");
76 | return new Dictionary();
77 | }
78 | }
79 |
80 | internal static bool IsEnabled => CodeEditor.CurrentEditor is VisualStudioEditor && UnityInstallation.IsMainUnityEditorProcess;
81 |
82 | // this one seems legacy and not used anymore
83 | // keeping it for now given it is public, so we need a major bump to remove it
84 | public void CreateIfDoesntExist()
85 | {
86 | if (!TryGetVisualStudioInstallationForPath(CodeEditor.CurrentEditorInstallation, true, out var installation))
87 | return;
88 |
89 | var generator = installation.ProjectGenerator;
90 | if (!generator.HasSolutionBeenGenerated())
91 | generator.Sync();
92 | }
93 |
94 | public void Initialize(string editorInstallationPath)
95 | {
96 | }
97 |
98 | internal virtual bool TryGetVisualStudioInstallationForPath(string editorPath, bool lookupDiscoveredInstallations, out IVisualStudioInstallation installation)
99 | {
100 | editorPath = Path.GetFullPath(editorPath);
101 |
102 | // lookup for well known installations
103 | if (lookupDiscoveredInstallations && _discoverInstallations.Result.TryGetValue(editorPath, out installation))
104 | return true;
105 |
106 | return Discovery.TryDiscoverInstallation(editorPath, out installation);
107 | }
108 |
109 | public virtual bool TryGetInstallationForPath(string editorPath, out CodeEditor.Installation installation)
110 | {
111 | var result = TryGetVisualStudioInstallationForPath(editorPath, lookupDiscoveredInstallations: false, out var vsi);
112 | installation = vsi?.ToCodeEditorInstallation() ?? default;
113 | return result;
114 | }
115 |
116 | public void OnGUI()
117 | {
118 | GUILayout.BeginHorizontal();
119 | GUILayout.FlexibleSpace();
120 |
121 | if (!TryGetVisualStudioInstallationForPath(CodeEditor.CurrentEditorInstallation, true, out var installation))
122 | return;
123 |
124 | var package = UnityEditor.PackageManager.PackageInfo.FindForAssembly(GetType().Assembly);
125 |
126 | var style = new GUIStyle
127 | {
128 | richText = true,
129 | margin = new RectOffset(0, 4, 0, 0)
130 | };
131 |
132 | GUILayout.Label($"{package.displayName} v{package.version} enabled", style);
133 | GUILayout.EndHorizontal();
134 |
135 | EditorGUILayout.LabelField("Generate .csproj files for:");
136 | EditorGUI.indentLevel++;
137 | SettingsButton(ProjectGenerationFlag.Embedded, "Embedded packages", "", installation);
138 | SettingsButton(ProjectGenerationFlag.Local, "Local packages", "", installation);
139 | SettingsButton(ProjectGenerationFlag.Registry, "Registry packages", "", installation);
140 | SettingsButton(ProjectGenerationFlag.Git, "Git packages", "", installation);
141 | SettingsButton(ProjectGenerationFlag.BuiltIn, "Built-in packages", "", installation);
142 | SettingsButton(ProjectGenerationFlag.LocalTarBall, "Local tarball", "", installation);
143 | SettingsButton(ProjectGenerationFlag.Unknown, "Packages from unknown sources", "", installation);
144 | SettingsButton(ProjectGenerationFlag.PlayerAssemblies, "Player projects", "For each player project generate an additional csproj with the name 'project-player.csproj'", installation);
145 | RegenerateProjectFiles(installation);
146 | EditorGUI.indentLevel--;
147 | }
148 |
149 | private static void RegenerateProjectFiles(IVisualStudioInstallation installation)
150 | {
151 | var rect = EditorGUI.IndentedRect(EditorGUILayout.GetControlRect());
152 | rect.width = 252;
153 | if (GUI.Button(rect, "Regenerate project files"))
154 | {
155 | installation.ProjectGenerator.Sync();
156 | }
157 | }
158 |
159 | private static void SettingsButton(ProjectGenerationFlag preference, string guiMessage, string toolTip, IVisualStudioInstallation installation)
160 | {
161 | var generator = installation.ProjectGenerator;
162 | var prevValue = generator.AssemblyNameProvider.ProjectGenerationFlag.HasFlag(preference);
163 |
164 | var newValue = EditorGUILayout.Toggle(new GUIContent(guiMessage, toolTip), prevValue);
165 | if (newValue != prevValue)
166 | generator.AssemblyNameProvider.ToggleProjectGeneration(preference);
167 | }
168 |
169 | public void SyncIfNeeded(string[] addedFiles, string[] deletedFiles, string[] movedFiles, string[] movedFromFiles, string[] importedFiles)
170 | {
171 | if (TryGetVisualStudioInstallationForPath(CodeEditor.CurrentEditorInstallation, true, out var installation))
172 | {
173 | installation.ProjectGenerator.SyncIfNeeded(addedFiles.Union(deletedFiles).Union(movedFiles).Union(movedFromFiles), importedFiles);
174 | }
175 |
176 | foreach (var file in importedFiles.Where(a => Path.GetExtension(a) == ".pdb"))
177 | {
178 | var pdbFile = FileUtility.GetAssetFullPath(file);
179 |
180 | // skip Unity packages like com.unity.ext.nunit
181 | if (pdbFile.IndexOf($"{Path.DirectorySeparatorChar}com.unity.", StringComparison.OrdinalIgnoreCase) > 0)
182 | continue;
183 |
184 | var asmFile = Path.ChangeExtension(pdbFile, ".dll");
185 | if (!File.Exists(asmFile) || !Image.IsAssembly(asmFile))
186 | continue;
187 |
188 | if (Symbols.IsPortableSymbolFile(pdbFile))
189 | continue;
190 |
191 | Debug.LogWarning($"Unity is only able to load mdb or portable-pdb symbols. {file} is using a legacy pdb format.");
192 | }
193 | }
194 |
195 | public void SyncAll()
196 | {
197 | if (TryGetVisualStudioInstallationForPath(CodeEditor.CurrentEditorInstallation, true, out var installation))
198 | {
199 | installation.ProjectGenerator.Sync();
200 | }
201 | }
202 |
203 | private static bool IsSupportedPath(string path, IGenerator generator)
204 | {
205 | // Path is empty with "Open C# Project", as we only want to open the solution without specific files
206 | if (string.IsNullOrEmpty(path))
207 | return true;
208 |
209 | // cs, uxml, uss, shader, compute, cginc, hlsl, glslinc, template are part of Unity builtin extensions
210 | // txt, xml, fnt, cd are -often- par of Unity user extensions
211 | // asdmdef is mandatory included
212 | return generator.IsSupportedFile(path);
213 | }
214 |
215 | public bool OpenProject(string path, int line, int column)
216 | {
217 | var editorPath = CodeEditor.CurrentEditorInstallation;
218 |
219 | if (!Discovery.TryDiscoverInstallation(editorPath, out var installation)) {
220 | Debug.LogWarning($"Visual Studio executable {editorPath} is not found. Please change your settings in Edit > Preferences > External Tools.");
221 | return false;
222 | }
223 |
224 | var generator = installation.ProjectGenerator;
225 | if (!IsSupportedPath(path, generator))
226 | return false;
227 |
228 | if (!IsProjectGeneratedFor(path, generator, out var missingFlag))
229 | Debug.LogWarning($"You are trying to open {path} outside a generated project. This might cause problems with IntelliSense and debugging. To avoid this, you can change your .csproj preferences in Edit > Preferences > External Tools and enable {GetProjectGenerationFlagDescription(missingFlag)} generation.");
230 |
231 | var solution = GetOrGenerateSolutionFile(generator);
232 | return installation.Open(path, line, column, solution);
233 | }
234 |
235 | private static string GetProjectGenerationFlagDescription(ProjectGenerationFlag flag)
236 | {
237 | switch (flag)
238 | {
239 | case ProjectGenerationFlag.BuiltIn:
240 | return "Built-in packages";
241 | case ProjectGenerationFlag.Embedded:
242 | return "Embedded packages";
243 | case ProjectGenerationFlag.Git:
244 | return "Git packages";
245 | case ProjectGenerationFlag.Local:
246 | return "Local packages";
247 | case ProjectGenerationFlag.LocalTarBall:
248 | return "Local tarball";
249 | case ProjectGenerationFlag.PlayerAssemblies:
250 | return "Player projects";
251 | case ProjectGenerationFlag.Registry:
252 | return "Registry packages";
253 | case ProjectGenerationFlag.Unknown:
254 | return "Packages from unknown sources";
255 | default:
256 | return string.Empty;
257 | }
258 | }
259 |
260 | private static bool IsProjectGeneratedFor(string path, IGenerator generator, out ProjectGenerationFlag missingFlag)
261 | {
262 | missingFlag = ProjectGenerationFlag.None;
263 |
264 | // No need to check when opening the whole solution
265 | if (string.IsNullOrEmpty(path))
266 | return true;
267 |
268 | // We only want to check for cs scripts
269 | if (ProjectGeneration.ScriptingLanguageForFile(path) != ScriptingLanguage.CSharp)
270 | return true;
271 |
272 | // Even on windows, the package manager requires relative path + unix style separators for queries
273 | var basePath = generator.ProjectDirectory;
274 | var relativePath = path
275 | .NormalizeWindowsToUnix()
276 | .Replace(basePath, string.Empty)
277 | .Trim(FileUtility.UnixSeparator);
278 |
279 | var packageInfo = UnityEditor.PackageManager.PackageInfo.FindForAssetPath(relativePath);
280 | if (packageInfo == null)
281 | return true;
282 |
283 | var source = packageInfo.source;
284 | if (!Enum.TryParse(source.ToString(), out var flag))
285 | return true;
286 |
287 | if (generator.AssemblyNameProvider.ProjectGenerationFlag.HasFlag(flag))
288 | return true;
289 |
290 | // Return false if we found a source not flagged for generation
291 | missingFlag = flag;
292 | return false;
293 | }
294 |
295 | private static string GetOrGenerateSolutionFile(IGenerator generator)
296 | {
297 | generator.Sync();
298 | return generator.SolutionFile();
299 | }
300 | }
301 | }
302 |
--------------------------------------------------------------------------------
/Editor/VisualStudioEditor.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ddfe5c11838c29047931abace68bf11e
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/VisualStudioInstallation.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 | using System;
6 | using System.IO;
7 | using Unity.CodeEditor;
8 | using IOPath = System.IO.Path;
9 |
10 | namespace Microsoft.Unity.VisualStudio.Editor
11 | {
12 | internal interface IVisualStudioInstallation
13 | {
14 | string Path { get; }
15 | bool SupportsAnalyzers { get; }
16 | Version LatestLanguageVersionSupported { get; }
17 | string[] GetAnalyzers();
18 | CodeEditor.Installation ToCodeEditorInstallation();
19 | bool Open(string path, int line, int column, string solutionPath);
20 | IGenerator ProjectGenerator { get; }
21 | void CreateExtraFiles(string projectDirectory);
22 | }
23 |
24 | internal abstract class VisualStudioInstallation : IVisualStudioInstallation
25 | {
26 | public string Name { get; set; }
27 | public string Path { get; set; }
28 | public Version Version { get; set; }
29 | public bool IsPrerelease { get; set; }
30 |
31 | public abstract bool SupportsAnalyzers { get; }
32 | public abstract Version LatestLanguageVersionSupported { get; }
33 | public abstract string[] GetAnalyzers();
34 | public abstract IGenerator ProjectGenerator { get; }
35 | public abstract void CreateExtraFiles(string projectDirectory);
36 | public abstract bool Open(string path, int line, int column, string solutionPath);
37 |
38 | protected Version GetLatestLanguageVersionSupported(VersionPair[] versions)
39 | {
40 | if (versions != null)
41 | {
42 | foreach (var entry in versions)
43 | {
44 | if (Version >= entry.IdeVersion)
45 | return entry.LanguageVersion;
46 | }
47 | }
48 |
49 | // default to 7.0
50 | return new Version(7, 0);
51 | }
52 |
53 | protected static string[] GetAnalyzers(string path)
54 | {
55 | var analyzersDirectory = IOPath.GetFullPath(IOPath.Combine(path, "Analyzers"));
56 |
57 | if (Directory.Exists(analyzersDirectory))
58 | return Directory.GetFiles(analyzersDirectory, "*Analyzers.dll", SearchOption.AllDirectories);
59 |
60 | return Array.Empty();
61 | }
62 |
63 | public CodeEditor.Installation ToCodeEditorInstallation()
64 | {
65 | return new CodeEditor.Installation() { Name = Name, Path = Path };
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Editor/VisualStudioInstallation.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c452f2dfc99f91b4c9883719d71b2d8f
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/VisualStudioIntegration.cs:
--------------------------------------------------------------------------------
1 | /*---------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | *--------------------------------------------------------------------------------------------*/
5 | using System;
6 | using System.Collections.Generic;
7 | using System.IO;
8 | using System.Linq;
9 | using System.Net;
10 | using System.Net.Sockets;
11 | using Microsoft.Unity.VisualStudio.Editor.Messaging;
12 | using Microsoft.Unity.VisualStudio.Editor.Testing;
13 | using UnityEditor;
14 | using UnityEngine;
15 | using MessageType = Microsoft.Unity.VisualStudio.Editor.Messaging.MessageType;
16 |
17 | namespace Microsoft.Unity.VisualStudio.Editor
18 | {
19 | [InitializeOnLoad]
20 | internal class VisualStudioIntegration
21 | {
22 | class Client
23 | {
24 | public IPEndPoint EndPoint { get; set; }
25 | public double LastMessage { get; set; }
26 | }
27 |
28 | private static Messager _messager;
29 |
30 | private static readonly Queue _incoming = new Queue();
31 | private static readonly Dictionary _clients = new Dictionary();
32 | private static readonly object _incomingLock = new object();
33 | private static readonly object _clientsLock = new object();
34 |
35 | static VisualStudioIntegration()
36 | {
37 | if (!VisualStudioEditor.IsEnabled)
38 | return;
39 |
40 | RunOnceOnUpdate(() =>
41 | {
42 | // Despite using ReuseAddress|!ExclusiveAddressUse, we can fail here:
43 | // - if another application is using this port with exclusive access
44 | // - or if the firewall is not properly configured
45 | var messagingPort = MessagingPort();
46 |
47 | try
48 | {
49 | _messager = Messager.BindTo(messagingPort);
50 | _messager.ReceiveMessage += ReceiveMessage;
51 | }
52 | catch (SocketException)
53 | {
54 | // We'll have a chance to try to rebind on next domain reload
55 | Debug.LogWarning($"Unable to use UDP port {messagingPort} for VS/Unity messaging. You should check if another process is already bound to this port or if your firewall settings are compatible.");
56 | }
57 |
58 | RunOnShutdown(Shutdown);
59 | });
60 |
61 | EditorApplication.update += OnUpdate;
62 |
63 | CheckLegacyAssemblies();
64 | }
65 |
66 | private static void CheckLegacyAssemblies()
67 | {
68 | var checkList = new HashSet(new[] { KnownAssemblies.UnityVS, KnownAssemblies.Messaging, KnownAssemblies.Bridge });
69 |
70 | try
71 | {
72 | var assemblies = AppDomain
73 | .CurrentDomain
74 | .GetAssemblies()
75 | .Where(a => checkList.Contains(a.GetName().Name));
76 |
77 | foreach (var assembly in assemblies)
78 | {
79 | // for now we only want to warn against local assemblies, do not check externals.
80 | var relativePath = FileUtility.MakeRelativeToProjectPath(assembly.Location);
81 | if (relativePath == null)
82 | continue;
83 |
84 | Debug.LogWarning($"Project contains legacy assembly that could interfere with the Visual Studio Package. You should delete {relativePath}");
85 | }
86 | }
87 | catch (Exception)
88 | {
89 | // abandon legacy check
90 | }
91 | }
92 |
93 | private static void RunOnceOnUpdate(Action action)
94 | {
95 | var callback = null as EditorApplication.CallbackFunction;
96 |
97 | callback = () =>
98 | {
99 | EditorApplication.update -= callback;
100 | action();
101 | };
102 |
103 | EditorApplication.update += callback;
104 | }
105 |
106 | private static void RunOnShutdown(Action action)
107 | {
108 | // Mono on OSX has all kinds of quirks on AppDomain shutdown
109 | #if UNITY_EDITOR_WIN
110 | AppDomain.CurrentDomain.DomainUnload += (_, __) => action();
111 | #endif
112 | }
113 |
114 | private static int DebuggingPort()
115 | {
116 | return 56000 + (System.Diagnostics.Process.GetCurrentProcess().Id % 1000);
117 | }
118 |
119 | private static int MessagingPort()
120 | {
121 | return DebuggingPort() + 2;
122 | }
123 |
124 | private static void ReceiveMessage(object sender, MessageEventArgs args)
125 | {
126 | OnMessage(args.Message);
127 | }
128 |
129 | private static void OnUpdate()
130 | {
131 | lock (_incomingLock)
132 | {
133 | while (_incoming.Count > 0)
134 | {
135 | ProcessIncoming(_incoming.Dequeue());
136 | }
137 | }
138 |
139 | lock (_clientsLock)
140 | {
141 | foreach (var client in _clients.Values.ToArray())
142 | {
143 | // EditorApplication.timeSinceStartup: The time since the editor was started, in seconds, not reset when starting play mode.
144 | if (EditorApplication.timeSinceStartup - client.LastMessage > 4)
145 | _clients.Remove(client.EndPoint);
146 | }
147 | }
148 | }
149 |
150 | private static void AddMessage(Message message)
151 | {
152 | lock (_incomingLock)
153 | {
154 | _incoming.Enqueue(message);
155 | }
156 | }
157 |
158 | private static void ProcessIncoming(Message message)
159 | {
160 | lock (_clientsLock)
161 | {
162 | CheckClient(message);
163 | }
164 |
165 | switch (message.Type)
166 | {
167 | case MessageType.Ping:
168 | Answer(message, MessageType.Pong);
169 | break;
170 | case MessageType.Play:
171 | Shutdown();
172 | EditorApplication.isPlaying = true;
173 | break;
174 | case MessageType.Stop:
175 | EditorApplication.isPlaying = false;
176 | break;
177 | case MessageType.Pause:
178 | EditorApplication.isPaused = true;
179 | break;
180 | case MessageType.Unpause:
181 | EditorApplication.isPaused = false;
182 | break;
183 | case MessageType.Build:
184 | // Not used anymore
185 | break;
186 | case MessageType.Refresh:
187 | Refresh();
188 | break;
189 | case MessageType.Version:
190 | Answer(message, MessageType.Version, PackageVersion());
191 | break;
192 | case MessageType.UpdatePackage:
193 | // Not used anymore
194 | break;
195 | case MessageType.ProjectPath:
196 | Answer(message, MessageType.ProjectPath, Path.GetFullPath(Path.Combine(Application.dataPath, "..")));
197 | break;
198 | case MessageType.ExecuteTests:
199 | TestRunnerApiListener.ExecuteTests(message.Value);
200 | break;
201 | case MessageType.RetrieveTestList:
202 | TestRunnerApiListener.RetrieveTestList(message.Value);
203 | break;
204 | case MessageType.ShowUsage:
205 | UsageUtility.ShowUsage(message.Value);
206 | break;
207 | }
208 | }
209 |
210 | private static void CheckClient(Message message)
211 | {
212 | var endPoint = message.Origin;
213 |
214 | if (!_clients.TryGetValue(endPoint, out var client))
215 | {
216 | client = new Client
217 | {
218 | EndPoint = endPoint,
219 | LastMessage = EditorApplication.timeSinceStartup
220 | };
221 |
222 | _clients.Add(endPoint, client);
223 | }
224 | else
225 | {
226 | client.LastMessage = EditorApplication.timeSinceStartup;
227 | }
228 | }
229 |
230 | internal static string PackageVersion()
231 | {
232 | var package = UnityEditor.PackageManager.PackageInfo.FindForAssembly(typeof(VisualStudioIntegration).Assembly);
233 | return package.version;
234 | }
235 |
236 | private static void Refresh()
237 | {
238 | // If the user disabled auto-refresh in Unity, do not try to force refresh the Asset database
239 | if (!EditorPrefs.GetBool("kAutoRefresh", true))
240 | return;
241 |
242 | if (UnityInstallation.IsInSafeMode)
243 | return;
244 |
245 | RunOnceOnUpdate(AssetDatabase.Refresh);
246 | }
247 |
248 | private static void OnMessage(Message message)
249 | {
250 | AddMessage(message);
251 | }
252 |
253 | private static void Answer(Client client, MessageType answerType, string answerValue)
254 | {
255 | Answer(client.EndPoint, answerType, answerValue);
256 | }
257 |
258 | private static void Answer(Message message, MessageType answerType, string answerValue = "")
259 | {
260 | var targetEndPoint = message.Origin;
261 |
262 | Answer(
263 | targetEndPoint,
264 | answerType,
265 | answerValue);
266 | }
267 |
268 | private static void Answer(IPEndPoint targetEndPoint, MessageType answerType, string answerValue)
269 | {
270 | _messager?.SendMessage(targetEndPoint, answerType, answerValue);
271 | }
272 |
273 | private static void Shutdown()
274 | {
275 | if (_messager == null)
276 | return;
277 |
278 | _messager.ReceiveMessage -= ReceiveMessage;
279 | _messager.Dispose();
280 | _messager = null;
281 | }
282 |
283 | internal static void BroadcastMessage(MessageType type, string value)
284 | {
285 | lock (_clientsLock)
286 | {
287 | foreach (var client in _clients.Values.ToArray())
288 | {
289 | Answer(client, type, value);
290 | }
291 | }
292 | }
293 | }
294 | }
295 |
--------------------------------------------------------------------------------
/Editor/VisualStudioIntegration.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b13003ee9d8f4aa4eadd98d24adbc79b
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/com.unity.ide.visualstudio.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Unity.Cursor.Editor",
3 | "references": [],
4 | "includePlatforms": [
5 | "Editor"
6 | ],
7 | "excludePlatforms": [],
8 | "allowUnsafeCode": false,
9 | "overrideReferences": true,
10 | "precompiledReferences": [
11 | "Newtonsoft.Json.dll"
12 | ],
13 | "autoReferenced": true,
14 | "defineConstraints": [],
15 | "versionDefines": [],
16 | "noEngineReferences": false
17 | }
--------------------------------------------------------------------------------
/Editor/com.unity.ide.visualstudio.asmdef.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 3bdbd29a5ae7b9d4ead62ff67cdd7d57
3 | AssemblyDefinitionImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Unity Technologies
4 | Copyright (c) 2019 Microsoft Corporation. All rights reserved.
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
--------------------------------------------------------------------------------
/LICENSE.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e1e01663a79c63e45b71da40a22e6954
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # How to install
2 |
3 | - Unity->Window->Package Manager
4 | - Click "+" left corner
5 | - Add package from git URL
6 | - Insert https://github.com/liyingsong99/com.unity.ide.cursor-et.git
7 | - Add
8 | - Done
9 |
10 | # 关于Cursor中的插件
11 | - C# Dev Kit 推荐使用v1.18.23 版本,并且禁止自动更新。
12 | - C# 推荐使用v2.72.27版本,同样禁止自动更新。
13 | - Unity 插件安装最新版本即可。
14 |
15 | # 关于ET框架适配内容
16 | - 适配了ET.sln解决方案
17 | - 添加了ET框架的rule参考,最好自己手动添加rule拓展内容,并复制到Cursor的默认Rule中。
18 |
--------------------------------------------------------------------------------
/README.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e06b3ec6cb984424bbf989b37579c2e2
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/ThirdPartyNotices.md:
--------------------------------------------------------------------------------
1 | This package contains third-party software components governed by the license(s) indicated below:
2 | ---------
3 |
4 | Component Name: VSWhere
5 |
6 | License Type: "MIT"
7 |
8 | The MIT License (MIT)
9 | Copyright (C) Microsoft Corporation. All rights reserved.
10 |
11 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 |
17 | ---------
18 | Component Name: benbuck/EnvDTE
19 |
20 | License Type: Zero-Clause BSD
21 |
22 | Permission to use, copy, modify, and/or distribute this software for any purpose
23 | with or without fee is hereby granted.
24 |
25 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
26 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
27 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
28 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
29 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
30 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
31 | THIS SOFTWARE.
--------------------------------------------------------------------------------
/ThirdPartyNotices.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 613aeddc44f353d4d9e9986919e730b9
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/ValidationConfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "FileContentKeywordValidation":
3 | {
4 | "Keywords":
5 | [
6 | {
7 | "Targets": "+",
8 | "Files":
9 | [
10 | "Editor/VisualStudioForWindowsInstallation.cs"
11 | ],
12 | "Patterns":
13 | [
14 | "vswhere\\.exe"
15 | ]
16 | }
17 | ]
18 | }
19 | }
--------------------------------------------------------------------------------
/ValidationConfig.json.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 8e9d2159475628940bb30bf1ca8c5432
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/ValidationExceptions.json:
--------------------------------------------------------------------------------
1 | {
2 | "ErrorExceptions": [
3 | {
4 | "ValidationTest": "API Validation",
5 | "ExceptionMessage": "Breaking changes require a new major version.",
6 | "PackageVersion": "2.0.18"
7 | },
8 | {
9 | "ValidationTest": "API Validation",
10 | "ExceptionMessage": "Additions require a new minor or major version.",
11 | "PackageVersion": "2.0.18"
12 | }
13 | ],
14 | "WarningExceptions": []
15 | }
--------------------------------------------------------------------------------
/ValidationExceptions.json.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e71f2385c737b5249a357a30c44ba7cb
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "com.boxqkrtm.ide.cursor",
3 | "displayName": "Cursor Editor",
4 | "description": "Cursor editor integration for supporting Cursor as code editor for unity. Adds support for generating csproj files for intellisense purposes, auto discovery of installations, etc.",
5 | "version": "2.0.24",
6 | "unity": "2019.4",
7 | "unityRelease": "25f1",
8 | "dependencies": {
9 | "com.unity.test-framework": "1.1.9"
10 | },
11 | "_upm": {
12 | "changelog": "Integration:\n\n- Add support for Cursor"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/package.json.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 0214c79dfec0ce44486e7d7e8d9b981b
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/rules/etrules.mdc:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | globs:
4 | alwaysApply: true
5 | ---
6 | Always respond in 中文
7 |
8 | # 文件创建规则
9 | -- Packages/cn.etetet.* 路径下创建的所有非静态类的代码文件,默认都为Entity组件
10 | -- 创建的所有类文件,都必须创建到Packages/cn.etetet.*目录下,*号代表具体的包名,例如core包代表着Packages/cn.etetet.core
11 | -- Entity 文件需要创建到Scritps/Model或Scritps/ModelView文件夹下, 并且必须继承Entity类和IAwake接口,至于其他接口,则根据需要添加
12 | -- Entity 类不允许有任何方法,其方法必须在对应的System类中以静态拓展函数的方式实现
13 | -- Entity 对应的System类需要创建到Scritps/Hotfix或Scritps/HotfixView文件夹下,必须为静态类且包含partial关键字,同时默认需要添加[EntitySystemOf(typeof(*))]标签,以及Awake生命周期函数
14 | -- 所有创建的类,其NameSpace默认都为ET或ET.Client或ET.Server
15 | -- 当创建entity到*包下时,需要分析该组件属于Model、ModelView、Hotfix、HotfixView中的某两个程序集,然后自动添加到相应的路径下,例如Packages/cn.etetet.* /Scripts/Model 及 Packages/cn.etetet.* /Scripts/Hotfix目录下
--------------------------------------------------------------------------------
/rules/etrules.mdc.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e6aac6cb208eaa04482b0764d486045e
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------