diff --git a/.github/workflows/ci-macvim.yaml b/.github/workflows/ci-macvim.yaml index 9e6e3c7130..085ff3f636 100644 --- a/.github/workflows/ci-macvim.yaml +++ b/.github/workflows/ci-macvim.yaml @@ -87,6 +87,11 @@ jobs: # Later, we pass the --enable-sparkle_1 flag to configure to set the corresponding ifdef. ln -fhs Sparkle_1.framework src/MacVim/Sparkle.framework + # Sparkle shows a dialog asking if the user wants to check for updates on 2nd launch of + # MacVim. On Sparkle 1 this is annoyingly a modal dialog box and interferes with tests. + # Just disable it by pre-setting to not check for updates. + defaults write org.vim.MacVim SUEnableAutomaticChecks 0 + - name: Set up Xcode if: matrix.xcode != '' run: | @@ -310,7 +315,12 @@ jobs: echo 'MacVim_xcode8.xcodeproj is outdated. Run "make -C src macvim-xcodeproj-compat" to re-generate it.'; false fi - - name: Build test binaries + - name: Test MacVim + timeout-minutes: 10 + run: | + make ${MAKE_BUILD_ARGS} -C src macvim-tests + + - name: Build Vim test binaries run: | # Build the unit test binaries first. With link-time-optimization they take some time to link. Running them # separately de-couples them from the timeout in tests, and allow us to build in parallel jobs (since tests @@ -320,11 +330,11 @@ jobs: set -o verbose make ${MAKE_BUILD_ARGS} -j${NPROC} -C src unittesttargets - - name: Test + - name: Test Vim timeout-minutes: 20 run: make ${MAKE_BUILD_ARGS} test - - name: Test GUI + - name: Test Vim (GUI) timeout-minutes: 20 run: | make ${MAKE_BUILD_ARGS} -C src/testdir clean diff --git a/src/MacVim/MMAppController.m b/src/MacVim/MMAppController.m index 79c4996b93..9afacf7a72 100644 --- a/src/MacVim/MMAppController.m +++ b/src/MacVim/MMAppController.m @@ -121,6 +121,7 @@ - (NSArray *)filterOpenFiles:(NSArray *)filenames - (void)handleXcodeModEvent:(NSAppleEventDescriptor *)event replyEvent:(NSAppleEventDescriptor *)reply; #endif ++ (NSDictionary*)parseOpenURL:(NSURL*)url; - (void)handleGetURLEvent:(NSAppleEventDescriptor *)event replyEvent:(NSAppleEventDescriptor *)reply; - (NSMutableDictionary *)extractArgumentsFromOdocEvent: @@ -523,30 +524,7 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification // If the current version is larger, set that to be stored. Don't // want to do it otherwise to prevent testing older versions flipping // the stored version back to an old one. - NSArray *lastUsedVersionItems = [lastUsedVersion componentsSeparatedByString:@"."]; - NSArray *currentVersionItems = [currentVersion componentsSeparatedByString:@"."]; - // Compare two arrays lexographically. We just assume that version - // numbers are also X.Y.Z… with no "beta" etc texts. - BOOL currentVersionLarger = NO; - for (int i = 0; i < currentVersionItems.count || i < lastUsedVersionItems.count; i++) { - if (i >= currentVersionItems.count) { - currentVersionLarger = NO; - break; - } - if (i >= lastUsedVersionItems.count) { - currentVersionLarger = YES; - break; - } - if (currentVersionItems[i].integerValue > lastUsedVersionItems[i].integerValue) { - currentVersionLarger = YES; - break; - } - else if (currentVersionItems[i].integerValue < lastUsedVersionItems[i].integerValue) { - currentVersionLarger = NO; - break; - } - } - + const BOOL currentVersionLarger = (compareSemanticVersions(lastUsedVersion, currentVersion) == 1); if (currentVersionLarger) { [ud setValue:currentVersion forKey:MMLastUsedBundleVersionKey]; @@ -2130,6 +2108,73 @@ - (void)handleXcodeModEvent:(NSAppleEventDescriptor *)event } #endif ++ (NSDictionary*)parseOpenURL:(NSURL*)url +{ + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; + + // Parse query ("url=file://...&line=14") into a dictionary + NSArray *queries = [[url query] componentsSeparatedByString:@"&"]; + NSEnumerator *enumerator = [queries objectEnumerator]; + NSString *param; + while ((param = [enumerator nextObject])) { + // query: = + NSArray *arr = [param componentsSeparatedByString:@"="]; + if ([arr count] == 2) { + // parse field + NSString *f = [arr objectAtIndex:0]; +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11 + f = [f stringByRemovingPercentEncoding]; +#else + f = [f stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; +#endif + + // parse value + NSString *v = [arr objectAtIndex:1]; + + // We need to decode the parameters here because most URL + // parsers treat the query component as needing to be decoded + // instead of treating it as is. It does mean that a link to + // open file "/tmp/file name.txt" will be + // mvim://open?url=file:///tmp/file%2520name.txt to encode a + // URL of file:///tmp/file%20name.txt. This double-encoding is + // intentional to follow the URL spec. +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11 + v = [v stringByRemovingPercentEncoding]; +#else + v = [v stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; +#endif + + if ([f isEqualToString:@"url"]) { + // Since the URL scheme uses a double-encoding due to a + // file:// URL encoded in another mvim: one, existing tools + // like iTerm2 could sometimes erroneously only do a single + // encode. To maximize compatiblity, we re-encode invalid + // characters if we detect them as they would not work + // later on when we pass this string to URLWithString. + // + // E.g. mvim://open?uri=file:///foo%20bar => "file:///foo bar" + // which is not a valid URL, so we re-encode it to + // file:///foo%20bar here. The only important case is to + // not touch the "%" character as it introduces ambiguity + // and the re-encoding is a nice compatibility feature, but + // the canonical form should be double-encoded, i.e. + // mvim://open?uri=file:///foo%2520bar +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 + if (AVAILABLE_MAC_OS(10, 10)) { + NSMutableCharacterSet *charSet = [NSMutableCharacterSet characterSetWithCharactersInString:@"%"]; + [charSet formUnionWithCharacterSet:NSCharacterSet.URLHostAllowedCharacterSet]; + [charSet formUnionWithCharacterSet:NSCharacterSet.URLPathAllowedCharacterSet]; + v = [v stringByAddingPercentEncodingWithAllowedCharacters:charSet]; + } +#endif + } + + [dict setValue:v forKey:f]; + } + } + return dict; +} + - (void)handleGetURLEvent:(NSAppleEventDescriptor *)event replyEvent:(NSAppleEventDescriptor *)reply { @@ -2138,7 +2183,7 @@ - (void)handleGetURLEvent:(NSAppleEventDescriptor *)event stringValue]]; // We try to be compatible with TextMate's URL scheme here, as documented - // at http://blog.macromates.com/2007/the-textmate-url-scheme/ . Currently, + // at https://macromates.com/blog/2007/the-textmate-url-scheme/ . Currently, // this means that: // // The format is: mvim://open? where arguments can be: @@ -2151,66 +2196,8 @@ - (void)handleGetURLEvent:(NSAppleEventDescriptor *)event // Example: mvim://open?url=file:///etc/profile&line=20 if ([[url host] isEqualToString:@"open"]) { - NSMutableDictionary *dict = [NSMutableDictionary dictionary]; - - // Parse query ("url=file://...&line=14") into a dictionary - NSArray *queries = [[url query] componentsSeparatedByString:@"&"]; - NSEnumerator *enumerator = [queries objectEnumerator]; - NSString *param; - while ((param = [enumerator nextObject])) { - // query: = - NSArray *arr = [param componentsSeparatedByString:@"="]; - if ([arr count] == 2) { - // parse field - NSString *f = [arr objectAtIndex:0]; -#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11 - f = [f stringByRemovingPercentEncoding]; -#else - f = [f stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; -#endif - - // parse value - NSString *v = [arr objectAtIndex:1]; - - // We need to decode the parameters here because most URL - // parsers treat the query component as needing to be decoded - // instead of treating it as is. It does mean that a link to - // open file "/tmp/file name.txt" will be - // mvim://open?url=file:///tmp/file%2520name.txt to encode a - // URL of file:///tmp/file%20name.txt. This double-encoding is - // intentional to follow the URL spec. -#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11 - v = [v stringByRemovingPercentEncoding]; -#else - v = [v stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; -#endif - - if ([f isEqualToString:@"url"]) { - // Since the URL scheme uses a double-encoding due to a - // file:// URL encoded in another mvim: one, existing tools - // like iTerm2 could sometimes erroneously only do a single - // encode. To maximize compatiblity, we re-encode invalid - // characters if we detect them as they would not work - // later on when we pass this string to URLWithString. - // - // E.g. mvim://open?uri=file:///foo%20bar => "file:///foo bar" - // which is not a valid URL, so we re-encode it to - // file:///foo%20bar here. The only important case is to - // not touch the "%" character as it introduces ambiguity - // and the re-encoding is a nice compatibility feature, but - // the canonical form should be double-encoded, i.e. - // mvim://open?uri=file:///foo%2520bar -#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11 - NSMutableCharacterSet *charSet = [NSMutableCharacterSet characterSetWithCharactersInString:@"%"]; - [charSet formUnionWithCharacterSet:NSCharacterSet.URLHostAllowedCharacterSet]; - [charSet formUnionWithCharacterSet:NSCharacterSet.URLPathAllowedCharacterSet]; - v = [v stringByAddingPercentEncodingWithAllowedCharacters:charSet]; -#endif - } - - [dict setValue:v forKey:f]; - } - } + // Parse the URL and process it + NSDictionary *dict = [MMAppController parseOpenURL:url]; // Actually open the file. NSString *file = [dict objectForKey:@"url"]; diff --git a/src/MacVim/MMBackend.m b/src/MacVim/MMBackend.m index 5e3bc0ff45..cd127be61e 100644 --- a/src/MacVim/MMBackend.m +++ b/src/MacVim/MMBackend.m @@ -2319,6 +2319,10 @@ - (void)handleInputEvent:(int)msgid data:(NSData *)data [self setImState:NO]; } else if (BackingPropertiesChangedMsgID == msgid) { [self redrawScreen]; + } else if (LoopBackMsgID == msgid) { + // This is a debug message used for confirming a message has been + // received and echoed back to caller for synchronization purpose. + [self queueMessage:msgid data:nil]; } else { ASLogWarn(@"Unknown message received (msgid=%d)", msgid); } diff --git a/src/MacVim/MacVim.h b/src/MacVim/MacVim.h index 946e729c45..97d662636a 100644 --- a/src/MacVim/MacVim.h +++ b/src/MacVim/MacVim.h @@ -344,6 +344,7 @@ extern const char * const MMVimMsgIDStrings[]; MSG(EnableThinStrokesMsgID) \ MSG(DisableThinStrokesMsgID) \ MSG(ShowDefinitionMsgID) \ + MSG(LoopBackMsgID) /* Simple message that Vim will reflect back to MacVim */ \ MSG(LastMsgID) \ enum { diff --git a/src/MacVim/MacVim.xcodeproj/project.pbxproj b/src/MacVim/MacVim.xcodeproj/project.pbxproj index f8e239e526..a10e62d398 100644 --- a/src/MacVim/MacVim.xcodeproj/project.pbxproj +++ b/src/MacVim/MacVim.xcodeproj/project.pbxproj @@ -71,6 +71,7 @@ 907FF7512521BCE200BADACB /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 907FF74F2521BCE200BADACB /* MainMenu.xib */; }; 907FF7542521BDA600BADACB /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = 907FF7522521BDA600BADACB /* Preferences.xib */; }; 907FF7572521BDC300BADACB /* FindAndReplace.xib in Resources */ = {isa = PBXBuildFile; fileRef = 907FF7552521BDC200BADACB /* FindAndReplace.xib */; }; + 908A1E092AE496D200AB5862 /* MacVimTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 908A1E082AE496D200AB5862 /* MacVimTests.m */; }; 909894382A56EB1E007B84A3 /* WhatsNew.xib in Resources */ = {isa = PBXBuildFile; fileRef = 909894362A56EB1E007B84A3 /* WhatsNew.xib */; }; 9098943C2A56ECF6007B84A3 /* MMWhatsNewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9098943B2A56ECF6007B84A3 /* MMWhatsNewController.m */; }; 90A33BEA28D563DF003A2E2F /* MMSparkle2Delegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 90A33BE928D563DF003A2E2F /* MMSparkle2Delegate.m */; }; @@ -107,6 +108,13 @@ remoteGlobalIDString = 8D57630D048677EA00EA77CD; remoteInfo = QuickLookStephen; }; + 908A1E0A2AE496D200AB5862 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8D1107260486CEB800E47090; + remoteInfo = MacVim; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -295,6 +303,9 @@ 907FF7622521C2FB00BADACB /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/MainMenu.strings"; sourceTree = ""; }; 907FF7632521CBAC00BADACB /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.strings"; sourceTree = ""; }; 907FF7642521CBC500BADACB /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/MainMenu.strings; sourceTree = ""; }; + 908A1E002AE4965900AB5862 /* MacVim.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = MacVim.xctestplan; sourceTree = ""; }; + 908A1E062AE496D200AB5862 /* MacVimTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MacVimTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 908A1E082AE496D200AB5862 /* MacVimTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MacVimTests.m; sourceTree = ""; }; 90922A3B221D429500F1E1F4 /* misc2.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = misc2.c; path = ../misc2.c; sourceTree = ""; }; 90922A3C221D429500F1E1F4 /* if_mzsch.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = if_mzsch.c; path = ../if_mzsch.c; sourceTree = ""; }; 90922A3D221D429500F1E1F4 /* version.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = version.h; path = ../version.h; sourceTree = ""; }; @@ -418,13 +429,13 @@ 9098943B2A56ECF6007B84A3 /* MMWhatsNewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MMWhatsNewController.m; sourceTree = ""; }; 90A33BE928D563DF003A2E2F /* MMSparkle2Delegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MMSparkle2Delegate.m; sourceTree = ""; }; 90A33BEC28D56423003A2E2F /* MMSparkle2Delegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MMSparkle2Delegate.h; sourceTree = ""; }; + 90AF83A92A8C37F70046DA2E /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 90AF83B32AA15BE50046DA2E /* if_perl.xs */ = {isa = PBXFileReference; explicitFileType = sourcecode.c; name = if_perl.xs; path = ../if_perl.xs; sourceTree = ""; }; 90AF83B42AA15C660046DA2E /* nv_cmdidxs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = nv_cmdidxs.h; path = ../nv_cmdidxs.h; sourceTree = ""; }; 90AF83B52AA15C660046DA2E /* alloc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = alloc.c; path = ../alloc.c; sourceTree = ""; }; 90AF83B62AA15C660046DA2E /* nv_cmds.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = nv_cmds.h; path = ../nv_cmds.h; sourceTree = ""; }; 90AF83B72AA15C660046DA2E /* vim9cmds.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = vim9cmds.c; path = ../vim9cmds.c; sourceTree = ""; }; 90AF83B82AA15C660046DA2E /* termdefs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = termdefs.h; path = ../termdefs.h; sourceTree = ""; }; - 90AF83A92A8C37F70046DA2E /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 90B9877B2A579F9500FC95D6 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; 90F84F1E2521F2270000268B /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/MainMenu.strings; sourceTree = ""; }; 90F84F232521F6480000268B /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/MainMenu.strings; sourceTree = ""; }; @@ -473,6 +484,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 908A1E032AE496D200AB5862 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -545,6 +563,7 @@ isa = PBXGroup; children = ( 8D1107320486CEB800E47090 /* MacVim.app */, + 908A1E062AE496D200AB5862 /* MacVimTests.xctest */, ); name = Products; sourceTree = ""; @@ -613,11 +632,13 @@ 29B97314FDCFA39411CA2CEA /* MacVim */ = { isa = PBXGroup; children = ( + 908A1E002AE4965900AB5862 /* MacVim.xctestplan */, 1D493D640C52482B00AB718C /* Executables */, 080E96DDFE201D6D7F000001 /* MacVim Source */, 29B97317FDCFA39411CA2CEA /* Resources */, 1DE602460C587F760055263D /* Vim Resources */, 90922A3A221D417800F1E1F4 /* Vim Source */, + 908A1E072AE496D200AB5862 /* MacVimTests */, 29B97323FDCFA39411CA2CEA /* Frameworks */, 52818AF81C1C073400F59085 /* QuickLook Plugin */, 19C28FACFE9D520D11CA2CBB /* Products */, @@ -680,6 +701,14 @@ name = Products; sourceTree = ""; }; + 908A1E072AE496D200AB5862 /* MacVimTests */ = { + isa = PBXGroup; + children = ( + 908A1E082AE496D200AB5862 /* MacVimTests.m */, + ); + path = MacVimTests; + sourceTree = ""; + }; 90922A3A221D417800F1E1F4 /* Vim Source */ = { isa = PBXGroup; children = ( @@ -903,6 +932,24 @@ productReference = 8D1107320486CEB800E47090 /* MacVim.app */; productType = "com.apple.product-type.application"; }; + 908A1E052AE496D200AB5862 /* MacVimTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 908A1E0C2AE496D200AB5862 /* Build configuration list for PBXNativeTarget "MacVimTests" */; + buildPhases = ( + 908A1E022AE496D200AB5862 /* Sources */, + 908A1E032AE496D200AB5862 /* Frameworks */, + 908A1E042AE496D200AB5862 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 908A1E0B2AE496D200AB5862 /* PBXTargetDependency */, + ); + name = MacVimTests; + productName = MacVimTests; + productReference = 908A1E062AE496D200AB5862 /* MacVimTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -911,6 +958,12 @@ attributes = { BuildIndependentTargetsInParallel = YES; LastUpgradeCheck = 0710; + TargetAttributes = { + 908A1E052AE496D200AB5862 = { + CreatedOnToolsVersion = 15.0.1; + TestTargetID = 8D1107260486CEB800E47090; + }; + }; }; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "MacVim" */; compatibilityVersion = "Xcode 6.3"; @@ -956,6 +1009,7 @@ projectRoot = ""; targets = ( 8D1107260486CEB800E47090 /* MacVim */, + 908A1E052AE496D200AB5862 /* MacVimTests */, ); }; /* End PBXProject section */ @@ -1020,6 +1074,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 908A1E042AE496D200AB5862 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -1176,6 +1237,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 908A1E022AE496D200AB5862 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 908A1E092AE496D200AB5862 /* MacVimTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -1189,6 +1258,11 @@ name = QuickLookStephen; targetProxy = 52818B001C1C084100F59085 /* PBXContainerItemProxy */; }; + 908A1E0B2AE496D200AB5862 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8D1107260486CEB800E47090 /* MacVim */; + targetProxy = 908A1E0A2AE496D200AB5862 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -1256,6 +1330,123 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 908A1E0D2AE496D200AB5862 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + 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_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + 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; + GENERATE_INFOPLIST_FILE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MacVim.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/MacVim"; + }; + name = Debug; + }; + 908A1E0E2AE496D200AB5862 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + 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; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + 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; + GENERATE_INFOPLIST_FILE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MacVim.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/MacVim"; + }; + name = Release; + }; C01FCF4B08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1352,6 +1543,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 908A1E0C2AE496D200AB5862 /* Build configuration list for PBXNativeTarget "MacVimTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 908A1E0D2AE496D200AB5862 /* Debug */, + 908A1E0E2AE496D200AB5862 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "MacVim" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/src/MacVim/MacVim.xcodeproj/xcshareddata/xcschemes/MacVim.xcscheme b/src/MacVim/MacVim.xcodeproj/xcshareddata/xcschemes/MacVim.xcscheme new file mode 100644 index 0000000000..475716d852 --- /dev/null +++ b/src/MacVim/MacVim.xcodeproj/xcshareddata/xcschemes/MacVim.xcscheme @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/MacVim/MacVim.xctestplan b/src/MacVim/MacVim.xctestplan new file mode 100644 index 0000000000..31c2a80b81 --- /dev/null +++ b/src/MacVim/MacVim.xctestplan @@ -0,0 +1,32 @@ +{ + "configurations" : [ + { + "id" : "6F3712E8-FA03-4BC3-99F0-F08F08C9C4F4", + "name" : "Configuration 1", + "options" : { + + } + } + ], + "defaultOptions" : { + "commandLineArgumentEntries" : [ + { + "argument" : "-IgnoreUserDefaults 1" + }, + { + "argument" : "-MMUntitledWindow 0" + } + ], + "testTimeoutsEnabled" : true + }, + "testTargets" : [ + { + "target" : { + "containerPath" : "container:MacVim.xcodeproj", + "identifier" : "908A1E052AE496D200AB5862", + "name" : "MacVimTests" + } + } + ], + "version" : 1 +} diff --git a/src/MacVim/MacVimTests/MacVimTests.m b/src/MacVim/MacVimTests/MacVimTests.m new file mode 100644 index 0000000000..05a191349c --- /dev/null +++ b/src/MacVim/MacVimTests/MacVimTests.m @@ -0,0 +1,334 @@ +// +// MacVimTests.m +// +// Contains unit tests and end-to-end app tests. Currently everything is in one +// file as we only have a few tests. As we expand test coverage we should split +// them up and refactor to more logical components. +// + +#import + +#import + +#import "Miscellaneous.h" +#import "MMAppController.h" +#import "MMApplication.h" +#import "MMTextView.h" +#import "MMWindowController.h" +#import "MMVimController.h" +#import "MMVimView.h" + +// Expose private methods for testing purposes +@interface MMAppController (Private) ++ (NSDictionary*)parseOpenURL:(NSURL*)url; +@end + +@interface MMVimController (Private) +- (void)handleMessage:(int)msgid data:(NSData *)data; +@end + +// Test harness +@interface MMAppController (Tests) +- (NSMutableArray*)vimControllers; +@end + +@implementation MMAppController (Tests) +- (NSMutableArray*)vimControllers { + return vimControllers; +} +@end + +@interface MacVimTests : XCTestCase + +@end + +@implementation MacVimTests + +/// Wait for a Vim controller to be added/removed. By the time this is fulfilled +/// the Vim window should be ready and visible. +- (void)waitForVimController:(int)delta { + NSArray *vimControllers = [MMAppController.sharedInstance vimControllers]; + const int desiredCount = (int)vimControllers.count + delta; + [self waitForExpectations:@[[[XCTNSPredicateExpectation alloc] + initWithPredicate:[NSPredicate predicateWithBlock:^(id vimControllers, NSDictionary *bindings) { + return (BOOL)((int)[(NSArray*)vimControllers count] == desiredCount); + }] + object:vimControllers]] + timeout:5]; +} + +/// Wait for event handling to be finished at the main loop. +- (void)waitForEventHandling { + // Inject a custom event. By the time we handle this event all queued events + // will have been consumed. + const NSInteger appEventType = 1687648131; // magic number to prevent collisions + XCTestExpectation *expectation = [self expectationWithDescription:@"EventHandling"]; + + SEL sel = @selector(sendEvent:); + Method method = class_getInstanceMethod([MMApplication class], sel); + + IMP origIMP = method_getImplementation(method); + IMP newIMP = imp_implementationWithBlock(^(id self, NSEvent *event) { + typedef void (*fn)(id,SEL,NSEvent*); + if (event.type == NSEventTypeApplicationDefined && event.data1 == appEventType) { + [expectation fulfill]; + } else { + ((fn)origIMP)(self, sel, event); + } + }); + + NSApplication* app = [NSApplication sharedApplication]; + NSEvent* customEvent = [NSEvent otherEventWithType:NSEventTypeApplicationDefined + location:NSMakePoint(50, 50) + modifierFlags:0 + timestamp:100 + windowNumber:[[NSApp mainWindow] windowNumber] + context:0 + subtype:0 + data1:appEventType + data2:0]; + + method_setImplementation(method, newIMP); + + [app postEvent:customEvent atStart:NO]; + [self waitForExpectations:@[expectation] timeout:10]; + + method_setImplementation(method, origIMP); +} + +/// Wait for Vim to process all pending messages in its queue. +- (void)waitForVimProcess { + // Implement this by sending a loopback message (Vim will send the message + // back to us) as a synchronization mechanism as Vim handles its messages + // sequentially. + XCTestExpectation *expectation = [self expectationWithDescription:@"VimLoopBack"]; + + SEL sel = @selector(handleMessage:data:); + Method method = class_getInstanceMethod([MMVimController class], sel); + + IMP origIMP = method_getImplementation(method); + IMP newIMP = imp_implementationWithBlock(^(id self, int msgid, NSData *data) { + typedef void (*fn)(id,SEL,int,NSData*); + if (msgid == LoopBackMsgID) { + [expectation fulfill]; + } else { + ((fn)origIMP)(self, sel, msgid, data); + } + }); + + method_setImplementation(method, newIMP); + + [[MMAppController.sharedInstance keyVimController] sendMessage:LoopBackMsgID data:nil]; + [self waitForExpectations:@[expectation] timeout:10]; + + method_setImplementation(method, origIMP); +} + +/// Wait for both event handling to be finished at the main loop and for Vim to +/// process all pending messages in its queue. +- (void)waitForEventHandlingAndVimProcess { + [self waitForEventHandling]; + [self waitForVimProcess]; +} + +/// Wait for a fixed timeout before fulfilling expectation. +/// +/// @note Should only be used for quick iteration / debugging unless we cannot +/// find an alternative way to specify an expectation, as timeouts tend to be +/// fragile and take more time to complete. +- (void)waitTimeout:(double)delaySecs { + XCTestExpectation *expectation = [self expectationWithDescription:@"Timeout"]; + dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delaySecs * NSEC_PER_SEC)); + dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ + [expectation fulfill]; + }); + [self waitForExpectations:@[expectation] timeout:delaySecs + 10]; +} + +/// Send a single key to MacVim via event handling system. +- (void)sendKeyToVim:(NSString*)chars withMods:(int)mods { + NSApplication* app = [NSApplication sharedApplication]; + NSEvent* keyEvent = [NSEvent keyEventWithType:NSEventTypeKeyDown + location:NSMakePoint(50, 50) + modifierFlags:mods + timestamp:100 + windowNumber:[[NSApp mainWindow] windowNumber] + context:0 + characters:chars + charactersIgnoringModifiers:chars + isARepeat:NO + keyCode:0]; + + [app postEvent:keyEvent atStart:NO]; +} + +/// Send a string to MacVim via event handling system. Each character will be +/// sent separately as if the user typed it. +- (void)sendStringToVim:(NSString*)chars withMods:(int)mods { + for (NSUInteger i = 0; i < chars.length; i++) { + unichar ch = [chars characterAtIndex:i]; + NSString *str = [NSString stringWithCharacters:&ch length:1]; + [self sendKeyToVim:str withMods:mods]; + } +} + +- (void)testCompareSemanticVersions { + // bogus values evaluate to 0 + XCTAssertEqual(0, compareSemanticVersions(@"bogus", @"")); + XCTAssertEqual(0, compareSemanticVersions(@"bogus", @"0")); + XCTAssertEqual(0, compareSemanticVersions(@"", @"")); + + // single values + XCTAssertEqual(1, compareSemanticVersions(@"", @"1")); + XCTAssertEqual(-1, compareSemanticVersions(@"1", @"")); + XCTAssertEqual(1, compareSemanticVersions(@"100", @"101")); + XCTAssertEqual(-1, compareSemanticVersions(@"101", @"100")); + + // multiple semantic values + XCTAssertEqual(1, compareSemanticVersions(@"100", @"100.1")); + XCTAssertEqual(-1, compareSemanticVersions(@"100.1", @"100")); + XCTAssertEqual(1, compareSemanticVersions(@"100.2", @"100.3")); + XCTAssertEqual(-1, compareSemanticVersions(@"100.10", @"100.2")); // double digit after the dot to make sure we are parsing it properly + XCTAssertEqual(0, compareSemanticVersions(@"234.5", @"234.5")); + XCTAssertEqual(-1, compareSemanticVersions(@"234.5.1", @"234.5")); + XCTAssertEqual(1, compareSemanticVersions(@"234.5", @"234.5.0")); +} + +/// Tests that parseOpenURL complies with the spec. See ":h macvim-url-handler". +- (void)testParseOpenURL { + XCTAssertEqualObjects([MMAppController parseOpenURL:[NSURL URLWithString:@"mvim://open?"]], @{}); + XCTAssertEqualObjects([MMAppController parseOpenURL:[NSURL URLWithString:@"mvim://open?url=file:///foo/bar"]], @{@"url": @"file:///foo/bar"}); + + // Test that we correctly decode the URL, where special characters like space need to be double encoded. + XCTAssertEqualObjects([MMAppController parseOpenURL:[NSURL URLWithString:@"mvim://open?url=file:///foo/bar%2520file"]], @{@"url": @"file:///foo/bar%20file"}); + XCTAssertEqualObjects([[NSURL URLWithString:@"file:///foo/bar%20file"] path], @"/foo/bar file"); + // Test opportunistic single-encoding for compatibility with old behaviors and other tools. + XCTAssertEqualObjects([MMAppController parseOpenURL:[NSURL URLWithString:@"mvim://open?url=file:///foo/bar%20file"]], @{@"url": @"file:///foo/bar%20file"}); + + // Test mixed single/double-encoding. + XCTAssertEqualObjects([MMAppController parseOpenURL:[NSURL URLWithString:@"mvim://open?url=file:///foo/bar%20%2520file%253F"]], @{@"url": @"file:///foo/bar%20%20file%3F"}); + + // Test that with certain special characters like "&", you have to encode at least once, as otherwise it will be interpreted as a separator. + XCTAssertEqualObjects([MMAppController parseOpenURL:[NSURL URLWithString:@"mvim://open?url=file:///foo&bar"]], @{@"url": @"file:///foo"}); // lost the "bar" in the path + XCTAssertEqualObjects([MMAppController parseOpenURL:[NSURL URLWithString:@"mvim://open?url=file:///foo%26bar"]], @{@"url": @"file:///foo&bar"}); + XCTAssertEqualObjects([[NSURL URLWithString:@"file:///foo&bar"] path], @"/foo&bar"); + XCTAssertEqualObjects([MMAppController parseOpenURL:[NSURL URLWithString:@"mvim://open?url=file:///foo%2526bar"]], @{@"url": @"file:///foo%26bar"}); + XCTAssertEqualObjects([[NSURL URLWithString:@"file:///foo%26bar"] path], @"/foo&bar"); + + // Test that '%' in a file name is a special case, where only double-encoding works. The opportunistic single-encoding doesn't work here. + XCTAssertEqualObjects([MMAppController parseOpenURL:[NSURL URLWithString:@"mvim://open?url=file:///foo%bar"]], @{}); // This should fail at decoding step + XCTAssertEqualObjects([MMAppController parseOpenURL:[NSURL URLWithString:@"mvim://open?url=file:///foo%25bar"]], @{@"url": @"file:///foo%bar"}); // Not valid file URL + XCTAssertEqualObjects([[NSURL URLWithString:@"file:///foo%bar"] path], nil); // Invalid decoded file URL leads to nil + XCTAssertEqualObjects([MMAppController parseOpenURL:[NSURL URLWithString:@"mvim://open?url=file:///foo%2525bar"]], @{@"url": @"file:///foo%25bar"}); + XCTAssertEqualObjects([[NSURL URLWithString:@"file:///foo%25bar"] path], @"/foo%bar"); +} + +/// Test that the "Vim Tutor" menu item works and can be used to launch the +/// bundled vimtutor. Previously this was silently broken by Vim v8.2.3502 +/// and fixed in https://github.com/macvim-dev/macvim/pull/1265. +- (void)testVimTutor { + MMAppController *app = MMAppController.sharedInstance; + + // Adding a new window is necessary for the vimtutor menu to show up as it's + // not part of the global menu + [app openNewWindow:NewWindowClean activate:YES]; + [self waitForVimController:1]; + + // Find the vimtutor menu and run it. + NSMenu *mainMenu = [NSApp mainMenu]; + NSMenu *helpMenu = [mainMenu findHelpMenu]; + NSMenuItem *vimTutorMenu = nil; + for (NSInteger i = 0; i < helpMenu.numberOfItems; ++i) { + NSMenuItem *menuItem = [helpMenu itemAtIndex:i]; + if ([menuItem.title isEqualToString:@"Vim Tutor"]) + vimTutorMenu = menuItem; + } + XCTAssertNotNil(vimTutorMenu); + XCTAssertEqual(vimTutorMenu.action, @selector(vimMenuItemAction:)); + [[[app keyVimController] windowController] vimMenuItemAction:vimTutorMenu]; + + // Make sure the menu item actually opened a new window and point to a tutor buffer + [self waitForVimController:1]; + + NSString *bufname = [[app keyVimController] evaluateVimExpression:@"bufname()"]; + XCTAssertTrue([bufname containsString:@"tutor"]); + + // Clean up + [[app keyVimController] sendMessage:VimShouldCloseMsgID data:nil]; + [self waitForVimController:-1]; + [[app keyVimController] sendMessage:VimShouldCloseMsgID data:nil]; + [self waitForVimController:-1]; +} + +/// Test that cmdline row calculation (used by MMCmdLineAlignBottom) is correct. +/// This is an integration test as the calculation is done in Vim, which has +/// special logic to account for "Press Enter" and "--more--" prompts when showing +/// messages. +- (void) testCmdlineRowCalculation { + MMAppController *app = MMAppController.sharedInstance; + + [app openNewWindow:NewWindowClean activate:YES]; + [self waitForVimController:1]; + + MMTextView *textView = [[[[app keyVimController] windowController] vimView] textView]; + const int numLines = [textView maxRows]; + const int numCols = [textView maxColumns]; + + // Define convenience macro (don't use functions to preserve line numbers in callstack) +#define ASSERT_NUM_CMDLINES(expected) \ +{ \ + const int cmdlineRow = [[[app keyVimController] objectForVimStateKey:@"cmdline_row"] intValue]; \ + const int numBottomLines = numLines - cmdlineRow; \ + XCTAssertEqual(expected, numBottomLines); \ +} + + // Default value + [self waitForEventHandlingAndVimProcess]; + ASSERT_NUM_CMDLINES(1); + + // Print more lines than we have room for to trigger "Press Enter" + [self sendStringToVim:@":echo join(repeat(['test line'], 3), \"\\n\")\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + ASSERT_NUM_CMDLINES(1); + + // Test non-1 cmdheight works + [self sendStringToVim:@":set cmdheight=3\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + ASSERT_NUM_CMDLINES(3); + + // Test typing enough characters to cause cmdheight to grow + [self sendStringToVim:[@":\"" stringByPaddingToLength:numCols * 3 - 1 withString:@"a" startingAtIndex:0] withMods:0]; + [self waitForEventHandlingAndVimProcess]; + ASSERT_NUM_CMDLINES(3); + + [self sendStringToVim:@"bbbb" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + ASSERT_NUM_CMDLINES(4); + + [self sendStringToVim:@"\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + ASSERT_NUM_CMDLINES(3); + + // Printing just enough lines within cmdheight should not affect anything + [self sendStringToVim:@":echo join(repeat(['test line'], 3), \"\\n\")\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + ASSERT_NUM_CMDLINES(3); + + // Printing more lines than cmdheight will once again trigger "Press Enter" + [self sendStringToVim:@":echo join(repeat(['test line'], 4), \"\\n\")\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + ASSERT_NUM_CMDLINES(1); + + // Printing more lines than the screen will trigger "--more--" prompt + [self sendStringToVim:@":echo join(repeat(['test line'], 2000), \"\\n\")\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + ASSERT_NUM_CMDLINES(1); + +#undef ASSERT_NUM_CMDLINES + + // Clean up + [[app keyVimController] sendMessage:VimShouldCloseMsgID data:nil]; + [self waitForVimController:-1]; +} + +@end diff --git a/src/MacVim/MacVim_xcode8.xcodeproj/project.pbxproj b/src/MacVim/MacVim_xcode8.xcodeproj/project.pbxproj index b1c8c0612b..070f45448b 100644 --- a/src/MacVim/MacVim_xcode8.xcodeproj/project.pbxproj +++ b/src/MacVim/MacVim_xcode8.xcodeproj/project.pbxproj @@ -72,6 +72,7 @@ 907FF7512521BCE200BADACB /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 907FF74F2521BCE200BADACB /* MainMenu.xib */; }; 907FF7542521BDA600BADACB /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = 907FF7522521BDA600BADACB /* Preferences.xib */; }; 907FF7572521BDC300BADACB /* FindAndReplace.xib in Resources */ = {isa = PBXBuildFile; fileRef = 907FF7552521BDC200BADACB /* FindAndReplace.xib */; }; + 908A1E092AE496D200AB5862 /* MacVimTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 908A1E082AE496D200AB5862 /* MacVimTests.m */; }; 909894382A56EB1E007B84A3 /* WhatsNew.xib in Resources */ = {isa = PBXBuildFile; fileRef = 909894362A56EB1E007B84A3 /* WhatsNew.xib */; }; 9098943C2A56ECF6007B84A3 /* MMWhatsNewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9098943B2A56ECF6007B84A3 /* MMWhatsNewController.m */; }; 90A33BEA28D563DF003A2E2F /* MMSparkle2Delegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 90A33BE928D563DF003A2E2F /* MMSparkle2Delegate.m */; }; @@ -108,6 +109,13 @@ remoteGlobalIDString = 8D57630D048677EA00EA77CD; remoteInfo = QuickLookStephen; }; + 908A1E0A2AE496D200AB5862 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8D1107260486CEB800E47090; + remoteInfo = MacVim; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -296,6 +304,9 @@ 907FF7622521C2FB00BADACB /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/MainMenu.strings"; sourceTree = ""; }; 907FF7632521CBAC00BADACB /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.strings"; sourceTree = ""; }; 907FF7642521CBC500BADACB /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/MainMenu.strings; sourceTree = ""; }; + 908A1E002AE4965900AB5862 /* MacVim.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = MacVim.xctestplan; sourceTree = ""; }; + 908A1E062AE496D200AB5862 /* MacVimTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MacVimTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 908A1E082AE496D200AB5862 /* MacVimTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MacVimTests.m; sourceTree = ""; }; 90922A3B221D429500F1E1F4 /* misc2.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = misc2.c; path = ../misc2.c; sourceTree = ""; }; 90922A3C221D429500F1E1F4 /* if_mzsch.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = if_mzsch.c; path = ../if_mzsch.c; sourceTree = ""; }; 90922A3D221D429500F1E1F4 /* version.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = version.h; path = ../version.h; sourceTree = ""; }; @@ -419,13 +430,13 @@ 9098943B2A56ECF6007B84A3 /* MMWhatsNewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MMWhatsNewController.m; sourceTree = ""; }; 90A33BE928D563DF003A2E2F /* MMSparkle2Delegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MMSparkle2Delegate.m; sourceTree = ""; }; 90A33BEC28D56423003A2E2F /* MMSparkle2Delegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MMSparkle2Delegate.h; sourceTree = ""; }; + 90AF83A92A8C37F70046DA2E /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 90AF83B32AA15BE50046DA2E /* if_perl.xs */ = {isa = PBXFileReference; explicitFileType = sourcecode.c; name = if_perl.xs; path = ../if_perl.xs; sourceTree = ""; }; 90AF83B42AA15C660046DA2E /* nv_cmdidxs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = nv_cmdidxs.h; path = ../nv_cmdidxs.h; sourceTree = ""; }; 90AF83B52AA15C660046DA2E /* alloc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = alloc.c; path = ../alloc.c; sourceTree = ""; }; 90AF83B62AA15C660046DA2E /* nv_cmds.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = nv_cmds.h; path = ../nv_cmds.h; sourceTree = ""; }; 90AF83B72AA15C660046DA2E /* vim9cmds.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = vim9cmds.c; path = ../vim9cmds.c; sourceTree = ""; }; 90AF83B82AA15C660046DA2E /* termdefs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = termdefs.h; path = ../termdefs.h; sourceTree = ""; }; - 90AF83A92A8C37F70046DA2E /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 90B9877B2A579F9500FC95D6 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; 90F84F1E2521F2270000268B /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/MainMenu.strings; sourceTree = ""; }; 90F84F232521F6480000268B /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/MainMenu.strings; sourceTree = ""; }; @@ -474,6 +485,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 908A1E032AE496D200AB5862 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -546,6 +564,7 @@ isa = PBXGroup; children = ( 8D1107320486CEB800E47090 /* MacVim.app */, + 908A1E062AE496D200AB5862 /* MacVimTests.xctest */, ); name = Products; sourceTree = ""; @@ -614,11 +633,13 @@ 29B97314FDCFA39411CA2CEA /* MacVim */ = { isa = PBXGroup; children = ( + 908A1E002AE4965900AB5862 /* MacVim.xctestplan */, 1D493D640C52482B00AB718C /* Executables */, 080E96DDFE201D6D7F000001 /* MacVim Source */, 29B97317FDCFA39411CA2CEA /* Resources */, 1DE602460C587F760055263D /* Vim Resources */, 90922A3A221D417800F1E1F4 /* Vim Source */, + 908A1E072AE496D200AB5862 /* MacVimTests */, 29B97323FDCFA39411CA2CEA /* Frameworks */, 52818AF81C1C073400F59085 /* QuickLook Plugin */, 19C28FACFE9D520D11CA2CBB /* Products */, @@ -681,6 +702,14 @@ name = Products; sourceTree = ""; }; + 908A1E072AE496D200AB5862 /* MacVimTests */ = { + isa = PBXGroup; + children = ( + 908A1E082AE496D200AB5862 /* MacVimTests.m */, + ); + path = MacVimTests; + sourceTree = ""; + }; 90922A3A221D417800F1E1F4 /* Vim Source */ = { isa = PBXGroup; children = ( @@ -904,6 +933,24 @@ productReference = 8D1107320486CEB800E47090 /* MacVim.app */; productType = "com.apple.product-type.application"; }; + 908A1E052AE496D200AB5862 /* MacVimTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 908A1E0C2AE496D200AB5862 /* Build configuration list for PBXNativeTarget "MacVimTests" */; + buildPhases = ( + 908A1E022AE496D200AB5862 /* Sources */, + 908A1E032AE496D200AB5862 /* Frameworks */, + 908A1E042AE496D200AB5862 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 908A1E0B2AE496D200AB5862 /* PBXTargetDependency */, + ); + name = MacVimTests; + productName = MacVimTests; + productReference = 908A1E062AE496D200AB5862 /* MacVimTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -912,6 +959,12 @@ attributes = { BuildIndependentTargetsInParallel = YES; LastUpgradeCheck = 0710; + TargetAttributes = { + 908A1E052AE496D200AB5862 = { + CreatedOnToolsVersion = 15.0.1; + TestTargetID = 8D1107260486CEB800E47090; + }; + }; }; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "MacVim" */; compatibilityVersion = "Xcode 6.3"; @@ -957,6 +1010,7 @@ projectRoot = ""; targets = ( 8D1107260486CEB800E47090 /* MacVim */, + 908A1E052AE496D200AB5862 /* MacVimTests */, ); }; /* End PBXProject section */ @@ -1021,6 +1075,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 908A1E042AE496D200AB5862 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -1177,6 +1238,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 908A1E022AE496D200AB5862 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 908A1E092AE496D200AB5862 /* MacVimTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -1190,6 +1259,11 @@ name = QuickLookStephen; targetProxy = 52818B001C1C084100F59085 /* PBXContainerItemProxy */; }; + 908A1E0B2AE496D200AB5862 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8D1107260486CEB800E47090 /* MacVim */; + targetProxy = 908A1E0A2AE496D200AB5862 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -1257,6 +1331,123 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 908A1E0D2AE496D200AB5862 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + 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_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + 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; + GENERATE_INFOPLIST_FILE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MacVim.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/MacVim"; + }; + name = Debug; + }; + 908A1E0E2AE496D200AB5862 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + 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; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + 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; + GENERATE_INFOPLIST_FILE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MacVim.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/MacVim"; + }; + name = Release; + }; C01FCF4B08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1353,6 +1544,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 908A1E0C2AE496D200AB5862 /* Build configuration list for PBXNativeTarget "MacVimTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 908A1E0D2AE496D200AB5862 /* Debug */, + 908A1E0E2AE496D200AB5862 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "MacVim" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/src/MacVim/Miscellaneous.h b/src/MacVim/Miscellaneous.h index e8b1607232..d245fbfa32 100644 --- a/src/MacVim/Miscellaneous.h +++ b/src/MacVim/Miscellaneous.h @@ -162,7 +162,7 @@ enum { // Create a view with a "show hidden files" button to be used as accessory for // open/save panels. This function assumes ownership of the view so do not // release it. -NSView *showHiddenFilesView(); +NSView *showHiddenFilesView(void); // Convert filenames (which are in a variant of decomposed form, NFD, on HFS+) @@ -175,11 +175,13 @@ NSView *showHiddenFilesView(); NSString *normalizeFilename(NSString *filename); NSArray *normalizeFilenames(NSArray *filenames); -BOOL shouldUseYosemiteTabBarStyle(); -BOOL shouldUseMojaveTabBarStyle(); +BOOL shouldUseYosemiteTabBarStyle(void); +BOOL shouldUseMojaveTabBarStyle(void); int getCurrentAppearance(NSAppearance *appearance); // Pasteboard helpers -NSPasteboardType getPasteboardFilenamesType(); +NSPasteboardType getPasteboardFilenamesType(void); NSArray* extractPasteboardFilenames(NSPasteboard *pboard); + +int compareSemanticVersions(NSString *oldVersion, NSString *newVersion); diff --git a/src/MacVim/Miscellaneous.m b/src/MacVim/Miscellaneous.m index acc5e0ab21..50d5fd261d 100644 --- a/src/MacVim/Miscellaneous.m +++ b/src/MacVim/Miscellaneous.m @@ -263,7 +263,7 @@ - (NSInteger)tag NSView * -showHiddenFilesView() +showHiddenFilesView(void) { // Return a new button object for each NSOpenPanel -- several of them // could be displayed at once. @@ -320,12 +320,12 @@ - (NSInteger)tag BOOL -shouldUseYosemiteTabBarStyle() +shouldUseYosemiteTabBarStyle(void) { return floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_10; } BOOL -shouldUseMojaveTabBarStyle() +shouldUseMojaveTabBarStyle(void) { #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 if (@available(macos 10.14, *)) { @@ -360,7 +360,7 @@ - (NSInteger)tag /// Returns the pasteboard type to use for retrieving file names from a list of /// files. /// @return The pasteboard type that can be passed to NSPasteboard for registration. -NSPasteboardType getPasteboardFilenamesType() +NSPasteboardType getPasteboardFilenamesType(void) { #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_13 return NSPasteboardTypeFileURL; @@ -407,3 +407,29 @@ NSPasteboardType getPasteboardFilenamesType() #endif } +/// Compare two version strings (must be in integers separated by dots) and see +/// if one is larger. +/// +/// @return 1 if newVersion is newer, 0 if equal, -1 if oldVersion newer. +int compareSemanticVersions(NSString *oldVersion, NSString *newVersion) +{ + NSArray *oldVersionItems = [oldVersion componentsSeparatedByString:@"."]; + NSArray *newVersionItems = [newVersion componentsSeparatedByString:@"."]; + // Compare two arrays lexographically. We just assume that version + // numbers are also X.Y.Z… with no "beta" etc texts. + for (int i = 0; i < oldVersionItems.count || i < newVersionItems.count; i++) { + if (i >= newVersionItems.count) { + return -1; + } + if (i >= oldVersionItems.count) { + return 1; + } + if (newVersionItems[i].integerValue > oldVersionItems[i].integerValue) { + return 1; + } + else if (newVersionItems[i].integerValue < oldVersionItems[i].integerValue) { + return -1; + } + } + return 0; +} diff --git a/src/Makefile b/src/Makefile index 639d3da220..5dd5377dd3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -3666,6 +3666,9 @@ $(RUNTIME_FOLDER_LIST): macvim: $(VIMTARGET) $(RUNTIME_FOLDER_LIST) xcodebuild -project MacVim/MacVim.xcodeproj $(XCODEFLAGS) +macvim-tests: + xcodebuild test -project MacVim/MacVim.xcodeproj -scheme MacVim $(XCODEFLAGS) + macvim-signed: MacVim/scripts/sign-developer-id $(RELEASEDIR)/MacVim.app $(ENTITLEMENTS)