Vitaliy Vashchenko Vitaliy Vashchenko - 7 months ago 114
Swift Question

NSTextStorage paragraph numbering

Is it possible to get paragraph number from

NSTextStorage
? It has property called paragraphs which is
NSArray
, but it's not clear how to deal with an individual objects in this array.

I am looking for some way to number each paragraph in storage, so I could find out in which one changes are made.

I was thinking to mark paragraphs with their ranges but the problem is that if I add or delete characters in one paragraph, all the ranges of the next paragraphs became invalid (they simply will change).

Ideally it will be great if I find a solution to get paragraph number via its range, so it wouldn't be necessary to iterate each paragraph. Here's code for getting range of edited paragraph:

let paragraphRange = textStorage.string.bridgeToObjectiveC().paragraphRangeForRange(editedRange)


But how can I use it to find out exact number of the paragraph in array? I can't just compare strings because user can type two or more identical strings.

UPDATE:
the only idea I came up with is using the whole bunch of NSTextViews, each for a single paragraph. Have no idea how else I could track changes in a particular paragraph. The only thing concerns me is if my document will have, let's say, 1000 pages or more, can I hope that such a construction of NSTextView series won't slow down the runtime?

Answer

I ended up with final solution. I just made this function inside my NSTextStorage subclass:

func paragraphRanges() -> [NSRange] {
    let storageString = self.storage.string as NSString
    let storageRange = NSMakeRange(0, storageString.length)
    var iteration = 0
    var paragraphRanges = [NSRange]()

    paragraphRanges.append(NSMakeRange(0, 0))
    storageString.enumerateSubstringsInRange(storageRange, options: NSStringEnumerationOptions.ByComposedCharacterSequences) { (substring, substringRange, enclosingRange, stop) -> () in
        paragraphRanges[iteration].length += 1

        let newLineSet = NSCharacterSet.newlineCharacterSet()
        let scanner = NSScanner(string: substring!)
        let notFound = scanner.scanUpToCharactersFromSet(newLineSet, intoString: nil)
        if !notFound && substring != " " {
            paragraphRanges.append(NSMakeRange(substringRange.location + 1, 0))
            iteration += 1
        }

    }

    return paragraphRanges
}

And that's it! Now I got an array of ranges for every paragraph in the text storage. And I can access them via simple integer index.