<commit_before>//
//  InvisibleFormatter.swift
//
//  CotEditor
//  https://coteditor.com
//
//  Created by 1024jp on 2018-04-01.
//
//  ---------------------------------------------------------------------------
//
//  © 2018 1024jp
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  https://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

import Foundation
import AppKit.NSColor

final class InvisibleFormatter: Formatter {
    
    // MARK: Properties
    
    var invisibles: [Invisible] = [.newLine, .tab, .fullwidthSpace]
    
    
    
    // MARK: -
    // MARK: Formatter Function
    
    /// convert to plain string
    override func string(for obj: Any?) -> String? {
        
        return obj as? String
    }
    
    
    /// make invisible characters visible
    override func attributedString(for obj: Any, withDefaultAttributes attrs: [NSAttributedString.Key: Any]? = nil) -> NSAttributedString? {
        
        guard let string = self.string(for: obj) else { return nil }
        
        let attributedString = NSMutableAttributedString(string: string, attributes: attrs)
        let attributes: [NSAttributedString.Key: Any] = [.foregroundColor: NSColor.tertiaryLabelColor]
        
        for (index, codeUnit) in string.utf16.enumerated() {
            guard
                let invisible = Invisible(codeUnit: codeUnit),
                self.invisibles.contains(invisible)
                else { continue }
            
            let range = NSRange(location: index, length: 1)
            attributedString.replaceCharacters(in: range, with: NSAttributedString(string: invisible.usedSymbol, attributes: attributes))
        }
        
        return attributedString
    }
    
    
    /// format backwards<commit_msg>Refactor InvisibleFormatter<commit_after>//
//  InvisibleFormatter.swift
//
//  CotEditor
//  https://coteditor.com
//
//  Created by 1024jp on 2018-04-01.
//
//  ---------------------------------------------------------------------------
//
//  © 2018 1024jp
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  https://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

import Foundation
import AppKit.NSColor

final class InvisibleFormatter: Formatter {
    
    // MARK: Properties
    
    private let invisibles: [Invisible] = [.newLine, .tab, .fullwidthSpace]
    
    
    
    // MARK: -
    // MARK: Formatter Function
    
    /// convert to plain string
    override func string(for obj: Any?) -> String? {
        
        return obj as? String
    }
    
    
    /// make invisible characters visible
    override func attributedString(for obj: Any, withDefaultAttributes attrs: [NSAttributedString.Key: Any]? = nil) -> NSAttributedString? {
        
        guard let string = self.string(for: obj) else { return nil }
        
        let attributedString = NSMutableAttributedString(string: string, attributes: attrs)
        let attributes = (attrs ?? [:]).merging([.foregroundColor: NSColor.tertiaryLabelColor]) { $1 }
        
        for (index, codeUnit) in string.utf16.enumerated() {
            guard
                let invisible = Invisible(codeUnit: codeUnit),
                self.invisibles.contains(invisible)
                else { continue }
            
            let attributedInvisible = NSAttributedString(string: invisible.usedSymbol, attributes: attributes)
            attributedString.replaceCharacters(in: NSRange(index...index), with: attributedInvisible)
        }
        
        return attributedString
    }
    
    
    /// format backwards
{'fraction_non_alphanumeric': 0.12732571172382873, 'fraction_numerical': 0.0121049092131809, 'mean_word_length': 2.0730027548209367, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 4, 'lorem ipsum': 0, 'www.': 2, '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': '5096109', 'n_tokens_mistral': 1221, 'n_tokens_neox': 1165, 'n_words': 376}