From 1934eb991e0166b75e28c434a24dff6c0315ac0e Mon Sep 17 00:00:00 2001 From: Yee Cheng Chin Date: Mon, 21 Sep 2020 22:50:29 -0700 Subject: [PATCH] Help menu search will now search Vim documentation Implement macOS Help menu's search functionality, so that it can be used to search Vim's documentation. For now, can use space-delimited search string to search Vim's doc tags. The search results will display something like "options.txt > 'termwinsize'" when searching for "term size". Currently this only works with Vim's built-in documentation. Due to the asynchronous nature of the search, it's a little tricky to support plugins as different Vim instances could be loading different plugins. For now, just the built-in Vim documentation should serve most of the needs. Also, properly set the help menu on the app so that localized menus will still show the search box (previously it had to be called exactly 'Help'). --- runtime/doc/gui_mac.txt | 3 +- src/MacVim/MMAppController.h | 9 ++- src/MacVim/MMAppController.m | 115 +++++++++++++++++++++++++++++++++-- src/MacVim/Miscellaneous.h | 1 + src/MacVim/Miscellaneous.m | 5 ++ 5 files changed, 125 insertions(+), 8 deletions(-) diff --git a/runtime/doc/gui_mac.txt b/runtime/doc/gui_mac.txt index 720147af31..bcd42d9a0e 100644 --- a/runtime/doc/gui_mac.txt +++ b/runtime/doc/gui_mac.txt @@ -463,8 +463,7 @@ is not used. Hint: The |:macaction| command supports command-line completion so you can enter ":maca" to see a list of all available actions. -Here is a random assortment of actions from Actions.plist which might be -useful. +Here are some of the actions from Actions.plist which might be useful. Action Description ~ fileOpen: Show "File Open" dialog diff --git a/src/MacVim/MMAppController.h b/src/MacVim/MMAppController.h index a4a1b94b40..cd5cfcfa59 100644 --- a/src/MacVim/MMAppController.h +++ b/src/MacVim/MMAppController.h @@ -17,7 +17,7 @@ @class SUUpdater; -@interface MMAppController : NSObject { +@interface MMAppController : NSObject { NSConnection *connection; NSMutableArray *vimControllers; NSString *openSelectionString; @@ -62,6 +62,7 @@ - (IBAction)selectPreviousWindow:(id)sender; - (IBAction)orderFrontPreferencePanel:(id)sender; - (IBAction)openWebsite:(id)sender; +- (IBAction)showVimHelp:(id)sender withCmd:(NSString *)cmd; - (IBAction)showVimHelp:(id)sender; - (IBAction)checkForUpdates:(id)sender; - (IBAction)zoomAll:(id)sender; @@ -69,4 +70,10 @@ - (IBAction)stayInBack:(id)sender; - (IBAction)stayLevelNormal:(id)sender; +- (NSArray *)localizedTitlesForItem:(id)item; +- (void)searchForItemsWithSearchString:(NSString *)searchString + resultLimit:(NSInteger)resultLimit + matchedItemHandler:(void (^)(NSArray *items))handleMatchedItems; +- (void)performActionForItem:(id)item; + @end diff --git a/src/MacVim/MMAppController.m b/src/MacVim/MMAppController.m index 28248bb52d..1b17048b53 100644 --- a/src/MacVim/MMAppController.m +++ b/src/MacVim/MMAppController.m @@ -300,6 +300,9 @@ - (id)init [connection release]; connection = nil; } + // Register help search handler to support search Vim docs via the Help menu + [NSApp registerUserInterfaceItemSearchHandler:self]; + #if !DISABLE_SPARKLE // Sparkle is enabled (this is the default). Initialize it. It will // automatically check for update. @@ -951,6 +954,9 @@ - (void)refreshMainMenu NSMenu *windowsMenu = [mainMenu findWindowsMenu]; [NSApp setWindowsMenu:windowsMenu]; + + NSMenu *helpMenu = [mainMenu findHelpMenu]; + [NSApp setHelpMenu:helpMenu]; } - (NSArray *)filterOpenFiles:(NSArray *)filenames @@ -1150,10 +1156,11 @@ - (IBAction)fileOpen:(id)sender NSInteger result = [panel runModal]; #if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10) - if (NSModalResponseOK == result) { + if (NSModalResponseOK == result) #else - if (NSOKButton == result) { + if (NSOKButton == result) #endif + { // NOTE: -[NSOpenPanel filenames] is deprecated on 10.7 so use // -[NSOpenPanel URLs] instead. The downside is that we have to check // that each URL is really a path first. @@ -1234,15 +1241,20 @@ - (IBAction)openWebsite:(id)sender [NSURL URLWithString:MMWebsiteString]]; } -- (IBAction)showVimHelp:(id)sender +- (IBAction)showVimHelp:(id)sender withCmd:(NSString *)cmd { ASLogDebug(@"Open window with Vim help"); - // Open a new window with the help window maximized. + // Open a new window with only the help window shown. [self launchVimProcessWithArguments:[NSArray arrayWithObjects: - @"-c", @":h gui_mac", @"-c", @":res", nil] + @"-c", cmd, @"-c", @":only", nil] workingDirectory:nil]; } +- (IBAction)showVimHelp:(id)sender +{ + [self showVimHelp:sender withCmd:@":h gui_mac"]; +} + - (IBAction)checkForUpdates:(id)sender { #if !DISABLE_SPARKLE @@ -1421,6 +1433,99 @@ - (NSArray *)serverList return array; } +// Begin NSUserInterfaceItemSearching implementation +- (NSArray *)localizedTitlesForItem:(id)item +{ + return item; +} + +- (void)searchForItemsWithSearchString:(NSString *)searchString + resultLimit:(NSInteger)resultLimit + matchedItemHandler:(void (^)(NSArray *items))handleMatchedItems +{ + // Search documentation tags and provide the results in a pair of (file + // name, tag name). Currently lazily parse the Vim's doc tags, and reuse + // that in future searches. + // + // Does not support plugins for now, as different Vim instances could have + // different plugins loaded. Theoretically it's possible to query the + // current Vim instance for what plugins are loaded and the tags associated + // with them but it's tricky especially since this function is not invoked + // on the main thread. Just providing Vim's builtin docs should be mostly + // good enough. + + static BOOL parsed = NO; + static NSMutableArray *parsedLineComponents = nil; + + @synchronized (self) { + if (!parsed) { + parsedLineComponents = [[NSMutableArray alloc]init]; + + NSString *tagsFilePath = [[[NSBundle mainBundle] resourcePath] + stringByAppendingPathComponent:@"vim/runtime/doc/tags"]; + NSString *fileContent = [NSString stringWithContentsOfFile:tagsFilePath encoding:NSUTF8StringEncoding error:NULL]; + NSArray *lines = [fileContent componentsSeparatedByString:@"\n"]; + + for (NSString *line in lines) { + NSArray *components = [line componentsSeparatedByString:@"\t"]; + if ([components count] < 2) { + continue; + } + [parsedLineComponents addObject:components]; + } + + parsed = YES; + } + } + + // Use a simple search algorithm where the string is split by whitespace and each word has to match + // substring in the tag. Don't do fuzzy matching or regex for simplicity for now. + NSArray *searchStrings = [searchString componentsSeparatedByString:@" "]; + + NSMutableArray *ret = [[[NSMutableArray alloc]init] autorelease]; + for (NSArray *line in parsedLineComponents) { + BOOL found = YES; + for (NSString *curSearchString in searchStrings) { + if (![line[0] localizedCaseInsensitiveContainsString:curSearchString]) { + found = NO; + break; + } + } + if (found) { + // We flip the ordering because we want it to look like "file_name.txt > tag_name" in the results. + NSArray *foundObject = @[line[1], line[0]]; + + if ([searchStrings count] == 1 && [searchString localizedCaseInsensitiveCompare:line[0]] == NSOrderedSame) { + // Exact match has highest priority. + [ret insertObject:foundObject atIndex:0]; + } + else { + // Don't do any other prioritization for now. May add more sophisticated sorting/heuristics + // in the future. + [ret addObject:foundObject]; + } + } + } + + // Return the results to callback. + handleMatchedItems(ret); +} + +- (void)performActionForItem:(id)item +{ + // When opening a help page, either open a new Vim instance, or reuse the + // existing one. + MMVimController *vimController = [self keyVimController]; + if (vimController == nil) { + [self showVimHelp:self withCmd:[NSString stringWithFormat: + @":help %@", item[1]]]; + return; + } + [vimController addVimInput:[NSString stringWithFormat: + @":help %@", item[1]]]; +} +// End NSUserInterfaceItemSearching + @end // MMAppController diff --git a/src/MacVim/Miscellaneous.h b/src/MacVim/Miscellaneous.h index 4d0d17e88d..176378e043 100644 --- a/src/MacVim/Miscellaneous.h +++ b/src/MacVim/Miscellaneous.h @@ -126,6 +126,7 @@ enum { - (NSMenu *)findApplicationMenu; - (NSMenu *)findServicesMenu; - (NSMenu *)findFileMenu; +- (NSMenu *)findHelpMenu; @end diff --git a/src/MacVim/Miscellaneous.m b/src/MacVim/Miscellaneous.m index 415c0d8337..93f4586d7f 100644 --- a/src/MacVim/Miscellaneous.m +++ b/src/MacVim/Miscellaneous.m @@ -178,6 +178,11 @@ - (NSMenu *)findFileMenu return [self findMenuContainingItemWithAction:@selector(performClose:)]; } +- (NSMenu *)findHelpMenu +{ + return [self findMenuContainingItemWithAction:@selector(openWebsite:)]; +} + @end // NSMenu (MMExtras)