123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- //
- // CYRTextStorage.m
- //
- // Version 0.2.0
- //
- // Created by Illya Busigin on 01/05/2014.
- // Copyright (c) 2014 Cyrillian, Inc.
- //
- // Distributed under MIT license.
- // Get the latest version from here:
- //
- // https://github.com/illyabusigin/CYRTextView
- //
- // The MIT License (MIT)
- //
- // Copyright (c) 2014 Cyrillian, Inc.
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy of
- // this software and associated documentation files (the "Software"), to deal in
- // the Software without restriction, including without limitation the rights to
- // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- // the Software, and to permit persons to whom the Software is furnished to do so,
- // subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in all
- // copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- #import "CYRTextStorage.h"
- #import "CYRToken.h"
- @interface CYRTextStorage ()
- @property (nonatomic, strong) NSMutableAttributedString *attributedString;
- @property (nonatomic, strong) NSMutableDictionary *regularExpressionCache;
- @end
- @implementation CYRTextStorage
- #pragma mark - Initialization & Setup
- - (id)init
- {
- if (self = [super init])
- {
- _defaultFont = [UIFont systemFontOfSize:12.0f];
- _attributedString = [NSMutableAttributedString new];
-
- _tokens = @[];
- _regularExpressionCache = @{}.mutableCopy;
- }
-
- return self;
- }
- #pragma mark - Overrides
- - (void)setTokens:(NSMutableArray *)tokens
- {
- _tokens = tokens;
-
- // Clear the regular expression cache
- [self.regularExpressionCache removeAllObjects];
-
- // Redraw all text
- [self update];
- }
- - (NSString *)string
- {
- return [_attributedString string];
- }
- - (NSDictionary *)attributesAtIndex:(NSUInteger)location effectiveRange:(NSRangePointer)range
- {
- return [_attributedString attributesAtIndex:location effectiveRange:range];
- }
- - (void)replaceCharactersInRange:(NSRange)range withString:(NSString*)str
- {
- [self beginEditing];
-
- [_attributedString replaceCharactersInRange:range withString:str];
-
- [self edited:NSTextStorageEditedCharacters | NSTextStorageEditedAttributes range:range changeInLength:str.length - range.length];
- [self endEditing];
- }
- - (void)setAttributes:(NSDictionary*)attrs range:(NSRange)range
- {
- [self beginEditing];
-
- [_attributedString setAttributes:attrs range:range];
-
- [self edited:NSTextStorageEditedAttributes range:range changeInLength:0];
- [self endEditing];
- }
- -(void)processEditing
- {
- [self performReplacementsForRange:[self editedRange]];
- [super processEditing];
- }
- - (void)performReplacementsForRange:(NSRange)changedRange
- {
- NSRange extendedRange = NSUnionRange(changedRange, [[_attributedString string] lineRangeForRange:NSMakeRange(NSMaxRange(changedRange), 0)]);
-
- [self applyStylesToRange:extendedRange];
- }
- -(void)update
- {
- [self addAttributes:@{NSFontAttributeName : self.defaultFont} range:NSMakeRange(0, self.length)];
-
- [self applyStylesToRange:NSMakeRange(0, self.length)];
- }
- - (void)applyStylesToRange:(NSRange)searchRange
- {
- if (self.editedRange.location == NSNotFound)
- {
- return;
- }
-
- NSRange paragaphRange = [self.string paragraphRangeForRange: self.editedRange];
-
- // Reset the text attributes
- [self setAttributes:@{NSForegroundColorAttributeName : [UIColor blackColor]} range:paragaphRange];
-
- [self setAttributes:@{NSFontAttributeName : self.defaultFont} range:paragaphRange];
-
- for (CYRToken *attribute in self.tokens)
- {
- NSRegularExpression *regex = [self expressionForDefinition:attribute.name];
- [regex enumerateMatchesInString:self.string options:0 range:paragaphRange
- usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
-
- [attribute.attributes enumerateKeysAndObjectsUsingBlock:^(NSString *attributeName, id attributeValue, BOOL *stop) {
- [self addAttribute:attributeName value:attributeValue range:result.range];
- }];
- }];
- }
- }
- - (NSRegularExpression *)expressionForDefinition:(NSString *)definition
- {
- __block CYRToken *attribute = nil;
-
- [self.tokens enumerateObjectsUsingBlock:^(CYRToken *enumeratedAttribute, NSUInteger idx, BOOL *stop) {
- if ([enumeratedAttribute.name isEqualToString:definition])
- {
- attribute = enumeratedAttribute;
- *stop = YES;
- }
- }];
-
- NSRegularExpression *expression = self.regularExpressionCache[attribute.expression];
-
- if (!expression)
- {
- expression = [NSRegularExpression regularExpressionWithPattern:attribute.expression
- options:NSRegularExpressionCaseInsensitive error:nil];
-
- [self.regularExpressionCache setObject:expression forKey:definition];
- }
-
- return expression;
- }
- @end
|