From b80f8b6798913f9e8f62a742e27be36841eaab2a Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Wed, 9 Feb 2022 00:56:41 +0800 Subject: [PATCH] PhraseEditor // Auto Consolidate Data on Save / Load. - We disabled the sort function at this moment, considering that users may prefer to keep their own customized sorting preferences. - To do: Auto-Reload editor content by monitoring the file stream changes. UserPhraseEditor // Auto Consolidate Data on Load. --- UserPhraseEditor/Document.swift | 10 ++++- UserPhraseEditor/StringExtension.swift | 58 ++++++++++++++++++++++++++ vChewing.xcodeproj/project.pbxproj | 4 ++ 3 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 UserPhraseEditor/StringExtension.swift diff --git a/UserPhraseEditor/Document.swift b/UserPhraseEditor/Document.swift index 9aaef1d5..6d6563d1 100644 --- a/UserPhraseEditor/Document.swift +++ b/UserPhraseEditor/Document.swift @@ -68,12 +68,18 @@ class Document: NSDocument { /// - Tag: readExample override func read(from data: Data, ofType typeName: String) throws { - content.read(from: data) + var strToDealWith = String(decoding: data, as: UTF8.self) + strToDealWith.formatConsolidate() + let processedIncomingData = Data(strToDealWith.utf8) + content.read(from: processedIncomingData) } /// - Tag: writeExample override func data(ofType typeName: String) throws -> Data { - return content.data()! + var strToDealWith = content.contentString + strToDealWith.formatConsolidate() + let outputData = Data(strToDealWith.utf8) + return outputData } // MARK: - Printing diff --git a/UserPhraseEditor/StringExtension.swift b/UserPhraseEditor/StringExtension.swift new file mode 100644 index 00000000..07ba51fc --- /dev/null +++ b/UserPhraseEditor/StringExtension.swift @@ -0,0 +1,58 @@ +// Copyright (c) 2021 and onwards The vChewing Project (MIT-NTL License). +/* +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and +to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +1. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +2. No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, + except as required to fulfill notice requirements above. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +import Foundation + +extension String { + mutating func removingRegexMatches(pattern: String, replaceWith: String = "") { + do { + let regex = try NSRegularExpression(pattern: pattern, options: .caseInsensitive) + let range = NSRange(location: 0, length: count) + self = regex.stringByReplacingMatches(in: self, options: [], range: range, withTemplate: replaceWith) + } catch { return } + } + mutating func formatConsolidate() { + // Step 1: Consolidating formats per line. + var arrData = self.components(separatedBy: "\n") + var varLineData = "" + var strProcessed = "" + for lineData in arrData { + varLineData = lineData + varLineData.removingRegexMatches(pattern: " ", replaceWith: " ") // CJKWhiteSpace to ASCIISpace + varLineData.removingRegexMatches(pattern: " ", replaceWith: " ") // NonBreakWhiteSpace to ASCIISpace + varLineData.removingRegexMatches(pattern: "\\s+", replaceWith: " ") // Consolidating Consecutive Spaves + varLineData.removingRegexMatches(pattern: "^\\s", replaceWith: "") // Trim Leading Space + varLineData.removingRegexMatches(pattern: "\\s$", replaceWith: "") // Trim Trailing Space + strProcessed += varLineData + strProcessed += "\n" + } + + // Step 2: Deduplication. + arrData = strProcessed.components(separatedBy: "\n") + strProcessed = "" // Reset its value + let arrDataDeduplicated = Array(NSOrderedSet(array: arrData).array as! [String]) + for lineData in arrDataDeduplicated { + strProcessed += lineData + strProcessed += "\n" + } + + // Step 3: Remove duplicated newlines at the end of the file. + strProcessed.removingRegexMatches(pattern: "\\n\\n", replaceWith: "\n") + self = strProcessed + } +} diff --git a/vChewing.xcodeproj/project.pbxproj b/vChewing.xcodeproj/project.pbxproj index 2082859f..5cd3806d 100644 --- a/vChewing.xcodeproj/project.pbxproj +++ b/vChewing.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 5B0AF8B527B2C8290096FE54 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0AF8B427B2C8290096FE54 /* StringExtension.swift */; }; 5B2DB16F27AF6891006D874E /* data-chs.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5B2DB16D27AF6891006D874E /* data-chs.txt */; }; 5B2DB17027AF6891006D874E /* data-cht.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5B2DB16E27AF6891006D874E /* data-cht.txt */; }; 5B62A31727AE73A700A19448 /* unzip.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A30927AE73A700A19448 /* unzip.m */; }; @@ -112,6 +113,7 @@ 5B05A47B27AFF7CA00437698 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 5B05A47C27AFF7CF00437698 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 5B05A47F27AFF84200437698 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/frmAboutWindow.strings; sourceTree = ""; }; + 5B0AF8B427B2C8290096FE54 /* StringExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = ""; }; 5B0CF37F27AD476E00784B08 /* SwiftyOpenCC */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = SwiftyOpenCC; path = Packages/SwiftyOpenCC; sourceTree = ""; }; 5B2DB16D27AF6891006D874E /* data-chs.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "data-chs.txt"; path = "Data/data-chs.txt"; sourceTree = ""; }; 5B2DB16E27AF6891006D874E /* data-cht.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "data-cht.txt"; path = "Data/data-cht.txt"; sourceTree = ""; }; @@ -532,6 +534,7 @@ 5BD05C6227B2BBEF004C4F1D /* AppDelegate.swift */, 5BD05C6327B2BBEF004C4F1D /* Content.swift */, 5BD05C6127B2BBEF004C4F1D /* Document.swift */, + 5B0AF8B427B2C8290096FE54 /* StringExtension.swift */, 5BD05C6527B2BBEF004C4F1D /* ViewController.swift */, 5BD05C6427B2BBEF004C4F1D /* WindowController.swift */, 5B73FB5427B2BD6900E9BF49 /* PhraseEditor-Info.plist */, @@ -859,6 +862,7 @@ files = ( 5BD05C6A27B2BBEF004C4F1D /* ViewController.swift in Sources */, 5BD05C6727B2BBEF004C4F1D /* AppDelegate.swift in Sources */, + 5B0AF8B527B2C8290096FE54 /* StringExtension.swift in Sources */, 5BD05C6927B2BBEF004C4F1D /* WindowController.swift in Sources */, 5BD05C6627B2BBEF004C4F1D /* Document.swift in Sources */, 5BD05C6827B2BBEF004C4F1D /* Content.swift in Sources */,