From 532679e68477f741bdfb17b7ee865156065d4bae Mon Sep 17 00:00:00 2001 From: Yee Cheng Chin Date: Sat, 18 Apr 2020 22:25:30 -0700 Subject: [PATCH] Fix mvim:// protocol handler not working well with iTerm2 iTerm2's MacVim integration generates URLs that has the file separator ("/") escaped as ("%2F"). This is incorrect (as it implies the slash is part of the file name itself instead of a separator), but work around it for now. Instead of using NSURL to parse the file:// URL, just decode the input parameter and manually parse it. This still handles the special characters like space or "?", but will handle the escaped slashes as well. Given that "/" is not a valid filename character we should not run into ambiguity here. Also, add dialog boxes to show an error if MacVim doesn't know how to handle a file path. Will add regression tests for these edge cases in a future commit. Fix #1020 --- src/MacVim/MMAppController.m | 97 ++++++++++++++++++++++++++---------- 1 file changed, 72 insertions(+), 25 deletions(-) diff --git a/src/MacVim/MMAppController.m b/src/MacVim/MMAppController.m index 3c688ecec0..dddef7849b 100644 --- a/src/MacVim/MMAppController.m +++ b/src/MacVim/MMAppController.m @@ -1767,8 +1767,23 @@ - (void)handleGetURLEvent:(NSAppleEventDescriptor *)event // parse value NSString *v = [arr objectAtIndex:1]; - // do not decode url, since it's a file URI - BOOL decode = ![f isEqualToString:@"url"]; + // Ideally we don't decode anything here. The input parameters + // should be used as-in as there would be no reason for caller + // to encoder anything. For the line component it's a simple + // string, and the URL should already be a proper file:// URL + // with all the necessary characters (e.g. space) encoded and + // we can just pass it to NSURL as-in below. + // However, iTerm2 appears to encode the slashes as well + // resulting in URL that looks like + // file://%2Fsome%2Ffolder/file%20with%20space which is wrong + // as this doesn't form a valid URL. To accommodate that, we + // decode the URL, and later on manually parse it instead of + // relying on NSURL. + // See: https://github.com/macvim-dev/macvim/issues/1020. + + // BOOL decode = ![f isEqualToString:@"url"]; + const BOOL decode = YES; + if (decode) { #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11 @@ -1785,31 +1800,63 @@ - (void)handleGetURLEvent:(NSAppleEventDescriptor *)event // Actually open the file. NSString *file = [dict objectForKey:@"url"]; if (file != nil) { - NSURL *fileUrl = [NSURL URLWithString:file]; - // TextMate only opens files that already exist. - if ([fileUrl isFileURL] - && [[NSFileManager defaultManager] fileExistsAtPath: - [fileUrl path]]) { - // Strip 'file://' path, else application:openFiles: might think - // the file is not yet open. - NSArray *filenames = [NSArray arrayWithObject:[fileUrl path]]; - - // Look for the line and column options. - NSDictionary *args = nil; - NSString *line = [dict objectForKey:@"line"]; - if (line) { - NSString *column = [dict objectForKey:@"column"]; - if (column) - args = [NSDictionary dictionaryWithObjectsAndKeys: - line, @"cursorLine", - column, @"cursorColumn", - nil]; - else - args = [NSDictionary dictionaryWithObject:line - forKey:@"cursorLine"]; + // Instead of passing "file" to NSURL directly, we just manually + // parse the URL because the URL is already decoded and NSURL will + // get confused by special chars like spaces. See above + // explanation. + if ([file hasPrefix:@"file:///"]) { + NSString *filePath = [file substringFromIndex:7]; + // Only opens files that already exist. + if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { + NSArray *filenames = [NSArray arrayWithObject:filePath]; + + // Look for the line and column options. + NSDictionary *args = nil; + NSString *line = [dict objectForKey:@"line"]; + if (line) { + NSString *column = [dict objectForKey:@"column"]; + if (column) + args = [NSDictionary dictionaryWithObjectsAndKeys: + line, @"cursorLine", + column, @"cursorColumn", + nil]; + else + args = [NSDictionary dictionaryWithObject:line + forKey:@"cursorLine"]; + } + + [self openFiles:filenames withArguments:args]; + } else { + NSAlert *alert = [[NSAlert alloc] init]; + [alert addButtonWithTitle:NSLocalizedString(@"OK", + @"Dialog button")]; + + [alert setMessageText:NSLocalizedString(@"Bad file path", + @"Bad file path dialog, title")]; + [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString( + @"Cannot open file path \"%@\"", + @"Bad file path dialog, text"), + filePath]]; + + [alert setAlertStyle:NSAlertStyleWarning]; + [alert runModal]; + [alert release]; } + } else { + NSAlert *alert = [[NSAlert alloc] init]; + [alert addButtonWithTitle:NSLocalizedString(@"OK", + @"Dialog button")]; + + [alert setMessageText:NSLocalizedString(@"Unknown File Protocol", + @"Unknown File Protocol dialog, title")]; + [alert setInformativeText:[NSString stringWithFormat:NSLocalizedString( + @"Unknown protocol in \"%@\"", + @"Unknown File Protocol dialog, text"), + file]]; - [self openFiles:filenames withArguments:args]; + [alert setAlertStyle:NSAlertStyleWarning]; + [alert runModal]; + [alert release]; } } } else {