NSTextView replaceCharactersInRange: withString: similar to toChangeTextInRanges: replacementStrings:

I have a rather complicated subclass of NSTextView in one of my projects. I am currently working on search / replace for working with the built-in search panel (e.g. Safari, Xcode) and want to properly support undo / redo for replace operations.

I want the Replace All command to support cancellation as a single command (that is, if there are 8 substitutions in the text view, it should also cancel these 8 substitutions immediately).

I am wondering if there should be an instance of shouldChangeTextInRanges:replaceStrings: which I can call after checking to replace. I was expecting to replaceCharactersInRanges:withStrings: or something similar, but it doesn't seem to exist.

The only way I can do it now is to first check with a call to shouldChangeTextInRanges:replaceStrings: then call replaceCharactersInRange:withString: with the entire range of the text view and a new line (with the changes made) as the second argument.

It just seems unnecessary, I really don't want to replace the entire line if I don't need it. Any ideas?

+4
source share
2 answers

After some messing around, I think I figured it out. Josh, I used your offer to start. I'm not sure if you edited your sentence or simply deleted it, but it left, so I cannot quote it in your answer.

In any case, you need to shift the ranges that you are going to replace after each call to replaceCharactersInRange:withString: otherwise bad things will happen, because the ranges do not match. Here is what I ended up with:

 // array of NSValue objects storing an NSRange NSArray *replaceRanges = [self replaceRanges]; NSString *replaceString = [self replaceString]; // array of NSString objects you are going to use for the replace operation, just replaceString repeated [replaceRanges count] times NSArray *replaceStrings = [self replaceStrings]; NSTextView *textView = [self textView]; // the amount we have to shift subequent replace ranges after each call to replaceCharactersInRange:withString: NSInteger locationShift = 0; // check to makes sure the replace can occur if ([textView shouldChangeTextInRanges:replaceRanges replacementStrings:replaceStrings]) { // we want to treat all these replacements as a single replacement for undo purposes [[textView textStorage] beginEditing]; for (NSValue *rangeValue in replaceRanges) { NSRange range = [rangeValue rangeValue]; // replace the range shifted by locationShift with our replaceString [[textView textStorage] replaceCharactersInRange:NSMakeRange(range.location + locationShift, range.length) withString:replaceString]; // update the shift amount, which is the difference between our replaced string length and the original match length locationShift += [replaceString length] - range.length; } // end the grouping operation [[textView textStorage] endEditing]; } 

This works great and supports cancellation, as expected, canceling this operation will result in all replacements being canceled immediately.

Anyway, thanks to Josh, as his answer made me point in the right direction.

+4
source

I am surprised that this is not grouped automatically. However, you can perform manual undo grouping; You will have to configure the reverse actions yourself. We hope this points you in the right direction:

 - (BOOL)shouldChangeTextInRanges:(NSArray *)affectedRanges replacementStrings:(NSArray *)replacementStrings { NSUndoManager * undoMan = [self undoManager]; [undoMan beginUndoGrouping]; NSEnumerator stringEnumerator = [replacementStrings objectEnumerator]; for( NSRange thisRange in affectedRanges ){ NSString * thisString = [stringEnumerator nextObject]; NSTextStorage * textStore = [self textStorage]; [[undoMan prepareWithInvocationTarget:textStore] replaceCharactersInRange:thisRange withString:[textStore attributedSubstringFromRange:thisRange]]; [textStore replaceCharactersInRange:thisRange withString:thisString]; } [undoMan endUndoGrouping]; [undoMan setActionName:@"Replace All"]; return NO; // because we just did it by hand } 
0
source

Source: https://habr.com/ru/post/1346977/


All Articles