diff --git a/Applications/TextMate/resources/English.lproj/MainMenu.xib b/Applications/TextMate/resources/English.lproj/MainMenu.xib index 94bf0515..0138884c 100644 --- a/Applications/TextMate/resources/English.lproj/MainMenu.xib +++ b/Applications/TextMate/resources/English.lproj/MainMenu.xib @@ -948,7 +948,7 @@ Replace & Find - f + g 1572864 2147483647 @@ -2471,7 +2471,7 @@ {269, 102} - {{0, 0}, {1440, 878}} + {{0, 0}, {2560, 1578}} {269, 124} {10000000000000, 10000000000000} Go to Line diff --git a/Frameworks/Find/src/FindWindowController.mm b/Frameworks/Find/src/FindWindowController.mm index a171eea6..c572dd91 100644 --- a/Frameworks/Find/src/FindWindowController.mm +++ b/Frameworks/Find/src/FindWindowController.mm @@ -301,7 +301,6 @@ static NSButton* OakCreateButton (NSString* label, NSBezelStyle bezel = NSRounde self.findAllButton.action = @selector(findAll:); self.replaceAllButton.action = @selector(replaceAll:); self.replaceAndFindButton.action = @selector(replaceAndFind:); - self.replaceAndFindButton.enabled = NO; self.findPreviousButton.action = @selector(findPrevious:); self.findNextButton.action = @selector(findNext:); @@ -321,6 +320,7 @@ static NSButton* OakCreateButton (NSString* label, NSBezelStyle bezel = NSRounde [self.wrapAroundCheckBox bind:NSValueBinding toObject:_objectController withKeyPath:@"content.wrapAround" options:nil]; [self.ignoreWhitespaceCheckBox bind:NSEnabledBinding toObject:_objectController withKeyPath:@"content.canIgnoreWhitespace" options:nil]; [self.statusTextField bind:NSValueBinding toObject:_objectController withKeyPath:@"content.statusString" options:nil]; + [self.replaceAndFindButton bind:NSEnabledBinding toObject:_objectController withKeyPath:@"content.folderSearch" options:@{ NSValueTransformerNameBindingOption: @"NSNegateBoolean" }]; NSView* contentView = self.window.contentView; for(NSView* view in [self.allViews allValues]) diff --git a/Frameworks/OakTextView/src/OakTextView.mm b/Frameworks/OakTextView/src/OakTextView.mm index 5d87fdc9..2c5b08e2 100644 --- a/Frameworks/OakTextView/src/OakTextView.mm +++ b/Frameworks/OakTextView/src/OakTextView.mm @@ -1575,15 +1575,34 @@ static void update_menu_key_equivalents (NSMenu* menu, action_to_key_t const& ac // TODO If aFindServer != self then we should record findWithOptions: instead of find{Next,Previous,All,…}: + find_operation_t findOperation = aFindServer.findOperation; + if(findOperation == kFindOperationReplace || findOperation == kFindOperationReplaceAndFind) + { + std::string replacement = to_s(aFindServer.replaceString); + if(NSDictionary* captures = [OakPasteboard pasteboardWithName:NSReplacePboard].auxiliaryOptionsForCurrent) + { + std::map variables; + for(NSString* key in [captures allKeys]) + variables.insert(std::make_pair(to_s(key), to_s((NSString*)captures[key]))); + replacement = format_string::expand(replacement, variables); + } + editor->insert(replacement, true); + + [self recordSelector:@selector(replace:) withArgument:nil]; + if(findOperation == kFindOperationReplaceAndFind) + findOperation = kFindOperationFind; + } + bool onlyInSelection = false; - switch(aFindServer.findOperation) + switch(findOperation) { case kFindOperationFindInSelection: case kFindOperationCountInSelection: onlyInSelection = editor->has_selection(); case kFindOperationFind: case kFindOperationCount: { - bool isCounting = aFindServer.findOperation == kFindOperationCount || aFindServer.findOperation == kFindOperationCountInSelection; + [OakPasteboard pasteboardWithName:NSReplacePboard].auxiliaryOptionsForCurrent = nil; + bool isCounting = findOperation == kFindOperationCount || findOperation == kFindOperationCountInSelection; std::string const findStr = to_s(aFindServer.findString); find::options_t options = aFindServer.findOptions; @@ -1592,15 +1611,15 @@ static void update_menu_key_equivalents (NSMenu* menu, action_to_key_t const& ac if(documents && [documents count] > 1) options &= ~find::wrap_around; - ng::ranges_t res; - citerate(pair, ng::find(document->buffer(), editor->ranges(), findStr, options, onlyInSelection ? editor->ranges() : ng::ranges_t())) - res.push_back(pair->first); + auto allMatches = ng::find(document->buffer(), editor->ranges(), findStr, options, onlyInSelection ? editor->ranges() : ng::ranges_t()); + ng::ranges_t res; + std::transform(allMatches.begin(), allMatches.end(), std::back_inserter(res), [](decltype(allMatches)::value_type const& p){ return p.first; }); if(onlyInSelection && res.sorted() == editor->ranges().sorted()) { res = ng::ranges_t(); - citerate(pair, ng::find(document->buffer(), editor->ranges(), findStr, options, ng::ranges_t())) - res.push_back(pair->first); + allMatches = ng::find(document->buffer(), editor->ranges(), findStr, options, ng::ranges_t()); + std::transform(allMatches.begin(), allMatches.end(), std::back_inserter(res), [](decltype(allMatches)::value_type const& p){ return p.first; }); } if(res.empty() && !isCounting && documents && [documents count] > 1) @@ -1633,21 +1652,30 @@ static void update_menu_key_equivalents (NSMenu* menu, action_to_key_t const& ac } else { - [self recordSelector:(options & find::all_matches) ? (aFindServer.findOperation == kFindOperationFind ? @selector(findAll:) : @selector(findAllInSelection:)) : ((options & find::backwards) ? @selector(findPrevious:) : @selector(findNext:)) withArgument:nil]; + [self recordSelector:(options & find::all_matches) ? (findOperation == kFindOperationFind ? @selector(findAll:) : @selector(findAllInSelection:)) : ((options & find::backwards) ? @selector(findPrevious:) : @selector(findNext:)) withArgument:nil]; std::set alreadySelected; citerate(range, editor->ranges()) alreadySelected.insert(*range); ng::ranges_t newSelection; - iterate(range, res) + for(auto range : res) { - if(alreadySelected.find(range->sorted()) == alreadySelected.end()) - newSelection.push_back(range->sorted()); + if(alreadySelected.find(range.sorted()) == alreadySelected.end()) + newSelection.push_back(range.sorted()); } if(!res.empty()) + { editor->set_selections(res); + if(res.size() == 1 && (options & find::regular_expression)) + { + NSMutableDictionary* captures = [NSMutableDictionary dictionary]; + for(auto pair : allMatches[res.last()]) + captures[[NSString stringWithCxxString:pair.first]] = [NSString stringWithCxxString:pair.second]; + [OakPasteboard pasteboardWithName:NSReplacePboard].auxiliaryOptionsForCurrent = captures; + } + } [self highlightRanges:newSelection]; [aFindServer didFind:newSelection.size() occurrencesOf:aFindServer.findString atPosition:res.size() == 1 ? document->buffer().convert(res.last().min().index) : text::pos_t::undefined]; @@ -1655,19 +1683,15 @@ static void update_menu_key_equivalents (NSMenu* menu, action_to_key_t const& ac } break; - case kFindOperationReplace: - case kFindOperationReplaceAndFind: - break; - case kFindOperationReplaceAll: case kFindOperationReplaceAllInSelection: { std::string const findStr = to_s(aFindServer.findString); std::string const replaceStr = to_s(aFindServer.replaceString); find::options_t options = aFindServer.findOptions; - [self recordSelector:(options & find::all_matches) ? (aFindServer.findOperation == kFindOperationReplaceAll ? @selector(replaceAll:) : @selector(replaceAllInSelection:)) : @selector(replace:) withArgument:nil]; + [self recordSelector:(options & find::all_matches) ? (findOperation == kFindOperationReplaceAll ? @selector(replaceAll:) : @selector(replaceAllInSelection:)) : @selector(replace:) withArgument:nil]; - ng::ranges_t const res = editor->replace_all(findStr, replaceStr, options, aFindServer.findOperation == kFindOperationReplaceAllInSelection); + ng::ranges_t const res = editor->replace_all(findStr, replaceStr, options, findOperation == kFindOperationReplaceAllInSelection); [aFindServer didReplace:res.size() occurrencesOf:aFindServer.findString with:aFindServer.replaceString]; } break;