Skip to content

Commit ebc61dd

Browse files
authored
Merge pull request swiftlang#1378 from eyeplum/attributed-string
2 parents b39800c + fba70bb commit ebc61dd

File tree

2 files changed

+463
-23
lines changed

2 files changed

+463
-23
lines changed

Foundation/NSAttributedString.swift

Lines changed: 100 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,15 @@ open class NSAttributedString: NSObject, NSCopying, NSMutableCopying, NSSecureCo
4848
}
4949

5050
open func copy(with zone: NSZone? = nil) -> Any {
51-
NSUnimplemented()
51+
return self
5252
}
5353

5454
open override func mutableCopy() -> Any {
5555
return mutableCopy(with: nil)
5656
}
5757

5858
open func mutableCopy(with zone: NSZone? = nil) -> Any {
59-
NSUnimplemented()
59+
return NSMutableAttributedString(attributedString: self)
6060
}
6161

6262
/// The character contents of the receiver as an NSString object.
@@ -88,7 +88,10 @@ open class NSAttributedString: NSObject, NSCopying, NSMutableCopying, NSSecureCo
8888
}
8989

9090
/// Returns an NSAttributedString object consisting of the characters and attributes within a given range in the receiver.
91-
open func attributedSubstring(from range: NSRange) -> NSAttributedString { NSUnimplemented() }
91+
open func attributedSubstring(from range: NSRange) -> NSAttributedString {
92+
let attributedSubstring = CFAttributedStringCreateWithSubstring(kCFAllocatorDefault, _cfObject, CFRange(range))
93+
return unsafeBitCast(attributedSubstring, to: NSAttributedString.self)
94+
}
9295

9396
/// Returns the attributes for the character at a given index, and by reference the range over which the attributes apply.
9497
open func attributes(at location: Int, longestEffectiveRange range: NSRangePointer?, in rangeLimit: NSRange) -> [NSAttributedStringKey : Any] {
@@ -109,7 +112,17 @@ open class NSAttributedString: NSObject, NSCopying, NSMutableCopying, NSSecureCo
109112
}
110113

111114
/// Returns a Boolean value that indicates whether the receiver is equal to another given attributed string.
112-
open func isEqual(to other: NSAttributedString) -> Bool { NSUnimplemented() }
115+
open func isEqual(to other: NSAttributedString) -> Bool {
116+
guard let runtimeClass = _CFRuntimeGetClassWithTypeID(CFAttributedStringGetTypeID()) else {
117+
fatalError("Could not obtain CFRuntimeClass of CFAttributedString")
118+
}
119+
120+
guard let equalFunction = runtimeClass.pointee.equal else {
121+
fatalError("Could not obtain equal function from CFRuntimeClass of CFAttributedString")
122+
}
123+
124+
return equalFunction(_cfObject, other._cfObject) == true
125+
}
113126

114127
/// Returns an NSAttributedString object initialized with the characters of a given string and no attribute information.
115128
public init(string: String) {
@@ -131,7 +144,13 @@ open class NSAttributedString: NSObject, NSCopying, NSMutableCopying, NSSecureCo
131144

132145
/// Returns an NSAttributedString object initialized with the characters and attributes of another given attributed string.
133146
public init(attributedString: NSAttributedString) {
134-
NSUnimplemented()
147+
// create an empty mutable attr string then immediately replace all of its contents
148+
let mutableAttributedString = NSMutableAttributedString(string: "")
149+
mutableAttributedString.setAttributedString(attributedString)
150+
151+
// use the resulting _string and _attributeArray to initialize a new instance
152+
_string = mutableAttributedString._string
153+
_attributeArray = mutableAttributedString._attributeArray
135154
}
136155

137156
/// Executes the block for each attribute in the range.
@@ -306,8 +325,17 @@ extension NSAttributedString {
306325

307326
open class NSMutableAttributedString : NSAttributedString {
308327

309-
open func replaceCharacters(in range: NSRange, with str: String) { NSUnimplemented() }
310-
open func setAttributes(_ attrs: [NSAttributedStringKey : Any]?, range: NSRange) { NSUnimplemented() }
328+
open func replaceCharacters(in range: NSRange, with str: String) {
329+
CFAttributedStringReplaceString(_cfMutableObject, CFRange(range), str._cfObject)
330+
}
331+
332+
open func setAttributes(_ attrs: [NSAttributedStringKey : Any]?, range: NSRange) {
333+
guard let attrs = attrs else {
334+
CFAttributedStringSetAttributes(_cfMutableObject, CFRange(range), nil, true)
335+
return
336+
}
337+
CFAttributedStringSetAttributes(_cfMutableObject, CFRange(range), attributesCFDictionary(from: attrs), true)
338+
}
311339

312340
open var mutableString: NSMutableString {
313341
return _string as! NSMutableString
@@ -317,22 +345,64 @@ open class NSMutableAttributedString : NSAttributedString {
317345
CFAttributedStringSetAttribute(_cfMutableObject, CFRange(range), name.rawValue._cfObject, _SwiftValue.store(value))
318346
}
319347

320-
open func addAttributes(_ attrs: [NSAttributedStringKey : Any], range: NSRange) { NSUnimplemented() }
348+
open func addAttributes(_ attrs: [NSAttributedStringKey : Any], range: NSRange) {
349+
CFAttributedStringSetAttributes(_cfMutableObject, CFRange(range), attributesCFDictionary(from: attrs), false)
350+
}
351+
352+
open func removeAttribute(_ name: NSAttributedStringKey, range: NSRange) {
353+
CFAttributedStringRemoveAttribute(_cfMutableObject, CFRange(range), name.rawValue._cfObject)
354+
}
355+
356+
open func replaceCharacters(in range: NSRange, with attrString: NSAttributedString) {
357+
CFAttributedStringReplaceAttributedString(_cfMutableObject, CFRange(range), attrString._cfObject)
358+
}
321359

322-
open func removeAttribute(_ name: NSAttributedStringKey, range: NSRange) { NSUnimplemented() }
360+
open func insert(_ attrString: NSAttributedString, at loc: Int) {
361+
let insertRange = NSRange(location: loc, length: 0)
362+
replaceCharacters(in: insertRange, with: attrString)
363+
}
364+
365+
open func append(_ attrString: NSAttributedString) {
366+
let appendRange = NSRange(location: length, length: 0)
367+
replaceCharacters(in: appendRange, with: attrString)
368+
}
369+
370+
open func deleteCharacters(in range: NSRange) {
371+
// To delete a range of the attributed string, call CFAttributedStringReplaceString() with empty string and specified range
372+
let emptyString = ""._cfObject
373+
CFAttributedStringReplaceString(_cfMutableObject, CFRange(range), emptyString)
374+
}
375+
376+
open func setAttributedString(_ attrString: NSAttributedString) {
377+
let fullStringRange = NSRange(location: 0, length: length)
378+
replaceCharacters(in: fullStringRange, with: attrString)
379+
}
380+
381+
open func beginEditing() {
382+
CFAttributedStringBeginEditing(_cfMutableObject)
383+
}
384+
385+
open func endEditing() {
386+
CFAttributedStringEndEditing(_cfMutableObject)
387+
}
323388

324-
open func replaceCharacters(in range: NSRange, with attrString: NSAttributedString) { NSUnimplemented() }
325-
open func insert(_ attrString: NSAttributedString, at loc: Int) { NSUnimplemented() }
326-
open func append(_ attrString: NSAttributedString) { NSUnimplemented() }
327-
open func deleteCharacters(in range: NSRange) { NSUnimplemented() }
328-
open func setAttributedString(_ attrString: NSAttributedString) { NSUnimplemented() }
389+
open override func copy(with zone: NSZone? = nil) -> Any {
390+
return NSAttributedString(attributedString: self)
391+
}
329392

330-
open func beginEditing() { NSUnimplemented() }
331-
open func endEditing() { NSUnimplemented() }
393+
public override init(string: String) {
394+
super.init(string: string)
395+
_string = NSMutableString(string: string)
396+
}
397+
398+
public override init(string: String, attributes attrs: [NSAttributedStringKey : Any]? = nil) {
399+
super.init(string: string, attributes: attrs)
400+
_string = NSMutableString(string: string)
401+
}
332402

333-
public override init(string str: String) {
334-
super.init(string: str)
335-
_string = NSMutableString(string: str)
403+
public override init(attributedString: NSAttributedString) {
404+
super.init(attributedString: attributedString)
405+
_string = NSMutableString(string: attributedString.string)
336406
}
337407

338408
public required init?(coder aDecoder: NSCoder) {
@@ -344,3 +414,14 @@ open class NSMutableAttributedString : NSAttributedString {
344414
extension NSMutableAttributedString {
345415
internal var _cfMutableObject: CFMutableAttributedString { return unsafeBitCast(self, to: CFMutableAttributedString.self) }
346416
}
417+
418+
private extension NSMutableAttributedString {
419+
420+
func attributesCFDictionary(from attrs: [NSAttributedStringKey : Any]) -> CFDictionary {
421+
var attributesDictionary = [String : Any]()
422+
for (key, value) in attrs {
423+
attributesDictionary[key.rawValue] = value
424+
}
425+
return attributesDictionary._cfObject
426+
}
427+
}

0 commit comments

Comments
 (0)