<commit_before>import Foundation
import UIKit


// MARK: - A helper for handling the formatting of ordered and unordered lists.
//
class TextListFormatter
{
    /// Creates a new text list, or modifies an existing text list.
    ///
    /// - Parameters
    ///     - ofStyle: The kind of List to apply
    ///     - inString: The NSMutableAttributed string to modify.
    ///     - atRange: The range at which to apply the list style.
    ///
    /// - Returns: An NSRange representing the change to the attributed string.
    ///
    func toggleList(ofStyle style: TextList.Style, inString string: NSMutableAttributedString, atRange range: NSRange) -> NSRange? {
        // Load Paragraph Ranges
        let paragraphRanges = string.paragraphRanges(spanningRange: range)
        guard paragraphRanges.isEmpty == false else {
            return nil
        }

        // 1st Paragraph: No List >> Apply, skipping paragraphs that currently contain TextLists
        guard let listRange = string.rangeOfTextList(atIndex: paragraphRanges[0].location) else {
            let filtered = string.filterListRanges(paragraphRanges, notMatchingStyle: style)
            return applyLists(ofStyle: style, toString: string, atNonContiguousRanges: filtered)
        }

        // 1st Paragraph: Matching List Style >> Remove
        guard let listAttribute = string.textListAttribute(spanningRange: listRange) where listAttribute.style != style else {
            return removeList(fromString: string, atRanges: paragraphRanges)
        }

        // 1st Paragraph: Non Matching Style >> Update!
        let listParagraphs = string.paragraphRanges(spanningRange: listRange)
        return applyList(ofStyle: style, toString: string, atRanges: listParagraphs)
    }
}


// MARK: - Private Helpers
//
private extension TextListFormatter
{
    /// Applies a TextList attribute to the specified non contiguous ranges.
    ///
    /// - Parameters:
    ///     - style: Style of TextList to be applied.
    ///     - string: Target String.
    ///     - ranges: Segments of the receiver string to be transformed into lists.
    ///
    /// - Returns: The affected NSRange, or nil if no changes were made.
    ///
    /// - Notes: The return NSRange, if any, will *INCLUDE* any String Ranges that lie between the non contiguous
    ///   groups. Specifically...
    ///
    ///     [Line 1]  Text                          [Line 1]  1. Text
    ///     [Line 2]  - Unordered List      >>      [Line 2]  - Unordered List
    ///     [Line 3]  More Text                     [Line 3]  1. More Text
    ///     [Line 4]  Something Else                [Line 4]  Something Else
    ///
    ///   Calling ApplyLists over Lines 1-3 should produce the results at the right hand, and lines 1-3 should
    ///   remain selected. Capicci?
    ///
    func applyLists(ofStyle style: TextList.Style, toString string: NSMutableAttributedString, atNonContiguousRanges ranges: [NSRange]) -> NSRange? {
        guard let first = ranges.first, let last = ranges.last else {
            return nil
        }<commit_msg>TextListFormatter: Updates documentation<commit_after>import Foundation
import UIKit


// MARK: - A helper for handling the formatting of ordered and unordered lists.
//
class TextListFormatter
{
    /// Creates a new text list, or modifies an existing text list.
    ///
    /// - Parameters
    ///     - ofStyle: The kind of List to apply
    ///     - inString: The NSMutableAttributed string to modify.
    ///     - atRange: The range at which to apply the list style.
    ///
    /// - Returns: An NSRange representing the change to the attributed string.
    ///
    func toggleList(ofStyle style: TextList.Style, inString string: NSMutableAttributedString, atRange range: NSRange) -> NSRange? {
        // Load Paragraph Ranges
        let paragraphRanges = string.paragraphRanges(spanningRange: range)
        guard paragraphRanges.isEmpty == false else {
            return nil
        }

        // 1st Paragraph: No List >> Apply, skipping paragraphs that currently contain TextLists
        guard let listRange = string.rangeOfTextList(atIndex: paragraphRanges[0].location) else {
            let filtered = string.filterListRanges(paragraphRanges, notMatchingStyle: style)
            return applyLists(ofStyle: style, toString: string, atNonContiguousRanges: filtered)
        }

        // 1st Paragraph: Matching List Style >> Remove
        guard let listAttribute = string.textListAttribute(spanningRange: listRange) where listAttribute.style != style else {
            return removeList(fromString: string, atRanges: paragraphRanges)
        }

        // 1st Paragraph: Non Matching Style >> Update!
        let listParagraphs = string.paragraphRanges(spanningRange: listRange)
        return applyList(ofStyle: style, toString: string, atRanges: listParagraphs)
    }
}


// MARK: - Private Helpers
//
private extension TextListFormatter
{
    /// Applies a TextList attribute to the specified non contiguous ranges.
    ///
    /// - Parameters:
    ///     - style: Style of TextList to be applied.
    ///     - string: Target String.
    ///     - ranges: Segments of the receiver string to be transformed into lists.
    ///
    /// - Returns: The affected NSRange, or nil if no changes were made.
    ///
    /// - Notes: The returned NSRange, if any, will *INCLUDE* any String Ranges that lie between the non contiguous
    ///   groups. Specifically...
    ///
    ///     [Line 1]  Text                          [Line 1]  1. Text
    ///     [Line 2]  - Unordered List      >>      [Line 2]  - Unordered List
    ///     [Line 3]  More Text                     [Line 3]  1. More Text
    ///     [Line 4]  Something Else                [Line 4]  Something Else
    ///
    ///   Calling ApplyLists over Lines 1-3 should produce the results at the right hand, and lines 1-3 should
    ///   remain selected. Capicci?
    ///
    func applyLists(ofStyle style: TextList.Style, toString string: NSMutableAttributedString, atNonContiguousRanges ranges: [NSRange]) -> NSRange? {
        guard let first = ranges.first, let last = ranges.last else {
            return nil
        }
{'fraction_non_alphanumeric': 0.09258361609306835, 'fraction_numerical': 0.0058167716917111, 'mean_word_length': 2.20559295701709, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2764445', 'n_tokens_mistral': 1701, 'n_tokens_neox': 1611, 'n_words': 590}