mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
255 lines
6.9 KiB
Plaintext
255 lines
6.9 KiB
Plaintext
//
|
|
// ASMutableAttributedStringBuilder.mm
|
|
// Texture
|
|
//
|
|
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
|
|
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
|
|
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
|
|
#import <AsyncDisplayKit/ASMutableAttributedStringBuilder.h>
|
|
|
|
@implementation ASMutableAttributedStringBuilder {
|
|
// Flag for the type of the current transaction (set or add)
|
|
BOOL _setRange;
|
|
// The range over which the currently pending transaction will occur
|
|
NSRange _pendingRange;
|
|
// The actual attribute dictionary that is being composed
|
|
NSMutableDictionary *_pendingRangeAttributes;
|
|
NSMutableAttributedString *_attrStr;
|
|
|
|
// We delay initialization of the _attrStr until we need to
|
|
NSString *_initString;
|
|
}
|
|
|
|
- (instancetype)init
|
|
{
|
|
if (self = [super init]) {
|
|
_attrStr = [[NSMutableAttributedString alloc] init];
|
|
_pendingRange.location = NSNotFound;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initWithString:(NSString *)str
|
|
{
|
|
return [self initWithString:str attributes:@{}];
|
|
}
|
|
|
|
- (instancetype)initWithString:(NSString *)str attributes:(NSDictionary *)attrs
|
|
{
|
|
if (self = [super init]) {
|
|
// We cache this in an ivar that we can lazily construct the attributed
|
|
// string with when we get to a forced commit point.
|
|
_initString = str;
|
|
// Triggers a creation of the _pendingRangeAttributes dictionary which then
|
|
// is filled with entries from the given attrs dict.
|
|
[[self _pendingRangeAttributes] addEntriesFromDictionary:attrs];
|
|
_setRange = NO;
|
|
_pendingRange = NSMakeRange(0, _initString.length);
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initWithAttributedString:(NSAttributedString *)attrStr
|
|
{
|
|
if (self = [super init]) {
|
|
_attrStr = [[NSMutableAttributedString alloc] initWithAttributedString:attrStr];
|
|
_pendingRange.location = NSNotFound;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (NSMutableAttributedString *)_attributedString
|
|
{
|
|
if (_attrStr == nil && _initString != nil) {
|
|
// We can lazily construct the attributed string if it hasn't already been
|
|
// created with the existing pending attributes. This is significantly
|
|
// faster if more attributes are added after initializing this instance
|
|
// and the new attributions are for the entire string anyway.
|
|
_attrStr = [[NSMutableAttributedString alloc] initWithString:_initString attributes:_pendingRangeAttributes];
|
|
_pendingRangeAttributes = nil;
|
|
_pendingRange.location = NSNotFound;
|
|
_initString = nil;
|
|
}
|
|
|
|
return _attrStr;
|
|
}
|
|
|
|
#pragma mark - Pending attribution
|
|
|
|
- (NSMutableDictionary *)_pendingRangeAttributes
|
|
{
|
|
// Lazy dictionary creation. Call this if you want to force initialization,
|
|
// otherwise just use the ivar.
|
|
if (_pendingRangeAttributes == nil) {
|
|
_pendingRangeAttributes = [[NSMutableDictionary alloc] init];
|
|
}
|
|
return _pendingRangeAttributes;
|
|
}
|
|
|
|
- (void)_applyPendingRangeAttributions
|
|
{
|
|
if (_attrStr == nil) {
|
|
// Trigger its creation if it doesn't exist.
|
|
[self _attributedString];
|
|
}
|
|
|
|
if (_pendingRangeAttributes.count == 0) {
|
|
return;
|
|
}
|
|
|
|
if (_pendingRange.location == NSNotFound) {
|
|
return;
|
|
}
|
|
|
|
if (_setRange) {
|
|
[[self _attributedString] setAttributes:_pendingRangeAttributes range:_pendingRange];
|
|
} else {
|
|
[[self _attributedString] addAttributes:_pendingRangeAttributes range:_pendingRange];
|
|
}
|
|
_pendingRangeAttributes = nil;
|
|
_pendingRange.location = NSNotFound;
|
|
}
|
|
|
|
#pragma mark - Editing
|
|
|
|
- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str
|
|
{
|
|
[self _applyPendingRangeAttributions];
|
|
[[self _attributedString] replaceCharactersInRange:range withString:str];
|
|
}
|
|
|
|
- (void)replaceCharactersInRange:(NSRange)range withAttributedString:(NSAttributedString *)attrString
|
|
{
|
|
[self _applyPendingRangeAttributions];
|
|
[[self _attributedString] replaceCharactersInRange:range withAttributedString:attrString];
|
|
}
|
|
|
|
- (void)addAttribute:(NSString *)name value:(id)value range:(NSRange)range
|
|
{
|
|
if (_setRange) {
|
|
[self _applyPendingRangeAttributions];
|
|
_setRange = NO;
|
|
}
|
|
|
|
if (!NSEqualRanges(_pendingRange, range)) {
|
|
[self _applyPendingRangeAttributions];
|
|
_pendingRange = range;
|
|
}
|
|
|
|
NSMutableDictionary *pendingAttributes = [self _pendingRangeAttributes];
|
|
pendingAttributes[name] = value;
|
|
}
|
|
|
|
- (void)addAttributes:(NSDictionary *)attrs range:(NSRange)range
|
|
{
|
|
if (_setRange) {
|
|
[self _applyPendingRangeAttributions];
|
|
_setRange = NO;
|
|
}
|
|
|
|
if (!NSEqualRanges(_pendingRange, range)) {
|
|
[self _applyPendingRangeAttributions];
|
|
_pendingRange = range;
|
|
}
|
|
|
|
NSMutableDictionary *pendingAttributes = [self _pendingRangeAttributes];
|
|
[pendingAttributes addEntriesFromDictionary:attrs];
|
|
}
|
|
|
|
- (void)insertAttributedString:(NSAttributedString *)attrString atIndex:(NSUInteger)loc
|
|
{
|
|
[self _applyPendingRangeAttributions];
|
|
[[self _attributedString] insertAttributedString:attrString atIndex:loc];
|
|
}
|
|
|
|
- (void)appendAttributedString:(NSAttributedString *)attrString
|
|
{
|
|
[self _applyPendingRangeAttributions];
|
|
[[self _attributedString] appendAttributedString:attrString];
|
|
}
|
|
|
|
- (void)deleteCharactersInRange:(NSRange)range
|
|
{
|
|
[self _applyPendingRangeAttributions];
|
|
[[self _attributedString] deleteCharactersInRange:range];
|
|
}
|
|
|
|
- (void)setAttributedString:(NSAttributedString *)attrString
|
|
{
|
|
[self _applyPendingRangeAttributions];
|
|
[[self _attributedString] setAttributedString:attrString];
|
|
}
|
|
|
|
- (void)setAttributes:(NSDictionary *)attrs range:(NSRange)range
|
|
{
|
|
if (!_setRange) {
|
|
[self _applyPendingRangeAttributions];
|
|
_setRange = YES;
|
|
}
|
|
|
|
if (!NSEqualRanges(_pendingRange, range)) {
|
|
[self _applyPendingRangeAttributions];
|
|
_pendingRange = range;
|
|
}
|
|
|
|
NSMutableDictionary *pendingAttributes = [self _pendingRangeAttributes];
|
|
[pendingAttributes addEntriesFromDictionary:attrs];
|
|
}
|
|
|
|
- (void)removeAttribute:(NSString *)name range:(NSRange)range
|
|
{
|
|
// This call looks like the other set/add functions, but in order for this
|
|
// function to perform as advertised we MUST first add the attributes we
|
|
// currently have pending.
|
|
[self _applyPendingRangeAttributions];
|
|
|
|
[[self _attributedString] removeAttribute:name range:range];
|
|
}
|
|
|
|
#pragma mark - Output
|
|
|
|
- (NSMutableAttributedString *)composedAttributedString
|
|
{
|
|
if (_pendingRangeAttributes.count > 0) {
|
|
[self _applyPendingRangeAttributions];
|
|
}
|
|
return [self _attributedString];
|
|
}
|
|
|
|
#pragma mark - Forwarding
|
|
|
|
- (NSUInteger)length
|
|
{
|
|
// If we just want a length call, no need to lazily construct the attributed string
|
|
return _attrStr ? _attrStr.length : _initString.length;
|
|
}
|
|
|
|
- (NSDictionary *)attributesAtIndex:(NSUInteger)location effectiveRange:(NSRangePointer)range
|
|
{
|
|
return [[self _attributedString] attributesAtIndex:location effectiveRange:range];
|
|
}
|
|
|
|
- (NSString *)string
|
|
{
|
|
return _attrStr ? _attrStr.string : _initString;
|
|
}
|
|
|
|
- (NSMutableString *)mutableString
|
|
{
|
|
return [[self _attributedString] mutableString];
|
|
}
|
|
|
|
- (void)beginEditing
|
|
{
|
|
[[self _attributedString] beginEditing];
|
|
}
|
|
|
|
- (void)endEditing
|
|
{
|
|
[[self _attributedString] endEditing];
|
|
}
|
|
|
|
@end
|