You've already forked com.unity.ide.cursor
mirror of
https://github.com/boxqkrtm/com.unity.ide.cursor.git
synced 2026-05-14 14:20:09 +00:00
com.unity.ide.visualstudio@2.0.8
## [2.0.8] - 2021-04-09 Project generation: Improved generation performance (especially with DOTS enabled projects). Improved stability. Updated Analyzers lookup strategy. Fixed .vsconfig file not generated when using "regenerate all". Integration Improved automation plugins. Documentation Open sourced automation plugins.
This commit is contained in:
20
CHANGELOG.md
20
CHANGELOG.md
@@ -1,10 +1,28 @@
|
||||
# Code Editor Package for Visual Studio
|
||||
|
||||
## [2.0.8] - 2021-04-09
|
||||
|
||||
Project generation:
|
||||
|
||||
Improved generation performance (especially with DOTS enabled projects).
|
||||
Improved stability.
|
||||
Updated Analyzers lookup strategy.
|
||||
Fixed .vsconfig file not generated when using "regenerate all".
|
||||
|
||||
Integration
|
||||
|
||||
Improved automation plugins.
|
||||
|
||||
Documentation
|
||||
|
||||
Open sourced automation plugins.
|
||||
|
||||
|
||||
## [2.0.7] - 2021-02-02
|
||||
|
||||
Integration:
|
||||
|
||||
Remove com.unity.nuget.newtonsoft-json dependency in favor of the built-in JsonUtility for the VS Test Runner.
|
||||
- Remove com.unity.nuget.newtonsoft-json dependency in favor of the built-in JsonUtility for the VS Test Runner.
|
||||
|
||||
|
||||
## [2.0.6] - 2021-01-20
|
||||
|
||||
@@ -0,0 +1,308 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 50;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
E08E02FF236392D000A4B1BE /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = E08E02FE236392D000A4B1BE /* main.mm */; };
|
||||
E08E03022363933B00A4B1BE /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E08E03012363933B00A4B1BE /* AppKit.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
E08E02F5236392A300A4B1BE /* AppleEventIntegration.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppleEventIntegration.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
E08E02F8236392A300A4B1BE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
E08E02FE236392D000A4B1BE /* main.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = "<group>"; };
|
||||
E08E03012363933B00A4B1BE /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
E08E02F2236392A300A4B1BE /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
E08E03022363933B00A4B1BE /* AppKit.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
E08E02EC236392A300A4B1BE = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E08E02F7236392A300A4B1BE /* AppleEventIntegration */,
|
||||
E08E02F6236392A300A4B1BE /* Products */,
|
||||
E08E03002363933B00A4B1BE /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E08E02F6236392A300A4B1BE /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E08E02F5236392A300A4B1BE /* AppleEventIntegration.bundle */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E08E02F7236392A300A4B1BE /* AppleEventIntegration */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E08E02F8236392A300A4B1BE /* Info.plist */,
|
||||
E08E02FE236392D000A4B1BE /* main.mm */,
|
||||
);
|
||||
path = AppleEventIntegration;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E08E03002363933B00A4B1BE /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E08E03012363933B00A4B1BE /* AppKit.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
E08E02F4236392A300A4B1BE /* AppleEventIntegration */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = E08E02FB236392A300A4B1BE /* Build configuration list for PBXNativeTarget "AppleEventIntegration" */;
|
||||
buildPhases = (
|
||||
E08E02F1236392A300A4B1BE /* Sources */,
|
||||
E08E02F2236392A300A4B1BE /* Frameworks */,
|
||||
E08E02F3236392A300A4B1BE /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = AppleEventIntegration;
|
||||
productName = AppleEventIntegration;
|
||||
productReference = E08E02F5236392A300A4B1BE /* AppleEventIntegration.bundle */;
|
||||
productType = "com.apple.product-type.bundle";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
E08E02ED236392A300A4B1BE /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1200;
|
||||
ORGANIZATIONNAME = Unity;
|
||||
TargetAttributes = {
|
||||
E08E02F4236392A300A4B1BE = {
|
||||
CreatedOnToolsVersion = 11.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = E08E02F0236392A300A4B1BE /* Build configuration list for PBXProject "AppleEventIntegration" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = E08E02EC236392A300A4B1BE;
|
||||
productRefGroup = E08E02F6236392A300A4B1BE /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
E08E02F4236392A300A4B1BE /* AppleEventIntegration */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
E08E02F3236392A300A4B1BE /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
E08E02F1236392A300A4B1BE /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
E08E02FF236392D000A4B1BE /* main.mm in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
E08E02F9236392A300A4B1BE /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
E08E02FA236392A300A4B1BE /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
E08E02FC236392A300A4B1BE /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
INFOPLIST_FILE = AppleEventIntegration/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.unity.visualstudio.AppleEventIntegration;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
WRAPPER_EXTENSION = bundle;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
E08E02FD236392A300A4B1BE /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
INFOPLIST_FILE = AppleEventIntegration/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.unity.visualstudio.AppleEventIntegration;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
WRAPPER_EXTENSION = bundle;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
E08E02F0236392A300A4B1BE /* Build configuration list for PBXProject "AppleEventIntegration" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
E08E02F9236392A300A4B1BE /* Debug */,
|
||||
E08E02FA236392A300A4B1BE /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
E08E02FB236392A300A4B1BE /* Build configuration list for PBXNativeTarget "AppleEventIntegration" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
E08E02FC236392A300A4B1BE /* Debug */,
|
||||
E08E02FD236392A300A4B1BE /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = E08E02ED236392A300A4B1BE /* Project object */;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2019 Unity. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
||||
291
Editor/AppleEventIntegration~/AppleEventIntegration/main.mm
Normal file
291
Editor/AppleEventIntegration~/AppleEventIntegration/main.mm
Normal file
@@ -0,0 +1,291 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Unity Technologies.
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// 'FSnd' FourCC
|
||||
#define keyFileSender 1179872868
|
||||
|
||||
// 16 bit aligned legacy struct - this should total 20 bytes
|
||||
struct SelectionRange
|
||||
{
|
||||
int16_t unused1; // 0 (not used)
|
||||
int16_t lineNum; // line to select (<0 to specify range)
|
||||
int32_t startRange; // start of selection range (if line < 0)
|
||||
int32_t endRange; // end of selection range (if line < 0)
|
||||
int32_t unused2; // 0 (not used)
|
||||
int32_t theDate; // modification date/time
|
||||
} __attribute__((packed));
|
||||
|
||||
static NSString* MakeNSString(const char* str)
|
||||
{
|
||||
if (!str)
|
||||
return NULL;
|
||||
|
||||
NSString* ret = [NSString stringWithUTF8String: str];
|
||||
return ret;
|
||||
}
|
||||
|
||||
static UInt32 GetCreatorOfThisApp()
|
||||
{
|
||||
static UInt32 creator = 0;
|
||||
if (creator == 0)
|
||||
{
|
||||
UInt32 type;
|
||||
CFBundleGetPackageInfo(CFBundleGetMainBundle(), &type, &creator);
|
||||
}
|
||||
return creator;
|
||||
}
|
||||
|
||||
static BOOL OpenFileAtLineWithAppleEvent(NSRunningApplication *runningApp, NSString* path, int line)
|
||||
{
|
||||
if (!runningApp)
|
||||
return NO;
|
||||
|
||||
NSURL *pathUrl = [NSURL fileURLWithPath: path];
|
||||
|
||||
NSAppleEventDescriptor* targetDescriptor = [NSAppleEventDescriptor
|
||||
descriptorWithProcessIdentifier: runningApp.processIdentifier];
|
||||
|
||||
NSAppleEventDescriptor* appleEvent = [NSAppleEventDescriptor
|
||||
appleEventWithEventClass: kCoreEventClass
|
||||
eventID: kAEOpenDocuments
|
||||
targetDescriptor: targetDescriptor
|
||||
returnID: kAutoGenerateReturnID
|
||||
transactionID: kAnyTransactionID];
|
||||
|
||||
[appleEvent
|
||||
setParamDescriptor: [NSAppleEventDescriptor
|
||||
descriptorWithDescriptorType: typeFileURL
|
||||
data: [[pathUrl absoluteString] dataUsingEncoding: NSUTF8StringEncoding]]
|
||||
forKeyword: keyDirectObject];
|
||||
|
||||
UInt32 packageCreator = GetCreatorOfThisApp();
|
||||
if (packageCreator == kUnknownType) {
|
||||
[appleEvent
|
||||
setParamDescriptor: [NSAppleEventDescriptor
|
||||
descriptorWithDescriptorType: typeApplicationBundleID
|
||||
data: [[[NSBundle mainBundle] bundleIdentifier] dataUsingEncoding: NSUTF8StringEncoding]]
|
||||
forKeyword: keyFileSender];
|
||||
} else {
|
||||
[appleEvent
|
||||
setParamDescriptor: [NSAppleEventDescriptor descriptorWithTypeCode: packageCreator]
|
||||
forKeyword: keyFileSender];
|
||||
}
|
||||
|
||||
if (line != -1) {
|
||||
// Add selection range to event
|
||||
SelectionRange range;
|
||||
range.unused1 = 0;
|
||||
range.lineNum = line - 1;
|
||||
range.startRange = -1;
|
||||
range.endRange = -1;
|
||||
range.unused2 = 0;
|
||||
range.theDate = -1;
|
||||
|
||||
[appleEvent
|
||||
setParamDescriptor: [NSAppleEventDescriptor
|
||||
descriptorWithDescriptorType: typeChar
|
||||
bytes: &range
|
||||
length: sizeof(SelectionRange)]
|
||||
forKeyword: keyAEPosition];
|
||||
}
|
||||
|
||||
AEDesc reply = { typeNull, NULL };
|
||||
OSErr err = AESendMessage(
|
||||
[appleEvent aeDesc],
|
||||
&reply,
|
||||
kAENoReply + kAENeverInteract,
|
||||
kAEDefaultTimeout);
|
||||
|
||||
return err == noErr;
|
||||
}
|
||||
|
||||
static BOOL ApplicationSupportsQueryOpenedSolution(NSString* appPath)
|
||||
{
|
||||
NSURL* appUrl = [NSURL fileURLWithPath: appPath];
|
||||
NSBundle* bundle = [NSBundle bundleWithURL: appUrl];
|
||||
|
||||
if (!bundle)
|
||||
return NO;
|
||||
|
||||
id versionValue = [bundle objectForInfoDictionaryKey: @"CFBundleVersion"];
|
||||
if (!versionValue || ![versionValue isKindOfClass: [NSString class]])
|
||||
return NO;
|
||||
|
||||
NSString* version = (NSString*)versionValue;
|
||||
NSArray* components = [version componentsSeparatedByString:@"."];
|
||||
if (!components || components.count < 2)
|
||||
return NO;
|
||||
|
||||
return [components[0] integerValue] >= 8
|
||||
&& [components[1] integerValue] >= 6;
|
||||
}
|
||||
|
||||
static NSArray<NSRunningApplication*>* QueryRunningInstances(NSString *appPath)
|
||||
{
|
||||
NSMutableArray<NSRunningApplication*>* instances = [[NSMutableArray alloc] init];
|
||||
NSURL *appUrl = [NSURL fileURLWithPath: appPath];
|
||||
|
||||
for (NSRunningApplication *runningApp in NSWorkspace.sharedWorkspace.runningApplications) {
|
||||
if (![runningApp isTerminated] && [runningApp.bundleURL isEqual: appUrl]) {
|
||||
[instances addObject: runningApp];
|
||||
}
|
||||
}
|
||||
|
||||
return instances;
|
||||
}
|
||||
|
||||
enum {
|
||||
kWorkspaceEventClass = 1448302419, /* 'VSWS' FourCC */
|
||||
kCurrentSelectedSolutionPathEventID = 1129534288 /* 'CSSP' FourCC */
|
||||
};
|
||||
|
||||
static BOOL TryQueryCurrentSolutionPath(NSRunningApplication* runningApp, NSString** solutionPath)
|
||||
{
|
||||
NSAppleEventDescriptor* targetDescriptor = [NSAppleEventDescriptor
|
||||
descriptorWithProcessIdentifier: runningApp.processIdentifier];
|
||||
|
||||
NSAppleEventDescriptor* appleEvent = [NSAppleEventDescriptor
|
||||
appleEventWithEventClass: kWorkspaceEventClass
|
||||
eventID: kCurrentSelectedSolutionPathEventID
|
||||
targetDescriptor: targetDescriptor
|
||||
returnID: kAutoGenerateReturnID
|
||||
transactionID: kAnyTransactionID];
|
||||
|
||||
AEDesc aeReply = { 0, };
|
||||
|
||||
OSErr sendResult = AESendMessage(
|
||||
[appleEvent aeDesc],
|
||||
&aeReply,
|
||||
kAEWaitReply | kAENeverInteract,
|
||||
kAEDefaultTimeout);
|
||||
|
||||
if (sendResult != noErr) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSAppleEventDescriptor *reply = [[NSAppleEventDescriptor alloc] initWithAEDescNoCopy: &aeReply];
|
||||
*solutionPath = [[reply descriptorForKeyword: keyDirectObject] stringValue];
|
||||
|
||||
return *solutionPath != NULL;
|
||||
}
|
||||
|
||||
static NSRunningApplication* QueryRunningApplicationOpenedOnSolution(NSString* appPath, NSString* solutionPath)
|
||||
{
|
||||
BOOL supportsQueryOpenedSolution = ApplicationSupportsQueryOpenedSolution(appPath);
|
||||
|
||||
for (NSRunningApplication *runningApp in QueryRunningInstances(appPath)) {
|
||||
// If the currently selected external editor does not support the opened solution apple event
|
||||
// then fallback to the previous behavior: take the first opened VSM and open the solution
|
||||
if (!supportsQueryOpenedSolution) {
|
||||
OpenFileAtLineWithAppleEvent(runningApp, solutionPath, -1);
|
||||
return runningApp;
|
||||
}
|
||||
|
||||
NSString* currentSolutionPath;
|
||||
if (TryQueryCurrentSolutionPath(runningApp, ¤tSolutionPath)) {
|
||||
if ([solutionPath isEqual:currentSolutionPath]) {
|
||||
return runningApp;
|
||||
}
|
||||
} else {
|
||||
// If VSM doesn't respond to the query opened solution event
|
||||
// we fallback to the previous behavior too
|
||||
OpenFileAtLineWithAppleEvent(runningApp, solutionPath, -1);
|
||||
return runningApp;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static NSRunningApplication* LaunchApplicationOnSolution(NSString* appPath, NSString* solutionPath)
|
||||
{
|
||||
NSURL* appUrl = [NSURL fileURLWithPath: appPath];
|
||||
NSMutableDictionary* config = [[NSMutableDictionary alloc] init];
|
||||
|
||||
NSRunningApplication* runningApp = [[NSWorkspace sharedWorkspace]
|
||||
launchApplicationAtURL: appUrl
|
||||
options: NSWorkspaceLaunchDefault | NSWorkspaceLaunchNewInstance
|
||||
configuration: config
|
||||
error: nil];
|
||||
|
||||
OpenFileAtLineWithAppleEvent(runningApp, solutionPath, -1);
|
||||
|
||||
return runningApp;
|
||||
}
|
||||
|
||||
static NSRunningApplication* QueryOrLaunchApplication(NSString* appPath, NSString* solutionPath)
|
||||
{
|
||||
NSRunningApplication* runningApp = QueryRunningApplicationOpenedOnSolution(appPath, solutionPath);
|
||||
|
||||
if (!runningApp)
|
||||
runningApp = LaunchApplicationOnSolution(appPath, solutionPath);
|
||||
|
||||
if (runningApp)
|
||||
[runningApp activateWithOptions: 0];
|
||||
|
||||
return runningApp;
|
||||
}
|
||||
|
||||
BOOL LaunchOrReuseApp(NSString* appPath, NSString* solutionPath, NSRunningApplication** outApp)
|
||||
{
|
||||
NSRunningApplication* app = QueryOrLaunchApplication(appPath, solutionPath);
|
||||
|
||||
if (outApp)
|
||||
*outApp = app;
|
||||
|
||||
return app != NULL;
|
||||
}
|
||||
|
||||
BOOL MonoDevelopOpenFile(NSString* appPath, NSString* solutionPath, NSString* filePath, int line)
|
||||
{
|
||||
NSRunningApplication* runningApp;
|
||||
if (!LaunchOrReuseApp(appPath, solutionPath, &runningApp)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (filePath) {
|
||||
return OpenFileAtLineWithAppleEvent(runningApp, filePath, line);
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
#if BUILD_APP
|
||||
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
if (argc != 5) {
|
||||
printf("Usage: AppleEventIntegration appPath solutionPath filePath lineNumber\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* appPath = argv[1];
|
||||
const char* solutionPath = argv[2];
|
||||
const char* filePath = argv[3];
|
||||
const int lineNumber = atoi(argv[4]);
|
||||
|
||||
@autoreleasepool
|
||||
{
|
||||
MonoDevelopOpenFile(MakeNSString(appPath), MakeNSString(solutionPath), MakeNSString(filePath), lineNumber);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
extern "C"
|
||||
{
|
||||
BOOL OpenVisualStudio(const char* appPath, const char* solutionPath, const char* filePath, int line)
|
||||
{
|
||||
return MonoDevelopOpenFile(MakeNSString(appPath), MakeNSString(solutionPath), MakeNSString(filePath), line);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
40
Editor/COMIntegration/COMIntegration~/BStrHolder.h
Normal file
40
Editor/COMIntegration/COMIntegration~/BStrHolder.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include <OleAuto.h>
|
||||
|
||||
struct BStrHolder
|
||||
{
|
||||
BStrHolder() :
|
||||
m_Str(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
BStrHolder(const wchar_t* str) :
|
||||
m_Str(SysAllocString(str))
|
||||
{
|
||||
}
|
||||
|
||||
~BStrHolder()
|
||||
{
|
||||
if (m_Str != NULL)
|
||||
SysFreeString(m_Str);
|
||||
}
|
||||
|
||||
operator BSTR() const
|
||||
{
|
||||
return m_Str;
|
||||
}
|
||||
|
||||
BSTR* operator&()
|
||||
{
|
||||
if (m_Str != NULL)
|
||||
{
|
||||
SysFreeString(m_Str);
|
||||
m_Str = NULL;
|
||||
}
|
||||
|
||||
return &m_Str;
|
||||
}
|
||||
|
||||
private:
|
||||
BSTR m_Str;
|
||||
};
|
||||
26
Editor/COMIntegration/COMIntegration~/BStrHolder.h.meta
Normal file
26
Editor/COMIntegration/COMIntegration~/BStrHolder.h.meta
Normal file
@@ -0,0 +1,26 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f68874d6ae00db4a993b9507d065658
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
13
Editor/COMIntegration/COMIntegration~/CMakeLists.txt
Normal file
13
Editor/COMIntegration/COMIntegration~/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
project(com)
|
||||
set(SOURCES
|
||||
COMIntegration.cpp
|
||||
BStrHolder.h
|
||||
ComPtr.h
|
||||
dte80a.tlh
|
||||
)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -Wall")
|
||||
add_executable(COMIntegration ${SOURCES})
|
||||
target_link_libraries(COMIntegration Shlwapi.lib)
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7cec3e1820a40be4486946c20d7ffd00
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
468
Editor/COMIntegration/COMIntegration~/COMIntegration.cpp
Normal file
468
Editor/COMIntegration/COMIntegration~/COMIntegration.cpp
Normal file
@@ -0,0 +1,468 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Unity Technologies.
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <windows.h>
|
||||
#include <shlwapi.h>
|
||||
|
||||
#include "BStrHolder.h"
|
||||
#include "ComPtr.h"
|
||||
#include "dte80a.tlh"
|
||||
|
||||
constexpr int RETRY_INTERVAL_MS = 150;
|
||||
constexpr int TIMEOUT_MS = 10000;
|
||||
|
||||
// Often a DTE call made to Visual Studio can fail after Visual Studio has just started. Usually the
|
||||
// return value will be RPC_E_CALL_REJECTED, meaning that Visual Studio is probably busy on another
|
||||
// thread. This types filter the RPC messages and retries to send the message until VS accepts it.
|
||||
class CRetryMessageFilter : public IMessageFilter
|
||||
{
|
||||
private:
|
||||
static bool ShouldRetryCall(DWORD dwTickCount, DWORD dwRejectType)
|
||||
{
|
||||
if (dwRejectType == SERVERCALL_RETRYLATER || dwRejectType == SERVERCALL_REJECTED) {
|
||||
return dwTickCount < TIMEOUT_MS;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
win::ComPtr<IMessageFilter> currentFilter;
|
||||
|
||||
public:
|
||||
CRetryMessageFilter()
|
||||
{
|
||||
HRESULT hr = CoRegisterMessageFilter(this, ¤tFilter);
|
||||
_ASSERT(SUCCEEDED(hr));
|
||||
}
|
||||
|
||||
~CRetryMessageFilter()
|
||||
{
|
||||
win::ComPtr<IMessageFilter> messageFilter;
|
||||
HRESULT hr = CoRegisterMessageFilter(currentFilter, &messageFilter);
|
||||
_ASSERT(SUCCEEDED(hr));
|
||||
}
|
||||
|
||||
// IUnknown methods
|
||||
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
|
||||
{
|
||||
static const QITAB qit[] =
|
||||
{
|
||||
QITABENT(CRetryMessageFilter, IMessageFilter),
|
||||
{ 0 },
|
||||
};
|
||||
return QISearch(this, qit, riid, ppv);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(ULONG) AddRef()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(ULONG) Release()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD STDMETHODCALLTYPE HandleInComingCall(DWORD dwCallType, HTASK htaskCaller, DWORD dwTickCount, LPINTERFACEINFO lpInterfaceInfo)
|
||||
{
|
||||
if (currentFilter)
|
||||
return currentFilter->HandleInComingCall(dwCallType, htaskCaller, dwTickCount, lpInterfaceInfo);
|
||||
|
||||
return SERVERCALL_ISHANDLED;
|
||||
}
|
||||
|
||||
DWORD STDMETHODCALLTYPE RetryRejectedCall(HTASK htaskCallee, DWORD dwTickCount, DWORD dwRejectType)
|
||||
{
|
||||
if (ShouldRetryCall(dwTickCount, dwRejectType))
|
||||
return RETRY_INTERVAL_MS;
|
||||
|
||||
if (currentFilter)
|
||||
return currentFilter->RetryRejectedCall(htaskCallee, dwTickCount, dwRejectType);
|
||||
|
||||
return (DWORD)-1;
|
||||
}
|
||||
|
||||
DWORD STDMETHODCALLTYPE MessagePending(HTASK htaskCallee, DWORD dwTickCount, DWORD dwPendingType)
|
||||
{
|
||||
if (currentFilter)
|
||||
return currentFilter->MessagePending(htaskCallee, dwTickCount, dwPendingType);
|
||||
|
||||
return PENDINGMSG_WAITDEFPROCESS;
|
||||
}
|
||||
};
|
||||
|
||||
static void DisplayProgressbar() {
|
||||
std::wcout << "displayProgressBar" << std::endl;
|
||||
}
|
||||
|
||||
static void ClearProgressbar() {
|
||||
std::wcout << "clearprogressbar" << std::endl;
|
||||
}
|
||||
|
||||
inline const std::wstring QuoteString(const std::wstring& str)
|
||||
{
|
||||
return L"\"" + str + L"\"";
|
||||
}
|
||||
|
||||
static std::wstring ErrorCodeToMsg(DWORD code)
|
||||
{
|
||||
LPWSTR msgBuf = nullptr;
|
||||
if (!FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&msgBuf, 0, nullptr))
|
||||
{
|
||||
return L"Unknown error";
|
||||
}
|
||||
else
|
||||
{
|
||||
return msgBuf;
|
||||
}
|
||||
}
|
||||
|
||||
// Get an environment variable
|
||||
static std::wstring GetEnvironmentVariableValue(const std::wstring& variableName) {
|
||||
DWORD currentBufferSize = MAX_PATH;
|
||||
std::wstring variableValue;
|
||||
variableValue.resize(currentBufferSize);
|
||||
|
||||
DWORD requiredBufferSize = GetEnvironmentVariableW(variableName.c_str(), variableValue.data(), currentBufferSize);
|
||||
if (requiredBufferSize == 0) {
|
||||
// Environment variable probably does not exist.
|
||||
return std::wstring();
|
||||
}
|
||||
|
||||
if (currentBufferSize < requiredBufferSize) {
|
||||
variableValue.resize(requiredBufferSize);
|
||||
if (GetEnvironmentVariableW(variableName.c_str(), variableValue.data(), currentBufferSize) == 0)
|
||||
return std::wstring();
|
||||
}
|
||||
|
||||
variableValue.resize(requiredBufferSize);
|
||||
return variableValue;
|
||||
}
|
||||
|
||||
static bool StartVisualStudioProcess(
|
||||
const std::filesystem::path &visualStudioExecutablePath,
|
||||
const std::filesystem::path &solutionPath,
|
||||
DWORD *dwProcessId) {
|
||||
|
||||
STARTUPINFOW si;
|
||||
PROCESS_INFORMATION pi;
|
||||
BOOL result;
|
||||
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
std::wstring startingDirectory = visualStudioExecutablePath.parent_path();
|
||||
|
||||
// Build the command line that is passed as the argv of the VS process
|
||||
// argv[0] must be the quoted full path to the VS exe
|
||||
std::wstringstream commandLineStream;
|
||||
commandLineStream << QuoteString(visualStudioExecutablePath) << L" ";
|
||||
|
||||
std::wstring vsArgsWide = GetEnvironmentVariableValue(L"UNITY_VS_ARGS");
|
||||
if (!vsArgsWide.empty())
|
||||
commandLineStream << vsArgsWide << L" ";
|
||||
|
||||
commandLineStream << QuoteString(solutionPath);
|
||||
|
||||
std::wstring commandLine = commandLineStream.str();
|
||||
|
||||
std::wcout << "Starting Visual Studio process with: " << commandLine << std::endl;
|
||||
|
||||
result = CreateProcessW(
|
||||
visualStudioExecutablePath.c_str(), // Full path to VS, must not be quoted
|
||||
commandLine.data(), // Command line, as passed as argv, separate arguments must be quoted if they contain spaces
|
||||
nullptr, // Process handle not inheritable
|
||||
nullptr, // Thread handle not inheritable
|
||||
false, // Set handle inheritance to FALSE
|
||||
0, // No creation flags
|
||||
nullptr, // Use parent's environment block
|
||||
startingDirectory.c_str(), // starting directory set to the VS directory
|
||||
&si,
|
||||
&pi);
|
||||
|
||||
if (!result) {
|
||||
DWORD error = GetLastError();
|
||||
std::wcout << "Starting Visual Studio process failed: " << ErrorCodeToMsg(error) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
*dwProcessId = pi.dwProcessId;
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static win::ComPtr<EnvDTE::_DTE> FindRunningVisualStudioWithSolution(
|
||||
const std::filesystem::path &visualStudioExecutablePath,
|
||||
const std::filesystem::path &solutionPath)
|
||||
{
|
||||
win::ComPtr<IUnknown> punk = nullptr;
|
||||
win::ComPtr<EnvDTE::_DTE> dte = nullptr;
|
||||
|
||||
CRetryMessageFilter retryMessageFilter;
|
||||
|
||||
// Search through the Running Object Table for an instance of Visual Studio
|
||||
// to use that either has the correct solution already open or does not have
|
||||
// any solution open.
|
||||
win::ComPtr<IRunningObjectTable> ROT;
|
||||
if (FAILED(GetRunningObjectTable(0, &ROT)))
|
||||
return nullptr;
|
||||
|
||||
win::ComPtr<IBindCtx> bindCtx;
|
||||
if (FAILED(CreateBindCtx(0, &bindCtx)))
|
||||
return nullptr;
|
||||
|
||||
win::ComPtr<IEnumMoniker> enumMoniker;
|
||||
if (FAILED(ROT->EnumRunning(&enumMoniker)))
|
||||
return nullptr;
|
||||
|
||||
win::ComPtr<IMoniker> moniker;
|
||||
ULONG monikersFetched = 0;
|
||||
while (SUCCEEDED(enumMoniker->Next(1, &moniker, &monikersFetched)) && monikersFetched) {
|
||||
if (FAILED(ROT->GetObject(moniker, &punk)))
|
||||
continue;
|
||||
|
||||
punk.As(&dte);
|
||||
if (!dte)
|
||||
continue;
|
||||
|
||||
// Okay, so we found an actual running instance of Visual Studio.
|
||||
|
||||
// Get the executable path of this running instance.
|
||||
BStrHolder visualStudioFullName;
|
||||
if (FAILED(dte->get_FullName(&visualStudioFullName)))
|
||||
continue;
|
||||
|
||||
std::filesystem::path currentVisualStudioExecutablePath = std::wstring(visualStudioFullName);
|
||||
|
||||
// Ask for its current solution.
|
||||
win::ComPtr<EnvDTE::_Solution> solution;
|
||||
if (FAILED(dte->get_Solution(&solution)))
|
||||
continue;
|
||||
|
||||
// Get the name of that solution.
|
||||
BStrHolder solutionFullName;
|
||||
if (FAILED(solution->get_FullName(&solutionFullName)))
|
||||
continue;
|
||||
|
||||
std::filesystem::path currentSolutionPath = std::wstring(solutionFullName);
|
||||
if (!currentSolutionPath.empty())
|
||||
std::wcout << "Visual Studio opened on " << currentSolutionPath.wstring() << std::endl;
|
||||
|
||||
// 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.
|
||||
// If we don't have a Visual Studio installation path to use, just use this solution.
|
||||
if (std::filesystem::equivalent(currentSolutionPath, solutionPath)) {
|
||||
std::wcout << "We found a running Visual Studio session with the solution open." << std::endl;
|
||||
if (!visualStudioExecutablePath.empty()) {
|
||||
if (std::filesystem::equivalent(currentVisualStudioExecutablePath, visualStudioExecutablePath)) {
|
||||
return dte;
|
||||
}
|
||||
else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
else {
|
||||
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;
|
||||
return dte;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool
|
||||
MonikerIsVisualStudioProcess(const win::ComPtr<IMoniker> &moniker, const win::ComPtr<IBindCtx> &bindCtx, const DWORD dwProcessId) {
|
||||
LPOLESTR oleMonikerName;
|
||||
if (FAILED(moniker->GetDisplayName(bindCtx, nullptr, &oleMonikerName)))
|
||||
return false;
|
||||
|
||||
std::wstring monikerName(oleMonikerName);
|
||||
|
||||
// VisualStudio Moniker is "!VisualStudio.DTE.$Version:$PID"
|
||||
// Example "!VisualStudio.DTE.14.0:1234"
|
||||
|
||||
if (monikerName.find(L"!VisualStudio.DTE") != 0)
|
||||
return false;
|
||||
|
||||
std::wstringstream suffixStream;
|
||||
suffixStream << ":";
|
||||
suffixStream << dwProcessId;
|
||||
|
||||
std::wstring suffix(suffixStream.str());
|
||||
|
||||
return monikerName.length() - suffix.length() == monikerName.find(suffix);
|
||||
}
|
||||
|
||||
static win::ComPtr<EnvDTE::_DTE> FindRunningVisualStudioWithPID(const DWORD dwProcessId) {
|
||||
win::ComPtr<IUnknown> punk = nullptr;
|
||||
win::ComPtr<EnvDTE::_DTE> dte = nullptr;
|
||||
|
||||
// Search through the Running Object Table for a Visual Studio
|
||||
// process with the process ID specified
|
||||
win::ComPtr<IRunningObjectTable> ROT;
|
||||
if (FAILED(GetRunningObjectTable(0, &ROT)))
|
||||
return nullptr;
|
||||
|
||||
win::ComPtr<IBindCtx> bindCtx;
|
||||
if (FAILED(CreateBindCtx(0, &bindCtx)))
|
||||
return nullptr;
|
||||
|
||||
win::ComPtr<IEnumMoniker> enumMoniker;
|
||||
if (FAILED(ROT->EnumRunning(&enumMoniker)))
|
||||
return nullptr;
|
||||
|
||||
win::ComPtr<IMoniker> moniker;
|
||||
ULONG monikersFetched = 0;
|
||||
while (SUCCEEDED(enumMoniker->Next(1, &moniker, &monikersFetched)) && monikersFetched) {
|
||||
if (FAILED(ROT->GetObject(moniker, &punk)))
|
||||
continue;
|
||||
|
||||
if (!MonikerIsVisualStudioProcess(moniker, bindCtx, dwProcessId))
|
||||
continue;
|
||||
|
||||
punk.As(&dte);
|
||||
if (dte)
|
||||
return dte;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool HaveRunningVisualStudioOpenFile(const win::ComPtr<EnvDTE::_DTE> &dte, const std::filesystem::path &filename, int line) {
|
||||
BStrHolder bstrFileName(filename.c_str());
|
||||
BStrHolder bstrKind(L"{00000000-0000-0000-0000-000000000000}"); // EnvDTE::vsViewKindPrimary
|
||||
win::ComPtr<EnvDTE::Window> window = nullptr;
|
||||
|
||||
CRetryMessageFilter retryMessageFilter;
|
||||
|
||||
if (!filename.empty()) {
|
||||
std::wcout << "Getting operations API from the Visual Studio session." << std::endl;
|
||||
|
||||
win::ComPtr<EnvDTE::ItemOperations> item_ops;
|
||||
if (FAILED(dte->get_ItemOperations(&item_ops)))
|
||||
return false;
|
||||
|
||||
std::wcout << "Waiting for the Visual Studio session to open the file: " << filename.wstring() << "." << std::endl;
|
||||
|
||||
if (FAILED(item_ops->OpenFile(bstrFileName, bstrKind, &window)))
|
||||
return false;
|
||||
|
||||
if (line > 0) {
|
||||
win::ComPtr<IDispatch> selection_dispatch;
|
||||
if (window && SUCCEEDED(window->get_Selection(&selection_dispatch))) {
|
||||
win::ComPtr<EnvDTE::TextSelection> selection;
|
||||
if (selection_dispatch &&
|
||||
SUCCEEDED(selection_dispatch->QueryInterface(__uuidof(EnvDTE::TextSelection), &selection)) &&
|
||||
selection) {
|
||||
selection->GotoLine(line, false);
|
||||
selection->EndOfLine(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window = nullptr;
|
||||
if (SUCCEEDED(dte->get_MainWindow(&window))) {
|
||||
// Allow the DTE to make its main window the foreground
|
||||
HWND hWnd;
|
||||
window->get_HWnd((LONG *)&hWnd);
|
||||
|
||||
DWORD processID;
|
||||
if (SUCCEEDED(GetWindowThreadProcessId(hWnd, &processID)))
|
||||
AllowSetForegroundWindow(processID);
|
||||
|
||||
// Activate() set the window to visible and active (blinks in taskbar)
|
||||
window->Activate();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool VisualStudioOpenFile(
|
||||
const std::filesystem::path &visualStudioExecutablePath,
|
||||
const std::filesystem::path &solutionPath,
|
||||
const std::filesystem::path &filename,
|
||||
int line)
|
||||
{
|
||||
win::ComPtr<EnvDTE::_DTE> dte = nullptr;
|
||||
|
||||
std::wcout << "Looking for a running Visual Studio session." << std::endl;
|
||||
|
||||
// TODO: If path does not exist pass empty, which will just try to match all windows with solution
|
||||
dte = FindRunningVisualStudioWithSolution(visualStudioExecutablePath, solutionPath);
|
||||
|
||||
if (!dte) {
|
||||
std::wcout << "No appropriate running Visual Studio session not found, creating a new one." << std::endl;
|
||||
|
||||
DisplayProgressbar();
|
||||
|
||||
DWORD dwProcessId;
|
||||
if (!StartVisualStudioProcess(visualStudioExecutablePath, solutionPath, &dwProcessId)) {
|
||||
ClearProgressbar();
|
||||
return false;
|
||||
}
|
||||
|
||||
int timeWaited = 0;
|
||||
|
||||
while (timeWaited < TIMEOUT_MS) {
|
||||
dte = FindRunningVisualStudioWithPID(dwProcessId);
|
||||
|
||||
if (dte)
|
||||
break;
|
||||
|
||||
std::wcout << "Retrying to acquire DTE" << std::endl;
|
||||
|
||||
Sleep(RETRY_INTERVAL_MS);
|
||||
timeWaited += RETRY_INTERVAL_MS;
|
||||
}
|
||||
|
||||
ClearProgressbar();
|
||||
|
||||
if (!dte)
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
std::wcout << "Using the existing Visual Studio session." << std::endl;
|
||||
}
|
||||
|
||||
return HaveRunningVisualStudioOpenFile(dte, filename, line);
|
||||
}
|
||||
|
||||
int wmain(int argc, wchar_t* argv[]) {
|
||||
if (argc != 3 && argc != 5) {
|
||||
std::wcerr << argc << ": wrong number of arguments\n" << "Usage: com.exe installationPath solutionPath [fileName lineNumber]" << std::endl;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
std::wcerr << argv[i] << std::endl;
|
||||
}
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (FAILED(CoInitialize(nullptr))) {
|
||||
std::wcerr << "CoInitialize failed." << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::filesystem::path visualStudioExecutablePath = std::filesystem::absolute(argv[1]);
|
||||
std::filesystem::path solutionPath = std::filesystem::absolute(argv[2]);
|
||||
|
||||
if (argc == 3) {
|
||||
VisualStudioOpenFile(visualStudioExecutablePath, solutionPath, L"", -1);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
std::filesystem::path fileName = std::filesystem::absolute(argv[3]);
|
||||
int lineNumber = std::stoi(argv[4]);
|
||||
|
||||
VisualStudioOpenFile(visualStudioExecutablePath, solutionPath, fileName, lineNumber);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ffa4010724f8d54aacbed867d4a5aa6
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
186
Editor/COMIntegration/COMIntegration~/ComPtr.h
Normal file
186
Editor/COMIntegration/COMIntegration~/ComPtr.h
Normal file
@@ -0,0 +1,186 @@
|
||||
#pragma once
|
||||
|
||||
namespace win
|
||||
{
|
||||
template<typename T>
|
||||
class ComPtr;
|
||||
|
||||
template<typename T>
|
||||
class ComPtrRef
|
||||
{
|
||||
private:
|
||||
ComPtr<T>& m_ComPtr;
|
||||
|
||||
ComPtrRef(ComPtr<T>& comPtr) :
|
||||
m_ComPtr(comPtr)
|
||||
{
|
||||
}
|
||||
|
||||
friend class ComPtr<T>;
|
||||
|
||||
public:
|
||||
inline operator T**()
|
||||
{
|
||||
return m_ComPtr.ReleaseAndGetAddressOf();
|
||||
}
|
||||
|
||||
inline operator void**()
|
||||
{
|
||||
return reinterpret_cast<void**>(m_ComPtr.ReleaseAndGetAddressOf());
|
||||
}
|
||||
|
||||
inline T* operator*() throw ()
|
||||
{
|
||||
return m_ComPtr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class ComPtr
|
||||
{
|
||||
private:
|
||||
T *ptr;
|
||||
|
||||
public:
|
||||
inline ComPtr(void) : ptr(NULL) {}
|
||||
inline ~ComPtr(void) { this->Free(); }
|
||||
|
||||
ComPtr(T *ptr)
|
||||
{
|
||||
if (NULL != (this->ptr = ptr))
|
||||
{
|
||||
this->ptr->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
ComPtr(const ComPtr &ptr)
|
||||
{
|
||||
if (NULL != (this->ptr = ptr.ptr))
|
||||
{
|
||||
this->ptr->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
inline bool operator!() const
|
||||
{
|
||||
return (NULL == this->ptr);
|
||||
}
|
||||
|
||||
inline operator T*() const { return this->ptr; }
|
||||
|
||||
inline T *operator->() const
|
||||
{
|
||||
//_assert(NULL != this->ptr);
|
||||
return this->ptr;
|
||||
}
|
||||
|
||||
inline T &operator*()
|
||||
{
|
||||
//_assert(NULL != this->ptr);
|
||||
return *this->ptr;
|
||||
}
|
||||
|
||||
inline ComPtrRef<T> operator&()
|
||||
{
|
||||
return ComPtrRef<T>(*this);
|
||||
}
|
||||
|
||||
const ComPtr &operator=(T *ptr)
|
||||
{
|
||||
if (this->ptr != ptr)
|
||||
{
|
||||
this->Free();
|
||||
|
||||
if (NULL != (this->ptr = ptr))
|
||||
{
|
||||
this->ptr->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
const ComPtr &operator=(const ComPtr &ptr)
|
||||
{
|
||||
if (this->ptr != ptr.ptr)
|
||||
{
|
||||
this->Free();
|
||||
|
||||
if (NULL != (this->ptr = ptr.ptr))
|
||||
{
|
||||
this->ptr->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Free(void)
|
||||
{
|
||||
if (NULL != this->ptr)
|
||||
{
|
||||
this->ptr->Release();
|
||||
this->ptr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
inline T** ReleaseAndGetAddressOf()
|
||||
{
|
||||
Free();
|
||||
return &ptr;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
inline HRESULT As(ComPtrRef<U> p) const throw ()
|
||||
{
|
||||
return ptr->QueryInterface(__uuidof(U), p);
|
||||
}
|
||||
|
||||
inline bool operator==(std::nullptr_t) const
|
||||
{
|
||||
return this->ptr == nullptr;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
inline bool operator==(U* other)
|
||||
{
|
||||
if (ptr == nullptr || other == nullptr)
|
||||
return ptr == other;
|
||||
|
||||
ComPtr<IUnknown> meUnknown;
|
||||
ComPtr<IUnknown> otherUnknown;
|
||||
|
||||
if (FAILED(this->ptr->QueryInterface(__uuidof(IUnknown), &meUnknown)))
|
||||
return false;
|
||||
|
||||
if (FAILED(other->QueryInterface(__uuidof(IUnknown), &otherUnknown)))
|
||||
return false;
|
||||
|
||||
return static_cast<IUnknown*>(meUnknown) == static_cast<IUnknown*>(otherUnknown);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
inline bool operator==(ComPtr<U>& other)
|
||||
{
|
||||
return *this == static_cast<U*>(other);
|
||||
}
|
||||
|
||||
inline bool operator!=(std::nullptr_t) const
|
||||
{
|
||||
return this->ptr != nullptr;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
inline bool operator!=(U* other)
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
inline bool operator!=(ComPtr<U>& other)
|
||||
{
|
||||
return *this != static_cast<U*>(other);
|
||||
}
|
||||
};
|
||||
}
|
||||
26
Editor/COMIntegration/COMIntegration~/ComPtr.h.meta
Normal file
26
Editor/COMIntegration/COMIntegration~/ComPtr.h.meta
Normal file
@@ -0,0 +1,26 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 013868b12dff0dc43adcc33513ae71bf
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
7538
Editor/COMIntegration/COMIntegration~/dte80a.tlh
Normal file
7538
Editor/COMIntegration/COMIntegration~/dte80a.tlh
Normal file
File diff suppressed because it is too large
Load Diff
7
Editor/COMIntegration/COMIntegration~/dte80a.tlh.meta
Normal file
7
Editor/COMIntegration/COMIntegration~/dte80a.tlh.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97115cd910ade104a9d05d65a6b6b7d9
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1
Editor/COMIntegration/COMIntegration~/release-build.txt
Normal file
1
Editor/COMIntegration/COMIntegration~/release-build.txt
Normal file
@@ -0,0 +1 @@
|
||||
cl /EHsc /std:c++17 COMIntegration.cpp /link Shlwapi.lib /out:"..\Release\COMIntegration.exe"
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b44687349be79f4184ba013fb6ffa0c
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -30,6 +30,8 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
||||
|
||||
public class AssemblyNameProvider : IAssemblyNameProvider
|
||||
{
|
||||
private readonly Dictionary<string, UnityEditor.PackageManager.PackageInfo> m_PackageInfoCache = new Dictionary<string, UnityEditor.PackageManager.PackageInfo>();
|
||||
|
||||
ProjectGenerationFlag m_ProjectGenerationFlag = (ProjectGenerationFlag)EditorPrefs.GetInt(
|
||||
"unity_project_generation_flag",
|
||||
(int)(ProjectGenerationFlag.Local | ProjectGenerationFlag.Embedded));
|
||||
@@ -55,55 +57,35 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
||||
|
||||
public IEnumerable<Assembly> GetAssemblies(Func<string, bool> shouldFileBePartOfSolution)
|
||||
{
|
||||
foreach (var assembly in CompilationPipeline.GetAssemblies())
|
||||
IEnumerable<Assembly> assemblies = GetAssembliesByType(AssembliesType.Editor, shouldFileBePartOfSolution, @"Temp\Bin\Debug\");
|
||||
|
||||
if (!ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.PlayerAssemblies))
|
||||
{
|
||||
return assemblies;
|
||||
}
|
||||
var playerAssemblies = GetAssembliesByType(AssembliesType.Player, shouldFileBePartOfSolution, @"Temp\Bin\Debug\Player\");
|
||||
return assemblies.Concat(playerAssemblies);
|
||||
}
|
||||
|
||||
private static IEnumerable<Assembly> GetAssembliesByType(AssembliesType type, Func<string, bool> shouldFileBePartOfSolution, string outputPath)
|
||||
{
|
||||
foreach (var assembly in CompilationPipeline.GetAssemblies(type))
|
||||
{
|
||||
if (assembly.sourceFiles.Any(shouldFileBePartOfSolution))
|
||||
{
|
||||
var options = new ScriptCompilerOptions
|
||||
{
|
||||
ResponseFiles = assembly.compilerOptions.ResponseFiles,
|
||||
AllowUnsafeCode = assembly.compilerOptions.AllowUnsafeCode,
|
||||
ApiCompatibilityLevel = assembly.compilerOptions.ApiCompatibilityLevel
|
||||
};
|
||||
|
||||
yield return new Assembly(assembly.name, @"Temp\Bin\Debug\",
|
||||
assembly.sourceFiles, new[] { "DEBUG", "TRACE" }.Concat(assembly.defines).Concat(EditorUserBuildSettings.activeScriptCompilationDefines).ToArray(),
|
||||
yield return new Assembly(
|
||||
assembly.name,
|
||||
outputPath,
|
||||
assembly.sourceFiles,
|
||||
assembly.defines,
|
||||
assembly.assemblyReferences,
|
||||
assembly.compiledAssemblyReferences,
|
||||
assembly.flags,
|
||||
assembly.compilerOptions
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
options,
|
||||
assembly.rootNamespace);
|
||||
#else
|
||||
options);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.PlayerAssemblies))
|
||||
{
|
||||
foreach (var assembly in CompilationPipeline.GetAssemblies(AssembliesType.Player).Where(assembly => assembly.sourceFiles.Any(shouldFileBePartOfSolution)))
|
||||
{
|
||||
var options = new ScriptCompilerOptions
|
||||
{
|
||||
ResponseFiles = assembly.compilerOptions.ResponseFiles,
|
||||
AllowUnsafeCode = assembly.compilerOptions.AllowUnsafeCode,
|
||||
ApiCompatibilityLevel = assembly.compilerOptions.ApiCompatibilityLevel
|
||||
};
|
||||
|
||||
yield return
|
||||
new Assembly(assembly.name, @"Temp\Bin\Debug\Player\",
|
||||
assembly.sourceFiles,
|
||||
new[] { "DEBUG", "TRACE" }.Concat(assembly.defines).ToArray(),
|
||||
assembly.assemblyReferences,
|
||||
assembly.compiledAssemblyReferences,
|
||||
assembly.flags,
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
options,
|
||||
assembly.rootNamespace);
|
||||
#else
|
||||
options);
|
||||
, assembly.rootNamespace
|
||||
#endif
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,9 +100,39 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
||||
return AssetDatabase.GetAllAssetPaths();
|
||||
}
|
||||
|
||||
private static string ResolvePotentialParentPackageAssetPath(string assetPath)
|
||||
{
|
||||
const string packagesPrefix = "packages/";
|
||||
if (!assetPath.StartsWith(packagesPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var followupSeparator = assetPath.IndexOf('/', packagesPrefix.Length);
|
||||
if (followupSeparator == -1)
|
||||
{
|
||||
return assetPath.ToLowerInvariant();
|
||||
}
|
||||
|
||||
return assetPath.Substring(0, followupSeparator).ToLowerInvariant();
|
||||
}
|
||||
|
||||
public UnityEditor.PackageManager.PackageInfo FindForAssetPath(string assetPath)
|
||||
{
|
||||
return UnityEditor.PackageManager.PackageInfo.FindForAssetPath(assetPath);
|
||||
var parentPackageAssetPath = ResolvePotentialParentPackageAssetPath(assetPath);
|
||||
if (parentPackageAssetPath == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (m_PackageInfoCache.TryGetValue(parentPackageAssetPath, out var cachedPackageInfo))
|
||||
{
|
||||
return cachedPackageInfo;
|
||||
}
|
||||
|
||||
var result = UnityEditor.PackageManager.PackageInfo.FindForAssetPath(parentPackageAssetPath);
|
||||
m_PackageInfoCache[parentPackageAssetPath] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool IsInternalizedPackagePath(string path)
|
||||
@@ -177,6 +189,11 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
||||
}
|
||||
}
|
||||
|
||||
internal void ResetPackageInfoCache()
|
||||
{
|
||||
m_PackageInfoCache.Clear();
|
||||
}
|
||||
|
||||
public void ResetProjectGenerationFlag()
|
||||
{
|
||||
ProjectGenerationFlag = ProjectGenerationFlag.None;
|
||||
|
||||
@@ -104,10 +104,6 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
SetupProjectSupportedExtensions();
|
||||
|
||||
// See https://devblogs.microsoft.com/setup/configure-visual-studio-across-your-organization-with-vsconfig/
|
||||
// We create a .vsconfig file to make sure our ManagedGame workload is installed
|
||||
CreateVsConfigIfNotFound();
|
||||
|
||||
// Don't sync if we haven't synced before
|
||||
if (HasSolutionBeenGenerated() && HasFilesBeenModified(affectedFiles, reimportedFiles))
|
||||
{
|
||||
@@ -161,6 +157,13 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
||||
RefreshCurrentInstallation();
|
||||
|
||||
SetupProjectSupportedExtensions();
|
||||
|
||||
(m_AssemblyNameProvider as AssemblyNameProvider)?.ResetPackageInfoCache();
|
||||
|
||||
// See https://devblogs.microsoft.com/setup/configure-visual-studio-across-your-organization-with-vsconfig/
|
||||
// We create a .vsconfig file to make sure our ManagedGame workload is installed
|
||||
CreateVsConfigIfNotFound();
|
||||
|
||||
var externalCodeAlreadyGeneratedProjects = OnPreGeneratingCSProjectFiles();
|
||||
|
||||
if (!externalCodeAlreadyGeneratedProjects)
|
||||
@@ -322,7 +325,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
||||
stringBuilders[assemblyName] = projectBuilder;
|
||||
}
|
||||
|
||||
projectBuilder.Append(" <None Include=\"").Append(EscapedRelativePathFor(asset)).Append("\" />").Append(k_WindowsNewline);
|
||||
projectBuilder.Append(" <None Include=\"").Append(EscapedRelativePathFor(asset)).Append("\" />").Append(k_WindowsNewline);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,6 +370,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
||||
{
|
||||
return TypeCache
|
||||
.GetTypesDerivedFrom<AssetPostprocessor>()
|
||||
.Where(t => t.Assembly.GetName().Name != "SyntaxTree.VisualStudio.Unity.Bridge") // never call into the bridge if loaded with the package
|
||||
.Select(t => t.GetMethod(name, SR.BindingFlags.Public | SR.BindingFlags.NonPublic | SR.BindingFlags.Static))
|
||||
.Where(m => m != null);
|
||||
}
|
||||
@@ -466,12 +470,19 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
||||
}
|
||||
projectBuilder.Append(@" </ItemGroup>").Append(k_WindowsNewline);
|
||||
|
||||
projectBuilder.Append(@" <ItemGroup>").Append(k_WindowsNewline);
|
||||
|
||||
// Append additional non-script files that should be included in project generation.
|
||||
if (allAssetsProjectParts.TryGetValue(assembly.name, out var additionalAssetsForProject))
|
||||
{
|
||||
projectBuilder.Append(@" <ItemGroup>").Append(k_WindowsNewline);
|
||||
|
||||
projectBuilder.Append(additionalAssetsForProject);
|
||||
|
||||
projectBuilder.Append(@" </ItemGroup>").Append(k_WindowsNewline);
|
||||
|
||||
}
|
||||
|
||||
projectBuilder.Append(@" <ItemGroup>").Append(k_WindowsNewline);
|
||||
|
||||
var responseRefs = responseFilesData.SelectMany(x => x.FullPathReferences.Select(r => r));
|
||||
var internalAssemblyReferences = assembly.assemblyReferences
|
||||
.Where(i => !i.sourceFiles.Any(ShouldFileBePartOfSolution)).Select(i => i.outputPath);
|
||||
|
||||
@@ -308,7 +308,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = progpath,
|
||||
Arguments = $"\"{CodeEditor.CurrentEditorInstallation}\" \"{absolutePath}\" {solution} {line}",
|
||||
Arguments = $"\"{CodeEditor.CurrentEditorInstallation}\" {solution} \"{absolutePath}\" {line}",
|
||||
CreateNoWindow = true,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.Win32;
|
||||
using Unity.CodeEditor;
|
||||
using IOPath = System.IO.Path;
|
||||
@@ -78,7 +77,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
||||
|
||||
if (versions != null)
|
||||
{
|
||||
foreach(var entry in versions)
|
||||
foreach (var entry in versions)
|
||||
{
|
||||
if (Version >= entry.IdeVersion)
|
||||
return entry.LanguageVersion;
|
||||
@@ -86,7 +85,7 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
||||
}
|
||||
|
||||
// default to 7.0 given we support at least VS 2017
|
||||
return new Version(7,0);
|
||||
return new Version(7, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,31 +104,37 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
||||
}
|
||||
}
|
||||
|
||||
private string GetWindowsBridgeFromRegistry()
|
||||
{
|
||||
var keyName = $"Software\\Microsoft\\Microsoft Visual Studio {Version.Major}.0 Tools for Unity";
|
||||
const string valueName = "UnityExtensionPath";
|
||||
|
||||
var bridge = ReadRegistry(Registry.CurrentUser, keyName, valueName);
|
||||
if (string.IsNullOrEmpty(bridge))
|
||||
bridge = ReadRegistry(Registry.LocalMachine, keyName, valueName);
|
||||
|
||||
return bridge;
|
||||
}
|
||||
|
||||
// We only use this to find analyzers, we do not need to load this assembly anymore
|
||||
private string GetBridgeLocation()
|
||||
private string GetExtensionPath()
|
||||
{
|
||||
if (VisualStudioEditor.IsWindows)
|
||||
{
|
||||
// Registry, using legacy bridge location
|
||||
var keyName = $"Software\\Microsoft\\Microsoft Visual Studio {Version.Major}.0 Tools for Unity";
|
||||
const string valueName = "UnityExtensionPath";
|
||||
const string extensionName = "Visual Studio Tools for Unity";
|
||||
const string extensionAssembly = "SyntaxTree.VisualStudio.Unity.dll";
|
||||
|
||||
var bridge = ReadRegistry(Registry.CurrentUser, keyName, valueName);
|
||||
if (string.IsNullOrEmpty(bridge))
|
||||
bridge = ReadRegistry(Registry.LocalMachine, keyName, valueName);
|
||||
var vsDirectory = IOPath.GetDirectoryName(Path);
|
||||
var vstuDirectory = IOPath.Combine(vsDirectory, "Extensions", "Microsoft", extensionName);
|
||||
|
||||
return bridge;
|
||||
if (File.Exists(IOPath.Combine(vstuDirectory, extensionAssembly)))
|
||||
return vstuDirectory;
|
||||
}
|
||||
|
||||
if (VisualStudioEditor.IsOSX)
|
||||
{
|
||||
// Environment, useful when developing UnityVS for Mac
|
||||
var bridge = Environment.GetEnvironmentVariable("VSTUM_BRIDGE");
|
||||
if (!string.IsNullOrEmpty(bridge) && File.Exists(bridge))
|
||||
return bridge;
|
||||
|
||||
const string addinBridge = "Editor/SyntaxTree.VisualStudio.Unity.Bridge.dll";
|
||||
const string addinName = "MonoDevelop.Unity";
|
||||
const string addinAssembly = addinName + ".dll";
|
||||
|
||||
// user addins repository
|
||||
var localAddins = IOPath.Combine(
|
||||
@@ -138,38 +143,54 @@ namespace Microsoft.Unity.VisualStudio.Editor
|
||||
|
||||
// In the user addins repository, the addins are suffixed by their versions, like `MonoDevelop.Unity.1.0`
|
||||
// When installing another local user addin, MD will remove files inside the folder
|
||||
// So we browse all VSTUM addins, and return the one with a bridge, which is the one MD will load
|
||||
// So we browse all VSTUM addins, and return the one with an addin assembly
|
||||
if (Directory.Exists(localAddins))
|
||||
{
|
||||
foreach (var folder in Directory.GetDirectories(localAddins, addinName + "*", SearchOption.TopDirectoryOnly))
|
||||
{
|
||||
bridge = IOPath.Combine(folder, addinBridge);
|
||||
if (File.Exists(bridge))
|
||||
return bridge;
|
||||
if (File.Exists(IOPath.Combine(folder, addinAssembly)))
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
|
||||
// Check in Visual Studio.app/
|
||||
// In that case the name of the addin is used
|
||||
bridge = IOPath.Combine(Path, $"Contents/Resources/lib/monodevelop/AddIns/{addinName}/{addinBridge}");
|
||||
if (File.Exists(bridge))
|
||||
return bridge;
|
||||
var addinPath = IOPath.Combine(Path, $"Contents/Resources/lib/monodevelop/AddIns/{addinName}");
|
||||
if (File.Exists(IOPath.Combine(addinPath, addinAssembly)))
|
||||
return addinPath;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string[] GetAnalyzers(string path)
|
||||
{
|
||||
var analyzersDirectory = IOPath.GetFullPath(IOPath.Combine(path, "Analyzers"));
|
||||
|
||||
if (Directory.Exists(analyzersDirectory))
|
||||
return Directory.GetFiles(analyzersDirectory, "*Analyzers.dll", SearchOption.AllDirectories);
|
||||
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
public string[] GetAnalyzers()
|
||||
{
|
||||
var bridge = GetBridgeLocation();
|
||||
var vstuPath = GetExtensionPath();
|
||||
if (string.IsNullOrEmpty(vstuPath))
|
||||
return Array.Empty<string>();
|
||||
|
||||
if (!string.IsNullOrEmpty(bridge))
|
||||
if (VisualStudioEditor.IsOSX)
|
||||
return GetAnalyzers(vstuPath);
|
||||
|
||||
if (VisualStudioEditor.IsWindows)
|
||||
{
|
||||
var baseLocation = IOPath.Combine(IOPath.GetDirectoryName(bridge), "..");
|
||||
var analyzerLocation = IOPath.GetFullPath(IOPath.Combine(baseLocation, "Analyzers"));
|
||||
var analyzers = GetAnalyzers(vstuPath);
|
||||
if (analyzers?.Length > 0)
|
||||
return analyzers;
|
||||
|
||||
if (Directory.Exists(analyzerLocation))
|
||||
return Directory.GetFiles(analyzerLocation, "*Analyzers.dll", SearchOption.AllDirectories);
|
||||
var bridge = GetWindowsBridgeFromRegistry();
|
||||
if (File.Exists(bridge))
|
||||
return GetAnalyzers(IOPath.Combine(IOPath.GetDirectoryName(bridge), ".."));
|
||||
}
|
||||
|
||||
// Local assets
|
||||
|
||||
12
package.json
12
package.json
@@ -2,21 +2,21 @@
|
||||
"name": "com.unity.ide.visualstudio",
|
||||
"displayName": "Visual Studio Editor",
|
||||
"description": "Code editor integration for supporting Visual Studio as code editor for unity. Adds support for generating csproj files for intellisense purposes, auto discovery of installations, etc.",
|
||||
"version": "2.0.7",
|
||||
"unity": "2020.1",
|
||||
"unityRelease": "0a12",
|
||||
"version": "2.0.8",
|
||||
"unity": "2019.4",
|
||||
"unityRelease": "21f1",
|
||||
"dependencies": {
|
||||
"com.unity.test-framework": "1.1.9"
|
||||
},
|
||||
"relatedPackages": {
|
||||
"com.unity.ide.visualstudio.tests": "2.0.7"
|
||||
"com.unity.ide.visualstudio.tests": "2.0.8"
|
||||
},
|
||||
"upmCi": {
|
||||
"footprint": "b6515ac9d75224fe45e288270d26a9e031c550a8"
|
||||
"footprint": "c51cc3db630d4f769464e8f12c2154af3ca5242c"
|
||||
},
|
||||
"repository": {
|
||||
"url": "https://github.cds.internal.unity3d.com/unity/com.unity.ide.visualstudio.git",
|
||||
"type": "git",
|
||||
"revision": "dec282022c7a95fada560c36f53da9dd155a142c"
|
||||
"revision": "5cdf5d932e6ecbbd83bbe6a40eec2a94b4979501"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user