diff --git a/.clang-format-swift.json b/.clang-format-swift.json deleted file mode 100644 index a4b9cf0a..00000000 --- a/.clang-format-swift.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "fileScopedDeclarationPrivacy" : { - "accessLevel" : "private" - }, - "indentation" : { - "spaces" : 2 - }, - "indentConditionalCompilationBlocks" : true, - "indentSwitchCaseLabels" : true, - "lineBreakAroundMultilineExpressionChainComponents" : false, - "lineBreakBeforeControlFlowKeywords" : false, - "lineBreakBeforeEachArgument" : false, - "lineBreakBeforeEachGenericRequirement" : false, - "lineLength" : 120, - "maximumBlankLines" : 1, - "prioritizeKeepingFunctionOutputTogether" : false, - "respectsExistingLineBreaks" : true, - "rules" : { - "AllPublicDeclarationsHaveDocumentation" : false, - "AlwaysUseLowerCamelCase" : true, - "AmbiguousTrailingClosureOverload" : true, - "BeginDocumentationCommentWithOneLineSummary" : false, - "DoNotUseSemicolons" : true, - "DontRepeatTypeInStaticProperties" : false, - "FileScopedDeclarationPrivacy" : true, - "FullyIndirectEnum" : true, - "GroupNumericLiterals" : true, - "IdentifiersMustBeASCII" : true, - "NeverForceUnwrap" : false, - "NeverUseForceTry" : false, - "NeverUseImplicitlyUnwrappedOptionals" : false, - "NoAccessLevelOnExtensionDeclaration" : true, - "NoBlockComments" : false, - "NoCasesWithOnlyFallthrough" : true, - "NoEmptyTrailingClosureParentheses" : true, - "NoLabelsInCasePatterns" : true, - "NoLeadingUnderscores" : false, - "NoParensAroundConditions" : true, - "NoVoidReturnOnFunctionSignature" : true, - "OneCasePerLine" : true, - "OneVariableDeclarationPerLine" : true, - "OnlyOneTrailingClosureArgument" : false, - "OrderedImports" : true, - "ReturnVoidInsteadOfEmptyTuple" : true, - "UseEarlyExits" : false, - "UseLetInEveryBoundCaseVariable" : false, - "UseShorthandTypeNames" : true, - "UseSingleLinePropertyGetter" : true, - "UseSynthesizedInitializer" : true, - "UseTripleSlashForDocumentationComments" : false, - "UseWhereClausesInForLoops" : false, - "ValidateDocumentationComments" : false - }, - "tabWidth" : 8, - "version" : 1 -} diff --git a/.swiftformat.json b/.swiftformat.json new file mode 100644 index 00000000..1160d8b5 --- /dev/null +++ b/.swiftformat.json @@ -0,0 +1,104 @@ +# SwiftFormat config compliant with Google Swift Guideline +# https://google.github.io/swift/#control-flow-statements + +# Specify version used in a project + +--swiftversion 5.5 + +# Rules explicitly required by the guideline + +--rules \ +blankLinesAroundMark, \ +blankLinesAtEndOfScope, \ +blankLinesAtStartOfScope, \ +blankLinesBetweenScopes, \ +braces, \ +consecutiveBlankLines, \ +consecutiveSpaces, \ +duplicateImports, \ +elseOnSameLine, \ +emptyBraces, \ +enumNamespaces, \ +extensionAccessControl, \ +hoistPatternLet, \ +indent, \ +leadingDelimiters, \ +linebreakAtEndOfFile, \ +markTypes, \ +organizeDeclarations, \ +redundantInit, \ +redundantParens, \ +redundantPattern, \ +redundantRawValues, \ +redundantType, \ +redundantVoidReturnType, \ +semicolons, \ +sortedImports, \ +sortedSwitchCases, \ +spaceAroundBraces, \ +spaceAroundBrackets, \ +spaceAroundComments, \ +spaceAroundGenerics, \ +spaceAroundOperators, \ +spaceAroundParens, \ +spaceInsideBraces, \ +spaceInsideBrackets, \ +spaceInsideComments, \ +spaceInsideGenerics, \ +spaceInsideParens, \ +todos, \ +trailingClosures, \ +trailingCommas, \ +trailingSpace, \ +typeSugar, \ +void, \ +wrap, \ +wrapArguments, \ +wrapAttributes, \ +# +# +# Additional rules not mentioned in the guideline, but helping to keep the codebase clean +# Quoting the guideline: +# Common themes among the rules in this section are: +# avoid redundancy, avoid ambiguity, and prefer implicitness over explicitness +# unless being explicit improves readability and/or reduces ambiguity. +# +# +andOperator, \ +isEmpty, \ +redundantBackticks, \ +redundantBreak, \ +redundantExtensionACL, \ +redundantGet, \ +redundantLetError, \ +redundantNilInit, \ +redundantObjc, \ +redundantReturn, \ +redundantSelf, \ +strongifiedSelf + + +# Options for basic rules + +--extensionacl on-declarations +--funcattributes prev-line +--indent 2 +--maxwidth 100 +--typeattributes prev-line +--varattributes prev-line +--voidtype tuple +--wraparguments before-first +--wrapparameters before-first +--wrapcollections before-first +--wrapreturntype if-multiline +--wrapconditions after-first + +# Option for additional rules + +--self init-only + +# Excluded folders + +--exclude Pods,**/UNTESTED_TODO,vendor,fastlane + +# https://github.com/NoemiRozpara/Google-SwiftFormat-Config \ No newline at end of file diff --git a/BuildVersionSpecifier.swift b/BuildVersionSpecifier.swift index e127c039..6d9f97c1 100755 --- a/BuildVersionSpecifier.swift +++ b/BuildVersionSpecifier.swift @@ -10,8 +10,8 @@ import Cocoa -extension String { - fileprivate mutating func regReplace(pattern: String, replaceWith: String = "") { +fileprivate extension String { + mutating func regReplace(pattern: String, replaceWith: String = "") { // Ref: https://stackoverflow.com/a/40993403/4162914 && https://stackoverflow.com/a/71291137/4162914 do { let regex = try NSRegularExpression( diff --git a/DataCompiler/dataCompiler.swift b/DataCompiler/dataCompiler.swift index 12b75599..941d4f13 100644 --- a/DataCompiler/dataCompiler.swift +++ b/DataCompiler/dataCompiler.swift @@ -12,8 +12,8 @@ import Foundation // MARK: - 前導工作 -extension String { - fileprivate mutating func regReplace(pattern: String, replaceWith: String = "") { +fileprivate extension String { + mutating func regReplace(pattern: String, replaceWith: String = "") { // Ref: https://stackoverflow.com/a/40993403/4162914 && https://stackoverflow.com/a/71291137/4162914 do { let regex = try NSRegularExpression( @@ -29,21 +29,21 @@ extension String { // MARK: - String charComponents Extension -extension String { - public var charComponents: [String] { map { String($0) } } +public extension String { + var charComponents: [String] { map { String($0) } } } -extension Array where Element == String.Element { - public var charComponents: [String] { map { String($0) } } +public extension Array where Element == String.Element { + var charComponents: [String] { map { String($0) } } } // MARK: - StringView Ranges Extension (by Isaac Xen) -extension String { - fileprivate func ranges(splitBy separator: Element) -> [Range] { +fileprivate extension String { + func ranges(splitBy separator: Element) -> [Range] { var startIndex = startIndex return split(separator: separator).reduce(into: []) { ranges, substring in - _ = range(of: substring, range: startIndex.. Double { +fileprivate extension Double { + func rounded(toPlaces places: Int) -> Double { let divisor = pow(10.0, Double(places)) return (self * divisor).rounded() / divisor } @@ -173,15 +173,15 @@ func rawDictForPhrases(isCHS: Bool) -> [Unigram] { return [] } // 預處理格式 - strRAW = strRAW.replacingOccurrences(of: " #MACOS", with: "") // 去掉 macOS 標記 + strRAW = strRAW.replacingOccurrences(of: " #MACOS", with: "") // 去掉 macOS 標記 // CJKWhiteSpace (\x{3000}) to ASCII Space // NonBreakWhiteSpace (\x{A0}) to ASCII Space // Tab to ASCII Space // 統整連續空格為一個 ASCII 空格 strRAW.regReplace(pattern: #"( +| +| +|\t+)+"#, replaceWith: " ") - strRAW.regReplace(pattern: #"(^ | $)"#, replaceWith: "") // 去除行尾行首空格 - strRAW.regReplace(pattern: #"(\f+|\r+|\n+)+"#, replaceWith: "\n") // CR & Form Feed to LF, 且去除重複行 - strRAW.regReplace(pattern: #"^(#.*|.*#WIN32.*)$"#, replaceWith: "") // 以#開頭的行都淨空+去掉所有 WIN32 特有的行 + strRAW.regReplace(pattern: #"(^ | $)"#, replaceWith: "") // 去除行尾行首空格 + strRAW.regReplace(pattern: #"(\f+|\r+|\n+)+"#, replaceWith: "\n") // CR & Form Feed to LF, 且去除重複行 + strRAW.regReplace(pattern: #"^(#.*|.*#WIN32.*)$"#, replaceWith: "") // 以#開頭的行都淨空+去掉所有 WIN32 特有的行 // 正式整理格式,現在就開始去重複: let arrData = Array( NSOrderedSet(array: strRAW.components(separatedBy: "\n")).array as! [String]) @@ -202,25 +202,25 @@ func rawDictForPhrases(isCHS: Bool) -> [Unigram] { } // 然後直接乾脆就轉成 Unigram 吧。 let arrCells: [String] = varLineDataProcessed.components(separatedBy: "\t") - count = 0 // 不需要再定義,因為之前已經有定義過了。 + count = 0 // 不需要再定義,因為之前已經有定義過了。 var phone = "" var phrase = "" var occurrence = 0 for cell in arrCells { count += 1 switch count { - case 1: phrase = cell - case 3: phone = cell - case 2: occurrence = Int(cell) ?? 0 - default: break + case 1: phrase = cell + case 3: phone = cell + case 2: occurrence = Int(cell) ?? 0 + default: break } } - if phrase != "" { // 廢掉空數據;之後無須再這樣處理。 + if phrase != "" { // 廢掉空數據;之後無須再這樣處理。 arrUnigramRAW += [ Unigram( key: phone, value: phrase, score: 0.0, count: occurrence - ) + ), ] } } @@ -242,15 +242,15 @@ func rawDictForKanjis(isCHS: Bool) -> [Unigram] { return [] } // 預處理格式 - strRAW = strRAW.replacingOccurrences(of: " #MACOS", with: "") // 去掉 macOS 標記 + strRAW = strRAW.replacingOccurrences(of: " #MACOS", with: "") // 去掉 macOS 標記 // CJKWhiteSpace (\x{3000}) to ASCII Space // NonBreakWhiteSpace (\x{A0}) to ASCII Space // Tab to ASCII Space // 統整連續空格為一個 ASCII 空格 strRAW.regReplace(pattern: #"( +| +| +|\t+)+"#, replaceWith: " ") - strRAW.regReplace(pattern: #"(^ | $)"#, replaceWith: "") // 去除行尾行首空格 - strRAW.regReplace(pattern: #"(\f+|\r+|\n+)+"#, replaceWith: "\n") // CR & Form Feed to LF, 且去除重複行 - strRAW.regReplace(pattern: #"^(#.*|.*#WIN32.*)$"#, replaceWith: "") // 以#開頭的行都淨空+去掉所有 WIN32 特有的行 + strRAW.regReplace(pattern: #"(^ | $)"#, replaceWith: "") // 去除行尾行首空格 + strRAW.regReplace(pattern: #"(\f+|\r+|\n+)+"#, replaceWith: "\n") // CR & Form Feed to LF, 且去除重複行 + strRAW.regReplace(pattern: #"^(#.*|.*#WIN32.*)$"#, replaceWith: "") // 以#開頭的行都淨空+去掉所有 WIN32 特有的行 // 正式整理格式,現在就開始去重複: let arrData = Array( NSOrderedSet(array: strRAW.components(separatedBy: "\n")).array as! [String]) @@ -281,20 +281,20 @@ func rawDictForKanjis(isCHS: Bool) -> [Unigram] { } // 然後直接乾脆就轉成 Unigram 吧。 let arrCells: [String] = varLineDataProcessed.components(separatedBy: "\t") - count = 0 // 不需要再定義,因為之前已經有定義過了。 + count = 0 // 不需要再定義,因為之前已經有定義過了。 var phone = "" var phrase = "" var occurrence = 0 for cell in arrCells { count += 1 switch count { - case 1: phrase = cell - case 3: phone = cell - case 2: occurrence = Int(cell) ?? 0 - default: break + case 1: phrase = cell + case 3: phone = cell + case 2: occurrence = Int(cell) ?? 0 + default: break } } - if phrase != "" { // 廢掉空數據;之後無須再這樣處理。 + if phrase != "" { // 廢掉空數據;之後無須再這樣處理。 if !isReverseLookupDictionaryProcessed { mapReverseLookup[phrase, default: []].append(cnvPhonabetToASCII(phone).data(using: .utf8)!) mapReverseLookupUnencrypted[phrase, default: []].append(phone) @@ -303,7 +303,7 @@ func rawDictForKanjis(isCHS: Bool) -> [Unigram] { Unigram( key: phone, value: phrase, score: 0.0, count: occurrence - ) + ), ] } } @@ -337,15 +337,15 @@ func rawDictForNonKanjis(isCHS: Bool) -> [Unigram] { return [] } // 預處理格式 - strRAW = strRAW.replacingOccurrences(of: " #MACOS", with: "") // 去掉 macOS 標記 + strRAW = strRAW.replacingOccurrences(of: " #MACOS", with: "") // 去掉 macOS 標記 // CJKWhiteSpace (\x{3000}) to ASCII Space // NonBreakWhiteSpace (\x{A0}) to ASCII Space // Tab to ASCII Space // 統整連續空格為一個 ASCII 空格 strRAW.regReplace(pattern: #"( +| +| +|\t+)+"#, replaceWith: " ") - strRAW.regReplace(pattern: #"(^ | $)"#, replaceWith: "") // 去除行尾行首空格 - strRAW.regReplace(pattern: #"(\f+|\r+|\n+)+"#, replaceWith: "\n") // CR & Form Feed to LF, 且去除重複行 - strRAW.regReplace(pattern: #"^(#.*|.*#WIN32.*)$"#, replaceWith: "") // 以#開頭的行都淨空+去掉所有 WIN32 特有的行 + strRAW.regReplace(pattern: #"(^ | $)"#, replaceWith: "") // 去除行尾行首空格 + strRAW.regReplace(pattern: #"(\f+|\r+|\n+)+"#, replaceWith: "\n") // CR & Form Feed to LF, 且去除重複行 + strRAW.regReplace(pattern: #"^(#.*|.*#WIN32.*)$"#, replaceWith: "") // 以#開頭的行都淨空+去掉所有 WIN32 特有的行 // 正式整理格式,現在就開始去重複: let arrData = Array( NSOrderedSet(array: strRAW.components(separatedBy: "\n")).array as! [String]) @@ -354,7 +354,7 @@ func rawDictForNonKanjis(isCHS: Bool) -> [Unigram] { varLineData = lineData // 先完成某兩步需要分行處理才能完成的格式整理。 varLineData = varLineData.components(separatedBy: " ").prefix(3).joined( - separator: "\t") // 提取前三欄的內容。 + separator: "\t") // 提取前三欄的內容。 let arrLineData = varLineData.components(separatedBy: " ") var varLineDataProcessed = "" var count = 0 @@ -370,26 +370,26 @@ func rawDictForNonKanjis(isCHS: Bool) -> [Unigram] { } // 然後直接乾脆就轉成 Unigram 吧。 let arrCells: [String] = varLineDataProcessed.components(separatedBy: "\t") - count = 0 // 不需要再定義,因為之前已經有定義過了。 + count = 0 // 不需要再定義,因為之前已經有定義過了。 var phone = "" var phrase = "" var occurrence = 0 for cell in arrCells { count += 1 switch count { - case 1: phrase = cell - case 3: phone = cell - case 2: occurrence = Int(cell) ?? 0 - default: break + case 1: phrase = cell + case 3: phone = cell + case 2: occurrence = Int(cell) ?? 0 + default: break } } - if phrase != "" { // 廢掉空數據;之後無須再這樣處理。 + if phrase != "" { // 廢掉空數據;之後無須再這樣處理。 exceptedChars.insert(phrase) arrUnigramRAW += [ Unigram( key: phone, value: phrase, score: 0.0, count: occurrence - ) + ), ] } } @@ -415,24 +415,24 @@ func weightAndSort(_ arrStructUncalculated: [Unigram], isCHS: Bool) -> [Unigram] for unigram in arrStructUncalculated { var weight: Double = 0 switch unigram.count { - case -2: // 拗音假名 - weight = -13 - case -1: // 單個假名 - weight = -13 - case 0: // 墊底低頻漢字與詞語 - weight = log10( - fscale ** (Double(unigram.value.count) / 3.0 - 1.0) * 0.25 / norm) - default: - weight = log10( - fscale ** (Double(unigram.value.count) / 3.0 - 1.0) - * Double(unigram.count) / norm) // Credit: MJHsieh. + case -2: // 拗音假名 + weight = -13 + case -1: // 單個假名 + weight = -13 + case 0: // 墊底低頻漢字與詞語 + weight = log10( + fscale ** (Double(unigram.value.count) / 3.0 - 1.0) * 0.25 / norm) + default: + weight = log10( + fscale ** (Double(unigram.value.count) / 3.0 - 1.0) + * Double(unigram.count) / norm) // Credit: MJHsieh. } - let weightRounded: Double = weight.rounded(toPlaces: 3) // 為了節省生成的檔案體積,僅保留小數點後三位。 + let weightRounded: Double = weight.rounded(toPlaces: 3) // 為了節省生成的檔案體積,僅保留小數點後三位。 arrStructCalculated += [ Unigram( key: unigram.key, value: unigram.value, score: weightRounded, count: unigram.count - ) + ), ] } NSLog(" - \(i18n): 成功計算權重。") @@ -763,7 +763,7 @@ func healthCheck(_ data: [Unigram]) -> String { let separator: String = { var result = "" - for _ in 0..<72 { result += "-" } + for _ in 0 ..< 72 { result += "-" } return result }() @@ -784,7 +784,7 @@ func healthCheck(_ data: [Unigram]) -> String { printl("\n其中有:") var insufficientsMap = [Int: [(String, String, Double, [Unigram], Double)]]() - for x in 2...10 { + for x in 2 ... 10 { insufficientsMap[x] = insufficients.filter { $0.0.split(separator: "-").count == x } } diff --git a/Installer/AppDelegate.swift b/Installer/AppDelegate.swift index b55ceff4..f089c231 100644 --- a/Installer/AppDelegate.swift +++ b/Installer/AppDelegate.swift @@ -53,7 +53,7 @@ class AppDelegate: NSWindowController, NSApplicationDelegate { var allRegisteredInstancesOfThisInputMethod: [TISInputSource] { guard let components = Bundle(url: imeURLInstalled)?.infoDictionary?["ComponentInputModeDict"] as? [String: Any], - let tsInputModeListKey = components["tsInputModeListKey"] as? [String: Any] + let tsInputModeListKey = components["tsInputModeListKey"] as? [String: Any] else { return [] } @@ -102,7 +102,7 @@ class AppDelegate: NSWindowController, NSApplicationDelegate { let currentVersion = currentBundle?.infoDictionary?[kCFBundleVersionKey as String] as? String currentVersionNumber = (currentVersion as NSString?)?.integerValue ?? 0 if shortVersion != nil, let currentVersion = currentVersion, - currentVersion.compare(installingVersion, options: .numeric) == .orderedAscending + currentVersion.compare(installingVersion, options: .numeric) == .orderedAscending { // Upgrading confirmed. installButton.title = NSLocalizedString("Upgrade", comment: "") diff --git a/Installer/AppDelegate_Extension.swift b/Installer/AppDelegate_Extension.swift index 0c285a99..29e05040 100644 --- a/Installer/AppDelegate_Extension.swift +++ b/Installer/AppDelegate_Extension.swift @@ -24,7 +24,7 @@ extension AppDelegate { let shouldWaitForTranslocationRemoval = Reloc.isAppBundleTranslocated(atPath: kTargetPartialPath) - && window.responds(to: #selector(NSWindow.beginSheet(_:completionHandler:))) + && window.responds(to: #selector(NSWindow.beginSheet(_:completionHandler:))) // 將既存輸入法扔到垃圾桶內 do { @@ -116,7 +116,7 @@ extension AppDelegate { _ = try? shell("/usr/bin/xattr -drs com.apple.quarantine \(kTargetPartialPath)") guard let theBundle = Bundle(url: imeURLInstalled), - let imeIdentifier = theBundle.bundleIdentifier + let imeIdentifier = theBundle.bundleIdentifier else { endAppWithDelay() return diff --git a/Makefile b/Makefile index 6435ac5b..22931f96 100644 --- a/Makefile +++ b/Makefile @@ -20,23 +20,14 @@ debug: DSTROOT = /Library/Input Methods VC_APP_ROOT = $(DSTROOT)/vChewing.app -.PHONY: clang-format lint batchfix format clang-format-swift clang-format-cpp +.PHONY: lint format -format: batchfix clang-format lint - -clang-format: - @git ls-files --exclude-standard | grep -E '\.swift$$' | xargs swift-format format --in-place --configuration ./.clang-format-swift.json --parallel - @git ls-files --exclude-standard | grep -E '\.swift$$' | xargs swift-format lint --configuration ./.clang-format-swift.json --parallel +format: + @swiftformat --swiftversion 5.5 --indent 2 ./ lint: - @git ls-files --exclude-standard | grep -E '\.swift$$' | xargs swift-format lint --configuration ./.clang-format-swift.json --parallel - -batchfix: @git ls-files --exclude-standard | grep -E '\.swift$$' | swiftlint --fix --autocorrect -advanced-lint: - @swiftformat --swiftversion 5.5 --indent 2 ./ - .PHONY: permission-check install-debug install-release permission-check: diff --git a/Packages/DanielGalasko_FolderMonitor/Package.swift b/Packages/DanielGalasko_FolderMonitor/Package.swift index 9b39ba61..4865aadc 100644 --- a/Packages/DanielGalasko_FolderMonitor/Package.swift +++ b/Packages/DanielGalasko_FolderMonitor/Package.swift @@ -4,19 +4,19 @@ import PackageDescription let package = Package( name: "FolderMonitor", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "FolderMonitor", targets: ["FolderMonitor"] - ) + ), ], dependencies: [], targets: [ .target( name: "FolderMonitor", dependencies: [] - ) + ), ] ) diff --git a/Packages/Fuziki_NSAttributedTextView/Package.swift b/Packages/Fuziki_NSAttributedTextView/Package.swift index 5d292f6c..db2951e7 100644 --- a/Packages/Fuziki_NSAttributedTextView/Package.swift +++ b/Packages/Fuziki_NSAttributedTextView/Package.swift @@ -4,19 +4,19 @@ import PackageDescription let package = Package( name: "NSAttributedTextView", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "NSAttributedTextView", targets: ["NSAttributedTextView"] - ) + ), ], dependencies: [], targets: [ .target( name: "NSAttributedTextView", dependencies: [] - ) + ), ] ) diff --git a/Packages/Fuziki_NSAttributedTextView/Sources/NSAttributedTextView/NSAttributedTextView.swift b/Packages/Fuziki_NSAttributedTextView/Sources/NSAttributedTextView/NSAttributedTextView.swift index 7978874c..ac32cf85 100644 --- a/Packages/Fuziki_NSAttributedTextView/Sources/NSAttributedTextView/NSAttributedTextView.swift +++ b/Packages/Fuziki_NSAttributedTextView/Sources/NSAttributedTextView/NSAttributedTextView.swift @@ -103,8 +103,8 @@ public class NSAttributedTextView: NSView { @discardableResult public func shrinkFrame() -> NSRect { let attrString: NSAttributedString = { switch direction { - case .horizontal: return attributedStringValue() - default: return attributedStringValue(areaCalculation: true) + case .horizontal: return attributedStringValue() + default: return attributedStringValue(areaCalculation: true) } }() var rect = attrString.boundingRect( @@ -130,14 +130,14 @@ public class NSAttributedTextView: NSView { let path = CGPath(rect: rect, transform: nil) let theCTFrameProgression: CTFrameProgression = { switch direction { - case .horizontal: return CTFrameProgression.topToBottom - case .vertical: return CTFrameProgression.rightToLeft - case .verticalReversed: return CTFrameProgression.leftToRight + case .horizontal: return CTFrameProgression.topToBottom + case .vertical: return CTFrameProgression.rightToLeft + case .verticalReversed: return CTFrameProgression.leftToRight } }() let frameAttrs: CFDictionary = [ - kCTFrameProgressionAttributeName: theCTFrameProgression.rawValue + kCTFrameProgressionAttributeName: theCTFrameProgression.rawValue, ] as CFDictionary let newFrame = CTFramesetterCreateFrame(setter, CFRangeMake(0, 0), path, frameAttrs) ctFrame = newFrame diff --git a/Packages/Jad_BookmarkManager/Package.swift b/Packages/Jad_BookmarkManager/Package.swift index 041abe6c..8bd10900 100644 --- a/Packages/Jad_BookmarkManager/Package.swift +++ b/Packages/Jad_BookmarkManager/Package.swift @@ -4,19 +4,19 @@ import PackageDescription let package = Package( name: "BookmarkManager", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "BookmarkManager", targets: ["BookmarkManager"] - ) + ), ], dependencies: [], targets: [ .target( name: "BookmarkManager", dependencies: [] - ) + ), ] ) diff --git a/Packages/Jad_BookmarkManager/Sources/BookmarkManager/BookmarkManager.swift b/Packages/Jad_BookmarkManager/Sources/BookmarkManager/BookmarkManager.swift index 0c54b715..2797864f 100644 --- a/Packages/Jad_BookmarkManager/Sources/BookmarkManager/BookmarkManager.swift +++ b/Packages/Jad_BookmarkManager/Sources/BookmarkManager/BookmarkManager.swift @@ -8,7 +8,7 @@ public class BookmarkManager { // Save bookmark for URL. Use this inside the NSOpenPanel `begin` closure public func saveBookmark(for url: URL) { guard let bookmarkDic = getBookmarkData(url: url), - let bookmarkURL = getBookmarkURL() + let bookmarkURL = getBookmarkURL() else { NSLog("Error getting data or bookmarkURL") return diff --git a/Packages/Qwertyyb_ShiftKeyUpChecker/Package.swift b/Packages/Qwertyyb_ShiftKeyUpChecker/Package.swift index e85f774b..54cb3213 100644 --- a/Packages/Qwertyyb_ShiftKeyUpChecker/Package.swift +++ b/Packages/Qwertyyb_ShiftKeyUpChecker/Package.swift @@ -4,19 +4,19 @@ import PackageDescription let package = Package( name: "ShiftKeyUpChecker", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "ShiftKeyUpChecker", targets: ["ShiftKeyUpChecker"] - ) + ), ], dependencies: [], targets: [ .target( name: "ShiftKeyUpChecker", dependencies: [] - ) + ), ] ) diff --git a/Packages/Qwertyyb_ShiftKeyUpChecker/Sources/ShiftKeyUpChecker/ShiftKeyUpChecker.swift b/Packages/Qwertyyb_ShiftKeyUpChecker/Sources/ShiftKeyUpChecker/ShiftKeyUpChecker.swift index db448b81..edaaeb44 100644 --- a/Packages/Qwertyyb_ShiftKeyUpChecker/Sources/ShiftKeyUpChecker/ShiftKeyUpChecker.swift +++ b/Packages/Qwertyyb_ShiftKeyUpChecker/Sources/ShiftKeyUpChecker/ShiftKeyUpChecker.swift @@ -5,8 +5,8 @@ import Carbon import Cocoa -extension Date { - fileprivate static func - (lhs: Date, rhs: Date) -> TimeInterval { +private extension Date { + static func - (lhs: Date, rhs: Date) -> TimeInterval { lhs.timeIntervalSinceReferenceDate - rhs.timeIntervalSinceReferenceDate } } @@ -40,8 +40,8 @@ public struct ShiftKeyUpChecker { private mutating func checkModifierKeyUp(event: NSEvent) -> Bool { if event.type == .flagsChanged, - event.modifierFlags.intersection(.deviceIndependentFlagsMask) == .init(rawValue: 0), - Date() - lastTime <= delayInterval, shiftIsBeingPressed + event.modifierFlags.intersection(.deviceIndependentFlagsMask) == .init(rawValue: 0), + Date() - lastTime <= delayInterval, shiftIsBeingPressed { // modifier keyup event lastTime = Date(timeInterval: -3600 * 4, since: Date()) @@ -56,8 +56,8 @@ public struct ShiftKeyUpChecker { print("isLeftShift: \(isLeftShift), isRightShift: \(isRightShift)") let isKeyDown = event.type == .flagsChanged - && checkModifier.contains(event.modifierFlags.intersection(.deviceIndependentFlagsMask)) - && checkKeyCode.contains(event.keyCode) + && checkModifier.contains(event.modifierFlags.intersection(.deviceIndependentFlagsMask)) + && checkKeyCode.contains(event.keyCode) if isKeyDown { // modifier keydown event lastTime = Date() diff --git a/Packages/RMJay_LineReader/Package.swift b/Packages/RMJay_LineReader/Package.swift index f55211b8..4a091f6e 100644 --- a/Packages/RMJay_LineReader/Package.swift +++ b/Packages/RMJay_LineReader/Package.swift @@ -4,23 +4,23 @@ import PackageDescription let package = Package( name: "LineReader", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "LineReader", targets: ["LineReader"] - ) + ), ], dependencies: [ - .package(path: "../vChewing_SwiftExtension") + .package(path: "../vChewing_SwiftExtension"), ], targets: [ .target( name: "LineReader", dependencies: [ - .product(name: "SwiftExtension", package: "vChewing_SwiftExtension") + .product(name: "SwiftExtension", package: "vChewing_SwiftExtension"), ] - ) + ), ] ) diff --git a/Packages/RMJay_LineReader/Sources/LineReader/LineReader.swift b/Packages/RMJay_LineReader/Sources/LineReader/LineReader.swift index dfbfe9c7..e353f56e 100644 --- a/Packages/RMJay_LineReader/Sources/LineReader/LineReader.swift +++ b/Packages/RMJay_LineReader/Sources/LineReader/LineReader.swift @@ -31,9 +31,9 @@ public class LineReader { // get a data from the buffer up to the next delimiter if let range = buffer.range(of: delimData) { // convert data to a string - let line = String(data: buffer.subdata(in: 0.. { } @available(macOS 10.15, *) -extension View { +public extension View { /// Wraps a SwiftUI `View` that can be extended to provide backport functionality. - public var backport: Backport { .init(self) } + var backport: Backport { .init(self) } } @available(macOS 10.15, *) -extension NSObjectProtocol { +public extension NSObjectProtocol { /// Wraps an `NSObject` that can be extended to provide backport functionality. - public var backport: Backport { .init(self) } + var backport: Backport { .init(self) } } @available(macOS 10.15, *) -extension AnyTransition { +public extension AnyTransition { /// Wraps an `AnyTransition` that can be extended to provide backport functionality. - public static var backport: Backport { + static var backport: Backport { Backport(.identity) } } diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Extras/FittingGeometryReader.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Extras/FittingGeometryReader.swift index 81d31621..c2fa4ec5 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Extras/FittingGeometryReader.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Extras/FittingGeometryReader.swift @@ -7,7 +7,7 @@ import SwiftUI @available(macOS 10.15, *) /// A geometry reader that automatically sizes its height to 'fit' its content. public struct FittingGeometryReader: View where Content: View { - @State private var height: CGFloat = 10 // must be non-zero + @State private var height: CGFloat = 10 // must be non-zero private var content: (GeometryProxy) -> Content @available(macOS 10.15, *) diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/Environment+String.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/Environment+String.swift index 3520c996..8ca4c53b 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/Environment+String.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/Environment+String.swift @@ -5,12 +5,12 @@ import SwiftUI @available(macOS 10.15, *) -extension EnvironmentValues { - fileprivate func containsValue(forKey key: String) -> Bool { +private extension EnvironmentValues { + func containsValue(forKey key: String) -> Bool { value(forKey: key) != nil } - fileprivate func value(forKey key: String, from mirror: Mirror, as _: T.Type) -> T? { + func value(forKey key: String, from mirror: Mirror, as _: T.Type) -> T? { // Found a match if let value = mirror.descendant("value", "some") { if let typedValue = value as? T { @@ -32,7 +32,7 @@ extension EnvironmentValues { /// Extracts a value from the environment by the name of its associated EnvironmentKey. /// Can be used to grab private environment values such as foregroundColor ("ForegroundColorKey"). - fileprivate func value(forKey key: String, as _: T.Type) -> T? { + func value(forKey key: String, as _: T.Type) -> T? { if let mirror = value(forKey: key) as? Mirror { return value(forKey: key, from: mirror, as: T.self) } else if let value = value(forKey: key) as? T { @@ -42,7 +42,7 @@ extension EnvironmentValues { } } - fileprivate func value(forKey key: String) -> Any? { + func value(forKey key: String) -> Any? { func keyFromTypeName(typeName: String) -> String? { let expectedPrefix = "TypedElement UIView { .init() } @@ -149,7 +149,7 @@ import SwiftUI #elseif os(macOS) @available(macOS 10.15, *) extension InspectionView { - fileprivate struct Representable: NSViewRepresentable { + struct Representable: NSViewRepresentable { let parent: InspectionView func makeNSView(context _: Context) -> NSView { diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/OwningController.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/OwningController.swift index 78705d3f..18d52078 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/OwningController.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/OwningController.swift @@ -5,8 +5,8 @@ #if os(iOS) import UIKit - extension UIView { - public var parentController: UIViewController? { + public extension UIView { + var parentController: UIViewController? { var responder: UIResponder? = self while !(responder is UIViewController), superview != nil { @@ -24,8 +24,8 @@ import AppKit @available(macOS 10.15, *) - extension NSView { - public var parentController: NSViewController? { + public extension NSView { + var parentController: NSViewController? { var responder: NSResponder? = self while !(responder is NSViewController), superview != nil { diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/SafeArea.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/SafeArea.swift index 15e3e0d5..32e88e5f 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/SafeArea.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/SafeArea.swift @@ -29,12 +29,12 @@ import SwiftUI guard let viewClassNameUtf8 = (viewSubclassName as NSString).utf8String else { return } guard let viewSubclass = objc_allocateClassPair(viewClass, viewClassNameUtf8, 0) else { return } - if let method = class_getInstanceMethod(UIView.self, #selector(getter:UIView.safeAreaInsets)) { + if let method = class_getInstanceMethod(UIView.self, #selector(getter: UIView.safeAreaInsets)) { let safeAreaInsets: @convention(block) (AnyObject) -> UIEdgeInsets = { _ in .zero } class_addMethod( - viewSubclass, #selector(getter:UIView.safeAreaInsets), imp_implementationWithBlock(safeAreaInsets), + viewSubclass, #selector(getter: UIView.safeAreaInsets), imp_implementationWithBlock(safeAreaInsets), method_getTypeEncoding(method) ) } diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/AppStorage/AppStorage.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/AppStorage/AppStorage.swift index 388057e3..6349b096 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/AppStorage/AppStorage.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/AppStorage/AppStorage.swift @@ -43,7 +43,7 @@ extension Backport where Wrapped == Any { } @available(macOS 10.15, *) -extension Backport.AppStorage { +public extension Backport.AppStorage { /// Creates a property that can read and write to a boolean user default. /// /// - Parameters: @@ -53,7 +53,7 @@ extension Backport.AppStorage { /// store. /// - store: The store to read and write to. A value /// of `nil` will use the user default store from the environment. - public init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value == Bool { + init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value == Bool { let value = store.value(forKey: key) as? Value ?? wrappedValue self.init( value: value, store: store, key: key, @@ -71,7 +71,7 @@ extension Backport.AppStorage { /// store. /// - store: The store to read and write to. A value /// of `nil` will use the user default store from the environment. - public init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value == Int { + init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value == Int { let value = store.value(forKey: key) as? Value ?? wrappedValue self.init( value: value, store: store, key: key, @@ -89,7 +89,7 @@ extension Backport.AppStorage { /// store. /// - store: The store to read and write to. A value /// of `nil` will use the user default store from the environment. - public init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value == Double { + init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value == Double { let value = store.value(forKey: key) as? Value ?? wrappedValue self.init( value: value, store: store, key: key, @@ -107,7 +107,7 @@ extension Backport.AppStorage { /// store. /// - store: The store to read and write to. A value /// of `nil` will use the user default store from the environment. - public init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value == String { + init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value == String { let value = store.value(forKey: key) as? Value ?? wrappedValue self.init( value: value, store: store, key: key, @@ -125,7 +125,7 @@ extension Backport.AppStorage { /// store. /// - store: The store to read and write to. A value /// of `nil` will use the user default store from the environment. - public init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value == URL { + init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value == URL { let value = store.url(forKey: key) ?? wrappedValue self.init( value: value, store: store, key: key, @@ -146,7 +146,7 @@ extension Backport.AppStorage { /// store. /// - store: The store to read and write to. A value /// of `nil` will use the user default store from the environment. - public init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value == Data { + init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value == Data { let value = store.value(forKey: key) as? Data ?? wrappedValue self.init( value: value, store: store, key: key, @@ -157,7 +157,7 @@ extension Backport.AppStorage { } @available(macOS 10.15, *) -extension Backport.AppStorage where Wrapped == Any, Value: ExpressibleByNilLiteral { +public extension Backport.AppStorage where Wrapped == Any, Value: ExpressibleByNilLiteral { /// Creates a property that can read and write an Optional boolean user /// default. /// @@ -168,7 +168,7 @@ extension Backport.AppStorage where Wrapped == Any, Value: ExpressibleByNilLiter /// store. /// - store: The store to read and write to. A value /// of `nil` will use the user default store from the environment. - public init(_ key: String, store: UserDefaults = .standard) where Value == Bool? { + init(_ key: String, store: UserDefaults = .standard) where Value == Bool? { let value = store.value(forKey: key) as? Value ?? .none self.init( value: value, store: store, key: key, @@ -187,7 +187,7 @@ extension Backport.AppStorage where Wrapped == Any, Value: ExpressibleByNilLiter /// store. /// - store: The store to read and write to. A value /// of `nil` will use the user default store from the environment. - public init(_ key: String, store: UserDefaults = .standard) where Value == Int? { + init(_ key: String, store: UserDefaults = .standard) where Value == Int? { let value = store.value(forKey: key) as? Value ?? .none self.init( value: value, store: store, key: key, @@ -206,7 +206,7 @@ extension Backport.AppStorage where Wrapped == Any, Value: ExpressibleByNilLiter /// store. /// - store: The store to read and write to. A value /// of `nil` will use the user default store from the environment. - public init(_ key: String, store: UserDefaults = .standard) where Value == Double? { + init(_ key: String, store: UserDefaults = .standard) where Value == Double? { let value = store.value(forKey: key) as? Value ?? .none self.init( value: value, store: store, key: key, @@ -225,7 +225,7 @@ extension Backport.AppStorage where Wrapped == Any, Value: ExpressibleByNilLiter /// store. /// - store: The store to read and write to. A value /// of `nil` will use the user default store from the environment. - public init(_ key: String, store: UserDefaults = .standard) where Value == String? { + init(_ key: String, store: UserDefaults = .standard) where Value == String? { let value = store.value(forKey: key) as? Value ?? .none self.init( value: value, store: store, key: key, @@ -244,7 +244,7 @@ extension Backport.AppStorage where Wrapped == Any, Value: ExpressibleByNilLiter /// store. /// - store: The store to read and write to. A value /// of `nil` will use the user default store from the environment. - public init(_ key: String, store: UserDefaults = .standard) where Value == URL? { + init(_ key: String, store: UserDefaults = .standard) where Value == URL? { let value = store.url(forKey: key) ?? .none self.init( value: value, store: store, key: key, @@ -263,7 +263,7 @@ extension Backport.AppStorage where Wrapped == Any, Value: ExpressibleByNilLiter /// store. /// - store: The store to read and write to. A value /// of `nil` will use the user default store from the environment. - public init(_ key: String, store: UserDefaults = .standard) where Value == Data? { + init(_ key: String, store: UserDefaults = .standard) where Value == Data? { let value = store.value(forKey: key) as? Value ?? .none self.init( value: value, store: store, key: key, @@ -274,7 +274,7 @@ extension Backport.AppStorage where Wrapped == Any, Value: ExpressibleByNilLiter } @available(macOS 10.15, *) -extension Backport.AppStorage where Wrapped == Any, Value: RawRepresentable { +public extension Backport.AppStorage where Wrapped == Any, Value: RawRepresentable { /// Creates a property that can read and write to a string user default, /// transforming that to `RawRepresentable` data type. /// @@ -295,7 +295,7 @@ extension Backport.AppStorage where Wrapped == Any, Value: RawRepresentable { /// store. /// - store: The store to read and write to. A value /// of `nil` will use the user default store from the environment. - public init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value.RawValue == String { + init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value.RawValue == String { let rawValue = store.value(forKey: key) as? Value.RawValue let value = rawValue.flatMap(Value.init) ?? wrappedValue self.init( @@ -325,7 +325,7 @@ extension Backport.AppStorage where Wrapped == Any, Value: RawRepresentable { /// store. /// - store: The store to read and write to. A value /// of `nil` will use the user default store from the environment. - public init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value.RawValue == Int { + init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value.RawValue == Int { let rawValue = store.value(forKey: key) as? Value.RawValue let value = rawValue.flatMap(Value.init) ?? wrappedValue self.init( diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Background/Background.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Background/Background.swift index bd844394..f2bcf4ab 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Background/Background.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Background/Background.swift @@ -5,7 +5,7 @@ import SwiftUI @available(macOS 10.15, *) -extension Backport where Wrapped: View { +public extension Backport where Wrapped: View { /// Layers the views that you specify behind this view. /// /// Use this modifier to place one or more views behind another view. @@ -128,7 +128,7 @@ extension Backport where Wrapped: View { /// The last view that you list appears at the front of the stack. /// /// - Returns: A view that uses the specified content as a background. - public func background(alignment: Alignment = .center, @ViewBuilder _ content: () -> Content) + func background(alignment: Alignment = .center, @ViewBuilder _ content: () -> Content) -> some View { self.content.background(content(), alignment: alignment) diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Dismiss/Dismiss.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Dismiss/Dismiss.swift index 36364613..0a27f290 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Dismiss/Dismiss.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Dismiss/Dismiss.swift @@ -5,7 +5,7 @@ import SwiftUI @available(macOS 10.15, *) -extension EnvironmentValues { +public extension EnvironmentValues { /// An action that dismisses the current presentation. /// /// Use this environment value to get the ``Backport.DismissAction`` instance @@ -73,7 +73,7 @@ extension EnvironmentValues { /// The dismiss action has no effect on a view that isn't currently /// presented. If you need to query whether SwiftUI is currently presenting /// a view, read the ``EnvironmentValues/backportIsPresented`` environment value. - public var backportDismiss: Backport.DismissAction { + var backportDismiss: Backport.DismissAction { .init(presentation: presentationMode) } @@ -104,7 +104,7 @@ extension EnvironmentValues { /// /// To dismiss the currently presented view, use /// ``EnvironmentValues/backportDismiss``. - public var backportIsPresented: Bool { + var backportIsPresented: Bool { presentationMode.wrappedValue.isPresented } } diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/DynamicType/DynamicType+Modifiers.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/DynamicType/DynamicType+Modifiers.swift index 14c175e0..a617ee40 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/DynamicType/DynamicType+Modifiers.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/DynamicType/DynamicType+Modifiers.swift @@ -5,7 +5,7 @@ import SwiftUI @available(macOS 10.15, *) -extension Backport where Wrapped: View { +public extension Backport where Wrapped: View { /// Sets the Dynamic Type size within the view to the given value. /// /// As an example, you can set a Dynamic Type size in `ContentView` to be @@ -31,7 +31,7 @@ extension Backport where Wrapped: View { /// - Returns: A view that sets the Dynamic Type size to the specified /// `size`. @ViewBuilder - public func dynamicTypeSize(_ size: Backport.DynamicTypeSize) -> some View { + func dynamicTypeSize(_ size: Backport.DynamicTypeSize) -> some View { content.environment(\.backportDynamicTypeSize, size) } @@ -66,8 +66,9 @@ extension Backport where Wrapped: View { /// - Returns: A view that constrains the Dynamic Type size of this view /// within the specified `range`. @ViewBuilder - public func dynamicTypeSize(_ range: T) -> some View - where T: RangeExpression, T.Bound == Backport.DynamicTypeSize { + func dynamicTypeSize(_ range: T) -> some View + where T: RangeExpression, T.Bound == Backport.DynamicTypeSize + { if let range = range as? Range { content .modifier(DynamicTypeRangeModifier()) diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/DynamicType/DynamicType.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/DynamicType/DynamicType.swift index d12b31d9..e048828a 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/DynamicType/DynamicType.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/DynamicType/DynamicType.swift @@ -63,38 +63,6 @@ extension Backport where Wrapped == Any { /// Create a Dynamic Type size from its `UIContentSizeCategory` equivalent. public init?(_ uiSizeCategory: UIContentSizeCategory) { switch uiSizeCategory { - case .extraSmall: - self = .xSmall - case .small: - self = .small - case .medium: - self = .medium - case .large: - self = .medium - case .extraLarge: - self = .xLarge - case .extraExtraLarge: - self = .xxLarge - case .extraExtraExtraLarge: - self = .xxxLarge - case .accessibilityMedium: - self = .accessibility1 - case .accessibilityLarge: - self = .accessibility2 - case .accessibilityExtraLarge: - self = .accessibility3 - case .accessibilityExtraExtraLarge: - self = .accessibility4 - case .accessibilityExtraExtraExtraLarge: - self = .accessibility5 - default: - return nil - } - } - #endif - - internal init(_ sizeCategory: ContentSizeCategory) { - switch sizeCategory { case .extraSmall: self = .xSmall case .small: @@ -102,7 +70,7 @@ extension Backport where Wrapped == Any { case .medium: self = .medium case .large: - self = .large + self = .medium case .extraLarge: self = .xLarge case .extraExtraLarge: @@ -120,36 +88,68 @@ extension Backport where Wrapped == Any { case .accessibilityExtraExtraExtraLarge: self = .accessibility5 default: - self = .large + return nil + } + } + #endif + + internal init(_ sizeCategory: ContentSizeCategory) { + switch sizeCategory { + case .extraSmall: + self = .xSmall + case .small: + self = .small + case .medium: + self = .medium + case .large: + self = .large + case .extraLarge: + self = .xLarge + case .extraExtraLarge: + self = .xxLarge + case .extraExtraExtraLarge: + self = .xxxLarge + case .accessibilityMedium: + self = .accessibility1 + case .accessibilityLarge: + self = .accessibility2 + case .accessibilityExtraLarge: + self = .accessibility3 + case .accessibilityExtraExtraLarge: + self = .accessibility4 + case .accessibilityExtraExtraExtraLarge: + self = .accessibility5 + default: + self = .large } } var sizeCategory: ContentSizeCategory { switch self { - case .xSmall: - return .extraSmall - case .small: - return .small - case .medium: - return .medium - case .large: - return .large - case .xLarge: - return .extraLarge - case .xxLarge: - return .extraExtraLarge - case .xxxLarge: - return .extraExtraExtraLarge - case .accessibility1: - return .accessibilityMedium - case .accessibility2: - return .accessibilityLarge - case .accessibility3: - return .accessibilityExtraLarge - case .accessibility4: - return .accessibilityExtraExtraLarge - case .accessibility5: - return .accessibilityExtraExtraExtraLarge + case .xSmall: + return .extraSmall + case .small: + return .small + case .medium: + return .medium + case .large: + return .large + case .xLarge: + return .extraLarge + case .xxLarge: + return .extraExtraLarge + case .xxxLarge: + return .extraExtraExtraLarge + case .accessibility1: + return .accessibilityMedium + case .accessibility2: + return .accessibilityLarge + case .accessibility3: + return .accessibilityExtraLarge + case .accessibility4: + return .accessibilityExtraExtraLarge + case .accessibility5: + return .accessibilityExtraExtraExtraLarge } } } @@ -159,30 +159,30 @@ extension Backport where Wrapped == Any { extension Backport.DynamicTypeSize { var dynamicTypeSize: DynamicTypeSize { switch self { - case .xSmall: - return .xSmall - case .small: - return .small - case .medium: - return .medium - case .large: - return .large - case .xLarge: - return .xLarge - case .xxLarge: - return .xxLarge - case .xxxLarge: - return .xxxLarge - case .accessibility1: - return .accessibility1 - case .accessibility2: - return .accessibility2 - case .accessibility3: - return .accessibility3 - case .accessibility4: - return .accessibility4 - case .accessibility5: - return .accessibility5 + case .xSmall: + return .xSmall + case .small: + return .small + case .medium: + return .medium + case .large: + return .large + case .xLarge: + return .xLarge + case .xxLarge: + return .xxLarge + case .xxxLarge: + return .xxxLarge + case .accessibility1: + return .accessibility1 + case .accessibility2: + return .accessibility2 + case .accessibility3: + return .accessibility3 + case .accessibility4: + return .accessibility4 + case .accessibility5: + return .accessibility5 } } } @@ -193,32 +193,32 @@ extension Backport.DynamicTypeSize { extension UIContentSizeCategory { public init(_ dynamicTypeSize: Backport.DynamicTypeSize?) { switch dynamicTypeSize { - case .xSmall: - self = .extraSmall - case .small: - self = .small - case .medium: - self = .medium - case .large: - self = .large - case .xLarge: - self = .extraLarge - case .xxLarge: - self = .extraExtraLarge - case .xxxLarge: - self = .extraExtraExtraLarge - case .accessibility1: - self = .accessibilityMedium - case .accessibility2: - self = .accessibilityLarge - case .accessibility3: - self = .accessibilityExtraLarge - case .accessibility4: - self = .accessibilityExtraExtraLarge - case .accessibility5: - self = .accessibilityExtraExtraExtraLarge - case .none: - self = .large + case .xSmall: + self = .extraSmall + case .small: + self = .small + case .medium: + self = .medium + case .large: + self = .large + case .xLarge: + self = .extraLarge + case .xxLarge: + self = .extraExtraLarge + case .xxxLarge: + self = .extraExtraExtraLarge + case .accessibility1: + self = .accessibilityMedium + case .accessibility2: + self = .accessibilityLarge + case .accessibility3: + self = .accessibilityExtraLarge + case .accessibility4: + self = .accessibilityExtraExtraLarge + case .accessibility5: + self = .accessibilityExtraExtraExtraLarge + case .none: + self = .large } } } diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/Label.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/Label.swift index 1d38aaea..2c293fc0 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/Label.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/Label.swift @@ -111,14 +111,14 @@ extension Backport where Wrapped == Any { } @available(macOS 10.15, *) -extension Backport.Label where Wrapped == Any, Title == Text, Icon == Image { +public extension Backport.Label where Wrapped == Any, Title == Text, Icon == Image { /// Creates a label with an icon image and a title generated from a /// localized string. /// /// - Parameters: /// - titleKey: A title generated from a localized string. /// - image: The name of the image resource to lookup. - public init(_ titleKey: LocalizedStringKey, image name: String) { + init(_ titleKey: LocalizedStringKey, image name: String) { self.init(title: { Text(titleKey) }, icon: { Image(name) }) } @@ -127,7 +127,7 @@ extension Backport.Label where Wrapped == Any, Title == Text, Icon == Image { /// - Parameters: /// - title: A string used as the label's title. /// - image: The name of the image resource to lookup. - public init(_ title: S, image name: String) where S: StringProtocol { + init(_ title: S, image name: String) where S: StringProtocol { self.init(title: { Text(title) }, icon: { Image(name) }) } } @@ -156,8 +156,9 @@ extension Backport.Label where Wrapped == Any, Title == Text, Icon == Image { } @available(macOS 10.15, *) -extension Backport.Label -where Wrapped == Any, Title == Backport.LabelStyleConfiguration.Title, Icon == Backport.LabelStyleConfiguration.Icon { +public extension Backport.Label + where Wrapped == Any, Title == Backport.LabelStyleConfiguration.Title, Icon == Backport.LabelStyleConfiguration.Icon +{ /// Creates a label representing the configuration of a style. /// /// You can use this initializer within the ``LabelStyle/makeBody(configuration:)`` @@ -177,7 +178,7 @@ where Wrapped == Any, Title == Backport.LabelStyleConfiguration.Title, Icon == B /// } /// /// - Parameter configuration: The label style to use. - public init(_ configuration: Backport.LabelStyleConfiguration) { + init(_ configuration: Backport.LabelStyleConfiguration) { config = configuration } } diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/LabeledContent/LabeledContent.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/LabeledContent/LabeledContent.swift index 312edb4e..3cc9cec2 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/LabeledContent/LabeledContent.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/LabeledContent/LabeledContent.swift @@ -137,8 +137,8 @@ extension Backport where Wrapped == Any { } @available(macOS 10.15, *) -extension Backport.LabeledContent -where +public extension Backport.LabeledContent + where Wrapped == Any, Label == Backport.LabeledContentStyleConfiguration.Label, Content == Backport.LabeledContentStyleConfiguration.Content { @@ -161,13 +161,13 @@ where /// } /// /// - Parameter configuration: The properties of the labeled content - public init(_ config: Backport.LabeledContentStyleConfiguration) { + init(_ config: Backport.LabeledContentStyleConfiguration) { self.config = config } } @available(macOS 10.15, *) -extension Backport.LabeledContent where Wrapped == Any, Label == Text, Content: View { +public extension Backport.LabeledContent where Wrapped == Any, Label == Text, Content: View { /// Creates a labeled view that generates its label from a localized string /// key. /// @@ -179,7 +179,7 @@ extension Backport.LabeledContent where Wrapped == Any, Label == Text, Content: /// - titleKey: The key for the view's localized title, that describes /// the purpose of the view. /// - content: The value content being labeled. - public init(_ titleKey: LocalizedStringKey, @ViewBuilder content: () -> Content) { + init(_ titleKey: LocalizedStringKey, @ViewBuilder content: () -> Content) { config = .init( label: Text(titleKey), content: content() @@ -196,7 +196,7 @@ extension Backport.LabeledContent where Wrapped == Any, Label == Text, Content: /// - Parameters: /// - title: A string that describes the purpose of the view. /// - content: The value content being labeled. - public init(_ title: S, @ViewBuilder content: () -> Content) where S: StringProtocol { + init(_ title: S, @ViewBuilder content: () -> Content) where S: StringProtocol { config = .init( label: Text(title), content: content() @@ -226,7 +226,7 @@ extension Backport.LabeledContent: View where Wrapped == Any, Label: View, Conte } @available(macOS 10.15, *) -extension Backport.LabeledContent where Wrapped == Any, Label == Text, Content == Text { +public extension Backport.LabeledContent where Wrapped == Any, Label == Text, Content == Text { /// Creates a labeled informational view. /// /// This initializer creates a ``Text`` label on your behalf, and treats the @@ -243,7 +243,7 @@ extension Backport.LabeledContent where Wrapped == Any, Label == Text, Content = /// - titleKey: The key for the view's localized title, that describes /// the purpose of the view. /// - value: The value being labeled. - public init(_ titleKey: LocalizedStringKey, value: S) { + init(_ titleKey: LocalizedStringKey, value: S) { config = .init( label: Text(titleKey), content: Text(value) @@ -266,7 +266,7 @@ extension Backport.LabeledContent where Wrapped == Any, Label == Text, Content = /// - Parameters: /// - title: A string that describes the purpose of the view. /// - value: The value being labeled. - public init(_ title: S1, value: S2) { + init(_ title: S1, value: S2) { config = .init( label: Text(title), content: Text(value) diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/LabeledContent/Styles/AutomaticLabeledContentStyle.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/LabeledContent/Styles/AutomaticLabeledContentStyle.swift index 0ba5ef1a..1d922ab0 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/LabeledContent/Styles/AutomaticLabeledContentStyle.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/LabeledContent/Styles/AutomaticLabeledContentStyle.swift @@ -5,8 +5,8 @@ import SwiftUI @available(macOS 10.15, *) -extension Backport where Wrapped == Any { - public struct AutomaticLabeledContentStyle: BackportLabeledContentStyle { +public extension Backport where Wrapped == Any { + struct AutomaticLabeledContentStyle: BackportLabeledContentStyle { public func makeBody(configuration: Configuration) -> some View { HStack(alignment: .firstTextBaseline) { configuration.label diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Navigation/NavigationDestination.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Navigation/NavigationDestination.swift index fd1858d7..26b4a9ec 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Navigation/NavigationDestination.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Navigation/NavigationDestination.swift @@ -47,7 +47,7 @@ extension Backport where Wrapped: View { .environment( \.navigationDestinations, [ - .init(type: D.self): .init { destination($0 as! D) } + .init(type: D.self): .init { destination($0 as! D) }, ] ) } @@ -96,8 +96,8 @@ private struct NavigationDestinationsEnvironmentKey: EnvironmentKey { } @available(macOS 10.15, *) -extension EnvironmentValues { - fileprivate var navigationDestinations: [AnyMetaType: DestinationView] { +private extension EnvironmentValues { + var navigationDestinations: [AnyMetaType: DestinationView] { get { self[NavigationDestinationsEnvironmentKey.self] } set { var current = self[NavigationDestinationsEnvironmentKey.self] @@ -127,8 +127,8 @@ extension AnyMetaType: Hashable { } @available(macOS 10.15, *) -extension Dictionary { - fileprivate subscript(_ key: Any.Type) -> Value? where Key == AnyMetaType { +private extension Dictionary { + subscript(_ key: Any.Type) -> Value? where Key == AnyMetaType { get { self[.init(type: key)] } _modify { yield &self[.init(type: key)] } } diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/OpenURL/OpenURL.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/OpenURL/OpenURL.swift index cb020df9..d5472bf3 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/OpenURL/OpenURL.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/OpenURL/OpenURL.swift @@ -132,16 +132,16 @@ extension Backport where Wrapped == Any { let result = handler(url).value switch result { - case .handled, .discarded: break - case let .systemAction(updatedUrl): - let resolved = updatedUrl ?? url - #if os(macOS) - NSWorkspace.shared.open(resolved) - #elseif os(iOS) || os(tvOS) - UIApplication.shared.open(resolved) - #else - WKExtension.shared().openSystemURL(resolved) - #endif + case .handled, .discarded: break + case let .systemAction(updatedUrl): + let resolved = updatedUrl ?? url + #if os(macOS) + NSWorkspace.shared.open(resolved) + #elseif os(iOS) || os(tvOS) + UIApplication.shared.open(resolved) + #else + WKExtension.shared().openSystemURL(resolved) + #endif } return result @@ -169,8 +169,8 @@ private struct BackportOpenURLKey: EnvironmentKey { } @available(macOS 10.15, *) -extension EnvironmentValues { - public var backportOpenURL: Backport.OpenURLAction { +public extension EnvironmentValues { + var backportOpenURL: Backport.OpenURLAction { get { self[BackportOpenURLKey.self] } set { self[BackportOpenURLKey.self] = newValue } } diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Overlay/Overlay.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Overlay/Overlay.swift index 0df902f0..62aa2941 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Overlay/Overlay.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Overlay/Overlay.swift @@ -5,7 +5,7 @@ import SwiftUI @available(macOS 10.15, *) -extension Backport where Wrapped: View { +public extension Backport where Wrapped: View { /// Layers the views that you specify in front of this view. /// /// Use this modifier to place one or more views in front of another view. @@ -121,8 +121,7 @@ extension Backport where Wrapped: View { /// The last view that you list appears at the front of the stack. /// /// - Returns: A view that uses the specified content as a foreground. - public func overlay(alignment: Alignment = .center, @ViewBuilder _ content: () -> Content) -> some View - { + func overlay(alignment: Alignment = .center, @ViewBuilder _ content: () -> Content) -> some View { self.content.overlay(content(), alignment: alignment) } } diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/ProgressView.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/ProgressView.swift index de3fc4df..e619a734 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/ProgressView.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/ProgressView.swift @@ -104,7 +104,8 @@ extension Backport.ProgressView where Wrapped == Any { /// task, meaning the task is complete if `value` equals `total`. The /// default value is `1.0`. public init(value: V?, total: V = 1.0) - where Label == EmptyView, CurrentValueLabel == EmptyView, V: BinaryFloatingPoint { + where Label == EmptyView, CurrentValueLabel == EmptyView, V: BinaryFloatingPoint + { if let value = value { config = .init(fractionCompleted: Double(value) / Double(total), preferredKind: .linear, max: Double(total)) } else { @@ -130,7 +131,8 @@ extension Backport.ProgressView where Wrapped == Any { /// - label: A view builder that creates a view that describes the task /// in progress. public init(value: V?, total: V = 1.0, @ViewBuilder label: () -> Label) - where CurrentValueLabel == EmptyView, V: BinaryFloatingPoint { + where CurrentValueLabel == EmptyView, V: BinaryFloatingPoint + { if let value = value { config = .init( fractionCompleted: Double(value) / Double(total), label: .init(content: label()), preferredKind: .linear @@ -200,7 +202,8 @@ extension Backport.ProgressView where Wrapped == Any { /// task, meaning the task is complete if `value` equals `total`. The /// default value is `1.0`. public init(_ titleKey: LocalizedStringKey, value: V?, total: V = 1.0) - where Label == Text, CurrentValueLabel == EmptyView, V: BinaryFloatingPoint { + where Label == Text, CurrentValueLabel == EmptyView, V: BinaryFloatingPoint + { if let value = value { config = .init( fractionCompleted: Double(value) / Double(total), label: .init(content: Text(titleKey)), preferredKind: .linear, @@ -237,7 +240,8 @@ extension Backport.ProgressView where Wrapped == Any { /// task, meaning the task is complete if `value` equals `total`. The /// default value is `1.0`. public init(_ title: S, value: V?, total: V = 1.0) - where Label == Text, CurrentValueLabel == EmptyView, S: StringProtocol, V: BinaryFloatingPoint { + where Label == Text, CurrentValueLabel == EmptyView, S: StringProtocol, V: BinaryFloatingPoint + { if let value = value { config = .init( fractionCompleted: Double(value) / Double(total), label: .init(content: Text(title)), preferredKind: .linear, diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/CircularProgressViewStyle.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/CircularProgressViewStyle.swift index ea624cd4..aa5e02e1 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/CircularProgressViewStyle.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/CircularProgressViewStyle.swift @@ -42,8 +42,8 @@ extension Backport where Wrapped == Any { } @available(macOS 10.15, *) -extension BackportProgressViewStyle where Self == Backport.CircularProgressViewStyle { - public static var circular: Self { .init() } +public extension BackportProgressViewStyle where Self == Backport.CircularProgressViewStyle { + static var circular: Self { .init() } } #if os(macOS) diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/DefaultProgressViewStyle.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/DefaultProgressViewStyle.swift index a979c922..a80edbbe 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/DefaultProgressViewStyle.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/DefaultProgressViewStyle.swift @@ -31,24 +31,24 @@ extension Backport where Wrapped == Any { /// its preferred progress type. public func makeBody(configuration: Configuration) -> some View { switch configuration.preferredKind { - case .circular: - Backport.CircularProgressViewStyle().makeBody(configuration: configuration) - case .linear: - #if os(iOS) - if configuration.fractionCompleted == nil { - Backport.CircularProgressViewStyle().makeBody(configuration: configuration) - } else { - Backport.LinearProgressViewStyle().makeBody(configuration: configuration) - } - #else + case .circular: + Backport.CircularProgressViewStyle().makeBody(configuration: configuration) + case .linear: + #if os(iOS) + if configuration.fractionCompleted == nil { + Backport.CircularProgressViewStyle().makeBody(configuration: configuration) + } else { Backport.LinearProgressViewStyle().makeBody(configuration: configuration) - #endif + } + #else + Backport.LinearProgressViewStyle().makeBody(configuration: configuration) + #endif } } } } @available(macOS 10.15, *) -extension BackportProgressViewStyle where Self == Backport.DefaultProgressViewStyle { - public static var automatic: Self { .init() } +public extension BackportProgressViewStyle where Self == Backport.DefaultProgressViewStyle { + static var automatic: Self { .init() } } diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/LinearProgressViewStyle.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/LinearProgressViewStyle.swift index dd098d02..c11e7aa0 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/LinearProgressViewStyle.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/LinearProgressViewStyle.swift @@ -63,8 +63,8 @@ extension Backport where Wrapped == Any { } @available(macOS 10.15, *) -extension BackportProgressViewStyle where Self == Backport.LinearProgressViewStyle { - public static var linear: Self { .init() } +public extension BackportProgressViewStyle where Self == Backport.LinearProgressViewStyle { + static var linear: Self { .init() } } #if os(macOS) diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook+iOS.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook+iOS.swift index 06b915f5..dba98d2b 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook+iOS.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook+iOS.swift @@ -9,7 +9,8 @@ import SwiftUI final class PreviewController: UIViewController, UIAdaptivePresentationControllerDelegate, QLPreviewControllerDelegate, QLPreviewControllerDataSource - where Items: RandomAccessCollection, Items.Element == URL { + where Items: RandomAccessCollection, Items.Element == URL + { var items: Items var selection: Binding { @@ -34,14 +35,14 @@ import SwiftUI private func updateControllerLifecycle(from oldValue: Items.Element?, to newValue: Items.Element?) { switch (oldValue, newValue) { - case (.none, .some): - presentController() - case (.some, .some): - updateController() - case (.some, .none): - dismissController() - case (.none, .none): - break + case (.none, .some): + presentController() + case (.some, .some): + updateController() + case (.some, .none): + dismissController() + case (.none, .none): + break } } diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook+macOS.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook+macOS.swift index d445195d..74731927 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook+macOS.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook+macOS.swift @@ -10,7 +10,8 @@ import SwiftUI @available(macOS 10.15, *) final class PreviewController: NSViewController, QLPreviewPanelDataSource, QLPreviewPanelDelegate - where Items: RandomAccessCollection, Items.Element == URL { + where Items: RandomAccessCollection, Items.Element == URL + { private let panel = QLPreviewPanel.shared()! private weak var windowResponder: NSResponder? @@ -27,14 +28,14 @@ import SwiftUI private func updateControllerLifecycle(from oldValue: Items.Element?, to newValue: Items.Element?) { switch (oldValue, newValue) { - case (.none, .some): - present() - case (.some, .some): - update() - case (.some, .none): - dismiss() - case (.none, .none): - break + case (.none, .some): + present() + case (.some, .some): + update() + case (.some, .none): + dismiss() + case (.none, .none): + break } } diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook.swift index 575a31ae..e9d3f962 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook.swift @@ -29,7 +29,8 @@ extension Backport where Wrapped: View { /// /// - Returns: A view that presents the preview of the contents of the URL. public func quickLookPreview(_ selection: Binding, in items: Items) -> some View - where Items: RandomAccessCollection, Items.Element == URL { + where Items: RandomAccessCollection, Items.Element == URL + { #if os(iOS) || os(macOS) content.background(QuicklookSheet(selection: selection, items: items)) #else @@ -64,7 +65,8 @@ extension Backport where Wrapped: View { @available(macOS 10.15, *) private struct QuicklookSheet: NSViewControllerRepresentable - where Items: RandomAccessCollection, Items.Element == URL { + where Items: RandomAccessCollection, Items.Element == URL + { let selection: Binding let items: Items @@ -81,7 +83,8 @@ extension Backport where Wrapped: View { #elseif os(iOS) private struct QuicklookSheet: UIViewControllerRepresentable - where Items: RandomAccessCollection, Items.Element == URL { + where Items: RandomAccessCollection, Items.Element == URL + { let selection: Binding let items: Items diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/RequestReview/RequestReview.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/RequestReview/RequestReview.swift index 53560e7d..3eb0d135 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/RequestReview/RequestReview.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/RequestReview/RequestReview.swift @@ -3,14 +3,14 @@ import SwiftUI #if os(iOS) || os(macOS) @available(macOS 10.15, *) - extension EnvironmentValues { + public extension EnvironmentValues { /// An instance that tells StoreKit to request an App Store rating or review from the user, if appropriate. /// Read the requestReview environment value to get an instance of this structure for a given Environment. Call the instance to tell StoreKit to ask the user to rate or review your app, if appropriate. You call the instance directly because it defines a callAsFunction() method that Swift calls when you call the instance. /// /// Although you normally call this instance to request a review when it makes sense in the user experience flow of your app, the App Store policy governs the actual display of the rating and review request view. Because calling this instance may not present an alert, don’t call it in response to a user action, such as a button tap. /// /// > When you call this instance while your app is in development mode, the system always displays a rating and review request view so you can test the user interface and experience. This instance has no effect when you call it in an app that you distribute using TestFlight. - @MainActor public var backportRequestReview: Backport.RequestReviewAction { .init() } + @MainActor var backportRequestReview: Backport.RequestReviewAction { .init() } } /// An instance that tells StoreKit to request an App Store rating or review from the user, if appropriate. diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Section/Section.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Section/Section.swift index 6523fb27..d071b231 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Section/Section.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Section/Section.swift @@ -33,13 +33,13 @@ extension Backport where Wrapped == Any { } @available(macOS 10.15, *) -extension Backport.Section where Wrapped == Any, Parent == Text, Footer == EmptyView { +public extension Backport.Section where Wrapped == Any, Parent == Text, Footer == EmptyView { /// Creates a section with the provided section content. /// - Parameters: /// - titleKey: The key for the section's localized title, which describes /// the contents of the section. /// - content: The section's content. - public init(_ titleKey: LocalizedStringKey, @ViewBuilder content: @escaping () -> Content) { + init(_ titleKey: LocalizedStringKey, @ViewBuilder content: @escaping () -> Content) { header = { Text(titleKey) } self.content = content footer = { EmptyView() } @@ -50,7 +50,7 @@ extension Backport.Section where Wrapped == Any, Parent == Text, Footer == Empty /// - Parameters: /// - title: A string that describes the contents of the section. /// - content: The section's content. - public init(_ title: S, @ViewBuilder content: @escaping () -> Content) where S: StringProtocol { + init(_ title: S, @ViewBuilder content: @escaping () -> Content) where S: StringProtocol { header = { Text(title) } self.content = content footer = { EmptyView() } diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Transition/PushTransition.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Transition/PushTransition.swift index ebf92e65..4fed461c 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Transition/PushTransition.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Transition/PushTransition.swift @@ -5,7 +5,7 @@ import SwiftUI @available(macOS 10.15, *) -extension Backport where Wrapped == AnyTransition { +public extension Backport where Wrapped == AnyTransition { /// Creates a transition that when added to a view will animate the view’s insertion by moving it in from the specified edge while fading it in, and animate its removal by moving it out towards the opposite edge and fading it out. /// - Parameter edge: the edge from which the view will be animated in. /// - Returns: A transition that animates a view by moving and fading it. @@ -13,17 +13,17 @@ extension Backport where Wrapped == AnyTransition { @available(watchOS, deprecated: 9.0) @available(macOS, deprecated: 13.0) @available(tvOS, deprecated: 16.0) - public func push(from edge: Edge) -> AnyTransition { + func push(from edge: Edge) -> AnyTransition { var oppositeEdge: Edge switch edge { - case .top: - oppositeEdge = .bottom - case .leading: - oppositeEdge = .trailing - case .bottom: - oppositeEdge = .top - case .trailing: - oppositeEdge = .leading + case .top: + oppositeEdge = .bottom + case .leading: + oppositeEdge = .trailing + case .bottom: + oppositeEdge = .top + case .trailing: + oppositeEdge = .leading } return .asymmetric( diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/Detents.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/Detents.swift index 4bb38a96..01b0f7ba 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/Detents.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/Detents.swift @@ -178,10 +178,10 @@ extension Backport where Wrapped == Any { @available(macOS 10.15, *) public static func < (lhs: PresentationDetent, rhs: PresentationDetent) -> Bool { switch (lhs, rhs) { - case (.large, .medium): - return false - default: - return true + case (.large, .medium): + return false + default: + return true } } } @@ -189,8 +189,8 @@ extension Backport where Wrapped == Any { #if os(iOS) @available(iOS 15, *) - extension Backport where Wrapped == Any { - fileprivate struct Representable: UIViewControllerRepresentable { + fileprivate extension Backport where Wrapped == Any { + struct Representable: UIViewControllerRepresentable { let detents: Set.PresentationDetent> let selection: Binding.PresentationDetent>? let largestUndimmed: Backport.PresentationDetent? @@ -207,8 +207,8 @@ extension Backport where Wrapped == Any { @available(macOS 10.15, *) @available(iOS 15, *) - extension Backport.Representable { - fileprivate final class Controller: UIViewController, UISheetPresentationControllerDelegate { + fileprivate extension Backport.Representable { + final class Controller: UIViewController, UISheetPresentationControllerDelegate { var detents: Set.PresentationDetent> var selection: Binding.PresentationDetent>? var largestUndimmed: Backport.PresentationDetent? @@ -256,10 +256,10 @@ extension Backport where Wrapped == Any { controller.animateChanges { controller.detents = detents.sorted().map { switch $0 { - case .medium: - return .medium() - default: - return .large() + case .medium: + return .medium() + default: + return .large() } } diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/DragIndicator.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/DragIndicator.swift index 9605e380..8794ad40 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/DragIndicator.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/DragIndicator.swift @@ -48,8 +48,8 @@ extension Backport where Wrapped: View { #if os(iOS) @available(iOS 15, *) @available(macOS 10.15, *) - extension Backport where Wrapped == Any { - fileprivate struct Representable: UIViewControllerRepresentable { + fileprivate extension Backport where Wrapped == Any { + struct Representable: UIViewControllerRepresentable { let visibility: Backport.Visibility func makeUIViewController(context _: Context) -> Backport.Representable.Controller { @@ -64,8 +64,8 @@ extension Backport where Wrapped: View { @available(macOS 10.15, *) @available(iOS 15, *) - extension Backport.Representable { - fileprivate final class Controller: UIViewController { + fileprivate extension Backport.Representable { + final class Controller: UIViewController { var visibility: Backport.Visibility @available(macOS 10.15, *) diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/InteractiveDetent.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/InteractiveDetent.swift index c80b39ca..a8126360 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/InteractiveDetent.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/InteractiveDetent.swift @@ -53,8 +53,8 @@ extension Backport where Wrapped: View { #if os(iOS) @available(iOS 15, *) - extension Backport where Wrapped == Any { - fileprivate struct Representable: UIViewControllerRepresentable { + fileprivate extension Backport where Wrapped == Any { + struct Representable: UIViewControllerRepresentable { let identifier: Backport.PresentationDetent.Identifier? func makeUIViewController(context _: Context) -> Backport.Representable.Controller { @@ -68,8 +68,8 @@ extension Backport where Wrapped: View { } @available(iOS 15, *) - extension Backport.Representable { - fileprivate final class Controller: UIViewController { + fileprivate extension Backport.Representable { + final class Controller: UIViewController { var identifier: Backport.PresentationDetent.Identifier? init(identifier: Backport.PresentationDetent.Identifier?) { diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/InteractiveDismiss.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/InteractiveDismiss.swift index d05e7da7..fc6d5ca7 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/InteractiveDismiss.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/InteractiveDismiss.swift @@ -5,7 +5,7 @@ import SwiftUI @available(macOS 10.15, *) -extension Backport where Wrapped: View { +public extension Backport where Wrapped: View { /// Conditionally prevents interactive dismissal of a popover or a sheet. /// /// Users can dismiss certain kinds of presentations using built-in @@ -76,7 +76,7 @@ extension Backport where Wrapped: View { @available(tvOS, deprecated: 16) @available(macOS, deprecated: 13) @available(watchOS, deprecated: 9) - public func interactiveDismissDisabled(_ isDisabled: Bool = true) -> some View { + func interactiveDismissDisabled(_ isDisabled: Bool = true) -> some View { #if os(iOS) if #available(iOS 15, *) { content.background(Backport.Representable(isModal: isDisabled, onAttempt: nil)) @@ -157,7 +157,7 @@ extension Backport where Wrapped: View { /// - Parameter onAttempt: A closure that will be called when an interactive dismiss attempt occurs. /// You can use this as an opportunity to present an confirmation or prompt to the user. @ViewBuilder - public func interactiveDismissDisabled(_ isDisabled: Bool = true, onAttempt: @escaping () -> Void) -> some View { + func interactiveDismissDisabled(_ isDisabled: Bool = true, onAttempt: @escaping () -> Void) -> some View { #if os(iOS) if #available(iOS 15, *) { content.background(Backport.Representable(isModal: isDisabled, onAttempt: onAttempt)) @@ -172,8 +172,8 @@ extension Backport where Wrapped: View { #if os(iOS) @available(macOS 10.15, *) - extension Backport where Wrapped == Any { - fileprivate struct Representable: UIViewControllerRepresentable { + fileprivate extension Backport where Wrapped == Any { + struct Representable: UIViewControllerRepresentable { let isModal: Bool let onAttempt: (() -> Void)? @@ -190,8 +190,8 @@ extension Backport where Wrapped: View { } @available(macOS 10.15, *) - extension Backport.Representable { - fileprivate final class Controller: UIViewController, UIAdaptivePresentationControllerDelegate { + fileprivate extension Backport.Representable { + final class Controller: UIViewController, UIAdaptivePresentationControllerDelegate { var isModal: Bool var onAttempt: (() -> Void)? weak var _delegate: UIAdaptivePresentationControllerDelegate? diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScaledMetric/ScaledMetric.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScaledMetric/ScaledMetric.swift index d0d82f47..2b40cbb7 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScaledMetric/ScaledMetric.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScaledMetric/ScaledMetric.swift @@ -24,7 +24,7 @@ extension Backport where Wrapped == Any { public var wrappedValue: Value { #if os(iOS) || os(tvOS) let traits = UITraitCollection(traitsFrom: [ - UITraitCollection(preferredContentSizeCategory: UIContentSizeCategory(sizeCategory: sizeCategory)) + UITraitCollection(preferredContentSizeCategory: UIContentSizeCategory(sizeCategory: sizeCategory)), ]) return Value(metrics.scaledValue(for: CGFloat(baseValue), compatibleWith: traits)) @@ -59,22 +59,22 @@ extension Backport where Wrapped == Any { } #if os(iOS) || os(tvOS) - extension UIContentSizeCategory { - fileprivate init(sizeCategory: ContentSizeCategory?) { + fileprivate extension UIContentSizeCategory { + init(sizeCategory: ContentSizeCategory?) { switch sizeCategory { - case .accessibilityExtraExtraExtraLarge: self = .accessibilityExtraExtraExtraLarge - case .accessibilityExtraExtraLarge: self = .accessibilityExtraExtraLarge - case .accessibilityExtraLarge: self = .accessibilityExtraLarge - case .accessibilityLarge: self = .accessibilityLarge - case .accessibilityMedium: self = .accessibilityMedium - case .extraExtraExtraLarge: self = .extraExtraExtraLarge - case .extraExtraLarge: self = .extraExtraLarge - case .extraLarge: self = .extraLarge - case .extraSmall: self = .extraSmall - case .large: self = .large - case .medium: self = .medium - case .small: self = .small - default: self = .unspecified + case .accessibilityExtraExtraExtraLarge: self = .accessibilityExtraExtraExtraLarge + case .accessibilityExtraExtraLarge: self = .accessibilityExtraExtraLarge + case .accessibilityExtraLarge: self = .accessibilityExtraLarge + case .accessibilityLarge: self = .accessibilityLarge + case .accessibilityMedium: self = .accessibilityMedium + case .extraExtraExtraLarge: self = .extraExtraExtraLarge + case .extraExtraLarge: self = .extraExtraLarge + case .extraLarge: self = .extraLarge + case .extraSmall: self = .extraSmall + case .large: self = .large + case .medium: self = .medium + case .small: self = .small + default: self = .unspecified } } } diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/ScrollDismissesKeyboardMode.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/ScrollDismissesKeyboardMode.swift index 16742319..f5880950 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/ScrollDismissesKeyboardMode.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/ScrollDismissesKeyboardMode.swift @@ -27,10 +27,10 @@ extension Backport where Wrapped == Any { #if os(iOS) var scrollViewDismissMode: UIScrollView.KeyboardDismissMode { switch dismissMode { - case .automatic: return .none - case .immediately: return .onDrag - case .interactively: return .interactive - case .never: return .none + case .automatic: return .none + case .immediately: return .onDrag + case .interactively: return .interactive + case .never: return .none } } #endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/Cells/UICollectionViewCell.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/Cells/UICollectionViewCell.swift index 375b2ece..8402d873 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/Cells/UICollectionViewCell.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/Cells/UICollectionViewCell.swift @@ -22,7 +22,7 @@ import SwiftUI /// Setting a content configuration replaces the existing contentView of the /// cell with a new content view instance from the configuration. public var contentConfiguration: BackportUIContentConfiguration? { - get { nil } // we can't really support anything here, so for now we'll return nil + get { nil } // we can't really support anything here, so for now we'll return nil nonmutating set { content.configuredView?.removeFromSuperview() @@ -39,8 +39,8 @@ import SwiftUI let insets = Mirror(reflecting: configuration) - .children.first(where: { $0.label == "insets" })?.value as? ProposedInsets - ?? .unspecified + .children.first(where: { $0.label == "insets" })?.value as? ProposedInsets + ?? .unspecified insets.top.flatMap { contentView.directionalLayoutMargins.top = $0 } insets.bottom.flatMap { contentView.directionalLayoutMargins.bottom = $0 } diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/Cells/UITableViewCell.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/Cells/UITableViewCell.swift index 13265d51..f8c6155a 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/Cells/UITableViewCell.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/Cells/UITableViewCell.swift @@ -22,7 +22,7 @@ import SwiftUI /// Setting a content configuration replaces the existing contentView of the /// cell with a new content view instance from the configuration. public var contentConfiguration: BackportUIContentConfiguration? { - get { nil } // we can't really support anything here, so for now we'll return nil + get { nil } // we can't really support anything here, so for now we'll return nil nonmutating set { content.configuredView?.removeFromSuperview() @@ -39,8 +39,8 @@ import SwiftUI let insets = Mirror(reflecting: configuration) - .children.first(where: { $0.label == "insets" })?.value as? ProposedInsets - ?? .unspecified + .children.first(where: { $0.label == "insets" })?.value as? ProposedInsets + ?? .unspecified insets.top.flatMap { contentView.directionalLayoutMargins.top = $0 } insets.bottom.flatMap { contentView.directionalLayoutMargins.bottom = $0 } diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/UIHostingConfiguration.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/UIHostingConfiguration.swift index e8517930..fa45cc8b 100755 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/UIHostingConfiguration.swift +++ b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/UIHostingConfiguration.swift @@ -36,7 +36,8 @@ import SwiftUI } */ public struct UIHostingConfiguration: BackportUIContentConfiguration - where Label: View, Background: View { + where Label: View, Background: View + { var content: Label var background: AnyView? var insets: ProposedInsets @@ -57,7 +58,8 @@ import SwiftUI /// - Parameter background: The contents of the SwiftUI hierarchy to be /// shown inside the background of the cell. public func background(@ViewBuilder background: () -> B) -> Backport.UIHostingConfiguration - where B: View { + where B: View + { .init(content: content, background: AnyView(background()), insets: insets, minSize: minSize) } @@ -95,7 +97,7 @@ import SwiftUI } } - extension Backport.UIHostingConfiguration { + public extension Backport.UIHostingConfiguration { /// Sets the margins around the content of the configuration. /// /// Use this modifier to replace the default margins applied to the root of @@ -112,7 +114,7 @@ import SwiftUI /// use the system default values. The default value is /// ``Edge/Set/all``. /// - length: The amount to apply. - public func margins(_ edges: Edge.Set = .all, _ length: CGFloat) -> Self { + func margins(_ edges: Edge.Set = .all, _ length: CGFloat) -> Self { var view = self if edges.contains(.leading) { view.insets.leading = length } if edges.contains(.trailing) { view.insets.trailing = length } @@ -138,7 +140,7 @@ import SwiftUI /// use the system default values. The default value is /// ``Edge/Set/all``. /// - insets: The insets to apply. - public func margins(_ edges: Edge.Set = .all, _ insets: EdgeInsets) -> Self { + func margins(_ edges: Edge.Set = .all, _ insets: EdgeInsets) -> Self { var view = self if edges.contains(.leading) { view.insets.leading = insets.leading } if edges.contains(.trailing) { view.insets.trailing = insets.trailing } diff --git a/Packages/Sindresorhus_SSPreferences/Package.swift b/Packages/Sindresorhus_SSPreferences/Package.swift index b5e9504a..f8e41f73 100644 --- a/Packages/Sindresorhus_SSPreferences/Package.swift +++ b/Packages/Sindresorhus_SSPreferences/Package.swift @@ -4,19 +4,19 @@ import PackageDescription let package = Package( name: "SSPreferences", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "SSPreferences", targets: [ - "SSPreferences" + "SSPreferences", ] - ) + ), ], targets: [ .target( name: "SSPreferences" - ) + ), ] ) diff --git a/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/Container.swift b/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/Container.swift index 1f9a9f2a..960d06d0 100755 --- a/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/Container.swift +++ b/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/Container.swift @@ -5,12 +5,12 @@ import SwiftUI @available(macOS 10.15, *) -extension SSPreferences { +public extension SSPreferences { /** Function builder for `Preferences` components used in order to restrict types of child views to be of type `Section`. */ @resultBuilder - public enum SectionBuilder { + enum SectionBuilder { public static func buildBlock(_ sections: Section...) -> [Section] { sections } @@ -19,7 +19,7 @@ extension SSPreferences { /** A view which holds `Preferences.Section` views and does all the alignment magic similar to `NSGridView` from AppKit. */ - public struct Container: View { + struct Container: View { private let sectionBuilder: () -> [Section] private let contentWidth: Double private let minimumLabelWidth: Double @@ -49,7 +49,7 @@ extension SSPreferences { let sections = sectionBuilder() return VStack(alignment: .preferenceSectionLabel) { - ForEach(0..: View, PreferencePaneConvertible { + struct Pane: View, PreferencePaneConvertible { let identifier: PaneIdentifier let title: String let toolbarIcon: NSImage @@ -49,7 +49,7 @@ extension SSPreferences { /** Hosting controller enabling `Preferences.Pane` to be used alongside AppKit `NSViewController`'s. */ - public final class PaneHostingController: NSHostingController, PreferencePane { + final class PaneHostingController: NSHostingController, PreferencePane { public let preferencePaneIdentifier: PaneIdentifier public let preferencePaneTitle: String public let toolbarItemIcon: NSImage @@ -84,11 +84,11 @@ extension SSPreferences { } @available(macOS 10.15, *) -extension View { +public extension View { /** Applies font and color for a label used for describing a preference. */ - public func preferenceDescription() -> some View { + func preferenceDescription() -> some View { font(.system(size: 11.0)) // TODO: Use `.foregroundStyle` when targeting macOS 12. .foregroundColor(.secondary) diff --git a/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/PreferencePane.swift b/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/PreferencePane.swift index c4f34185..292cd645 100755 --- a/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/PreferencePane.swift +++ b/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/PreferencePane.swift @@ -4,8 +4,8 @@ import Cocoa -extension SSPreferences { - public struct PaneIdentifier: Hashable, RawRepresentable, Codable { +public extension SSPreferences { + struct PaneIdentifier: Hashable, RawRepresentable, Codable { public let rawValue: String public init(rawValue: String) { @@ -20,24 +20,24 @@ public protocol PreferencePane: NSViewController { var toolbarItemIcon: NSImage { get } } -extension PreferencePane { - public var toolbarItemIdentifier: NSToolbarItem.Identifier { +public extension PreferencePane { + var toolbarItemIdentifier: NSToolbarItem.Identifier { preferencePaneIdentifier.toolbarItemIdentifier } - public var toolbarItemIcon: NSImage { .empty } + var toolbarItemIcon: NSImage { .empty } } -extension SSPreferences.PaneIdentifier { - public init(_ rawValue: String) { +public extension SSPreferences.PaneIdentifier { + init(_ rawValue: String) { self.init(rawValue: rawValue) } - public init(fromToolbarItemIdentifier itemIdentifier: NSToolbarItem.Identifier) { + init(fromToolbarItemIdentifier itemIdentifier: NSToolbarItem.Identifier) { self.init(rawValue: itemIdentifier.rawValue) } - public var toolbarItemIdentifier: NSToolbarItem.Identifier { + var toolbarItemIdentifier: NSToolbarItem.Identifier { NSToolbarItem.Identifier(rawValue) } } diff --git a/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/PreferencesStyle.swift b/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/PreferencesStyle.swift index adbe49d9..cc506f4f 100755 --- a/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/PreferencesStyle.swift +++ b/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/PreferencesStyle.swift @@ -4,8 +4,8 @@ import Cocoa -extension SSPreferences { - public enum Style { +public extension SSPreferences { + enum Style { case toolbarItems case segmentedControl } diff --git a/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/PreferencesTabViewController.swift b/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/PreferencesTabViewController.swift index 92e60b03..af44e008 100755 --- a/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/PreferencesTabViewController.swift +++ b/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/PreferencesTabViewController.swift @@ -45,14 +45,14 @@ final class PreferencesTabViewController: NSViewController, PreferencesStyleCont toolbar.delegate = self switch style { - case .segmentedControl: - preferencesStyleController = SegmentedControlStyleViewController(preferencePanes: preferencePanes) - case .toolbarItems: - preferencesStyleController = ToolbarItemStyleViewController( - preferencePanes: preferencePanes, - toolbar: toolbar, - centerToolbarItems: false - ) + case .segmentedControl: + preferencesStyleController = SegmentedControlStyleViewController(preferencePanes: preferencePanes) + case .toolbarItems: + preferencesStyleController = ToolbarItemStyleViewController( + preferencePanes: preferencePanes, + toolbar: toolbar, + centerToolbarItems: false + ) } preferencesStyleController.delegate = self @@ -159,33 +159,31 @@ final class PreferencesTabViewController: NSViewController, PreferencesStyleCont ) { let isAnimated = options - .intersection([ - .crossfade, - .slideUp, - .slideDown, - .slideForward, - .slideBackward, - .slideLeft, - .slideRight, - ]) - .isEmpty == false + .intersection([ + .crossfade, + .slideUp, + .slideDown, + .slideForward, + .slideBackward, + .slideLeft, + .slideRight, + ]) + .isEmpty == false if isAnimated { - NSAnimationContext.runAnimationGroup( - { context in - context.allowsImplicitAnimation = true - context.duration = 0.25 - context.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) - setWindowFrame(for: toViewController, animated: true) + NSAnimationContext.runAnimationGroup({ context in + context.allowsImplicitAnimation = true + context.duration = 0.25 + context.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) + setWindowFrame(for: toViewController, animated: true) - super.transition( - from: fromViewController, - to: toViewController, - options: options, - completionHandler: completion - ) - }, completionHandler: nil - ) + super.transition( + from: fromViewController, + to: toViewController, + options: options, + completionHandler: completion + ) + }, completionHandler: nil) } else { super.transition( from: fromViewController, diff --git a/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/PreferencesWindowController.swift b/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/PreferencesWindowController.swift index 4c7f63e3..55dc64d5 100755 --- a/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/PreferencesWindowController.swift +++ b/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/PreferencesWindowController.swift @@ -27,7 +27,7 @@ public final class PreferencesWindowController: NSWindowController { private func updateToolbarVisibility() { window?.toolbar?.isVisible = (hidesToolbarForSingleItem == false) - || (tabViewController.preferencePanesCount > 1) + || (tabViewController.preferencePanesCount > 1) } public init( @@ -54,10 +54,10 @@ public final class PreferencesWindowController: NSWindowController { window.titleVisibility = { switch style { - case .toolbarItems: - return .visible - case .segmentedControl: - return preferencePanes.count <= 1 ? .visible : .hidden + case .toolbarItems: + return .visible + case .segmentedControl: + return preferencePanes.count <= 1 ? .visible : .hidden } }() @@ -122,9 +122,9 @@ public final class PreferencesWindowController: NSWindowController { } } -extension PreferencesWindowController { +public extension PreferencesWindowController { /// Returns the active pane if it responds to the given action. - public override func supplementalTarget(forAction action: Selector, sender: Any?) -> Any? { + override func supplementalTarget(forAction action: Selector, sender: Any?) -> Any? { if let target = super.supplementalTarget(forAction: action, sender: sender) { return target } @@ -134,13 +134,13 @@ extension PreferencesWindowController { } if let target = NSApp.target(forAction: action, to: activeViewController, from: sender) as? NSResponder, - target.responds(to: action) + target.responds(to: action) { return target } if let target = activeViewController.supplementalTarget(forAction: action, sender: sender) as? NSResponder, - target.responds(to: action) + target.responds(to: action) { return target } @@ -150,11 +150,11 @@ extension PreferencesWindowController { } @available(macOS 10.15, *) -extension PreferencesWindowController { +public extension PreferencesWindowController { /** Create a preferences window from only SwiftUI-based preference panes. */ - public convenience init( + convenience init( panes: [PreferencePaneConvertible], style: SSPreferences.Style = .toolbarItems, animated: Bool = true, diff --git a/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/Section.swift b/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/Section.swift index 09d32ac9..f361feee 100755 --- a/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/Section.swift +++ b/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/Section.swift @@ -5,12 +5,12 @@ import SwiftUI @available(macOS 10.15, *) -extension SSPreferences { +public extension SSPreferences { /** Represents a section with right-aligned title and optional bottom divider. */ @available(macOS 10.15, *) - public struct Section: View { + struct Section: View { /** Preference key holding max width of section labels. */ @@ -74,7 +74,7 @@ extension SSPreferences { ) { self.label = label() .overlay(LabelOverlay()) - .eraseToAnyView() // TODO: Remove use of `AnyView`. + .eraseToAnyView() // TODO: Remove use of `AnyView`. self.bottomDivider = bottomDivider self.verticalAlignment = verticalAlignment let stack = VStack(alignment: .leading) { content() } diff --git a/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/SegmentedControlStyleViewController.swift b/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/SegmentedControlStyleViewController.swift index 4d7422bb..dcdd3634 100755 --- a/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/SegmentedControlStyleViewController.swift +++ b/Packages/Sindresorhus_SSPreferences/Source/SSPreferences/SegmentedControlStyleViewController.swift @@ -61,7 +61,7 @@ final class SegmentedControlStyleViewController: NSViewController, PreferencesSt let title = preferencePane.preferencePaneTitle let titleSize = title.size( withAttributes: [ - .font: NSFont.systemFont(ofSize: NSFont.systemFontSize(for: .regular)) + .font: NSFont.systemFont(ofSize: NSFont.systemFontSize(for: .regular)), ] ) diff --git a/Packages/vChewing_CandidateWindow/Package.swift b/Packages/vChewing_CandidateWindow/Package.swift index 89b15c14..821fb7ae 100644 --- a/Packages/vChewing_CandidateWindow/Package.swift +++ b/Packages/vChewing_CandidateWindow/Package.swift @@ -4,13 +4,13 @@ import PackageDescription let package = Package( name: "CandidateWindow", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "CandidateWindow", targets: ["CandidateWindow"] - ) + ), ], dependencies: [ .package(path: "../vChewing_Shared"), diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidateCellData.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidateCellData.swift index e0e9e033..f6356142 100644 --- a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidateCellData.swift +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidateCellData.swift @@ -28,8 +28,8 @@ public class CandidateCellData: Hashable { public var displayedText: String public var size: Double { Self.unifiedSize } public var isSelected: Bool = false - public var whichRow: Int = 0 // 橫排選字窗專用 - public var whichColumn: Int = 0 // 縱排選字窗專用 + public var whichRow: Int = 0 // 橫排選字窗專用 + public var whichColumn: Int = 0 // 縱排選字窗專用 public var index: Int = 0 public var subIndex: Int = 0 @@ -149,8 +149,8 @@ public class CandidateCellData: Hashable { // MARK: - Contents specifically made for macOS 12 and newer. @available(macOS 12, *) -extension CandidateCellData { - public var attributedStringForSwiftUI: some View { +public extension CandidateCellData { + var attributedStringForSwiftUI: some View { var result: some View { ZStack(alignment: .leading) { if isSelected { @@ -179,21 +179,21 @@ extension CandidateCellData { // MARK: - Contents specifically made for macOS 10.15 and macOS 11. @available(macOS 10.15, *) -extension CandidateCellData { - public var themeColorBackports: some View { +public extension CandidateCellData { + var themeColorBackports: some View { // 設定當前高亮候選字的背景顏色。 let result: Color = { switch locale { - case "zh-Hans": return Color.red - case "zh-Hant": return Color.blue - case "ja": return Color.pink - default: return Color.accentColor + case "zh-Hans": return Color.red + case "zh-Hant": return Color.blue + case "ja": return Color.pink + default: return Color.accentColor } }() return result.opacity(0.85) } - public var attributedStringForSwiftUIBackports: some View { + var attributedStringForSwiftUIBackports: some View { var result: some View { ZStack(alignment: .leading) { if isSelected { diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift index 34a1a194..cb280677 100644 --- a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CandidatePool.swift @@ -38,73 +38,73 @@ public struct CandidatePool { public var currentLineNumber: Int { switch currentLayout { - case .horizontal: - return currentRowNumber - case .vertical: - return currentColumnNumber - @unknown default: - return 0 + case .horizontal: + return currentRowNumber + case .vertical: + return currentColumnNumber + @unknown default: + return 0 } } public var candidateLines: [[CandidateCellData]] { switch currentLayout { - case .horizontal: - return candidateRows - case .vertical: - return candidateColumns - @unknown default: - return [] + case .horizontal: + return candidateRows + case .vertical: + return candidateColumns + @unknown default: + return [] } } public var maxLineCapacity: Int { switch currentLayout { - case .horizontal: - return maxRowCapacity - case .vertical: - return maxColumnCapacity - @unknown default: - return 0 + case .horizontal: + return maxRowCapacity + case .vertical: + return maxColumnCapacity + @unknown default: + return 0 } } public var maxLinesPerPage: Int { get { switch currentLayout { - case .horizontal: - return maxRowsPerPage - case .vertical: - return maxColumnsPerPage - @unknown default: - return 0 + case .horizontal: + return maxRowsPerPage + case .vertical: + return maxColumnsPerPage + @unknown default: + return 0 } } set { switch currentLayout { - case .horizontal: - maxRowsPerPage = newValue - case .vertical: - maxColumnsPerPage = newValue - @unknown default: - return + case .horizontal: + maxRowsPerPage = newValue + case .vertical: + maxColumnsPerPage = newValue + @unknown default: + return } } } public var rangeForLastPageBlanked: Range { switch currentLayout { - case .horizontal: return rangeForLastHorizontalPageBlanked - case .vertical: return rangeForLastVerticalPageBlanked - @unknown default: return 0..<0 + case .horizontal: return rangeForLastHorizontalPageBlanked + case .vertical: return rangeForLastVerticalPageBlanked + @unknown default: return 0 ..< 0 } } public var rangeForCurrentPage: Range { switch currentLayout { - case .horizontal: return rangeForCurrentHorizontalPage - case .vertical: return rangeForCurrentVerticalPage - @unknown default: return 0..<0 + case .horizontal: return rangeForCurrentHorizontalPage + case .vertical: return rangeForCurrentVerticalPage + @unknown default: return 0 ..< 0 } } @@ -176,17 +176,17 @@ public struct CandidatePool { public mutating func selectNewNeighborLine(isForward: Bool) { switch currentLayout { - case .horizontal: selectNewNeighborRow(direction: isForward ? .down : .up) - case .vertical: selectNewNeighborColumn(direction: isForward ? .right : .left) - @unknown default: break + case .horizontal: selectNewNeighborRow(direction: isForward ? .down : .up) + case .vertical: selectNewNeighborColumn(direction: isForward ? .right : .left) + @unknown default: break } } public mutating func highlight(at indexSpecified: Int) { switch currentLayout { - case .horizontal: highlightHorizontal(at: indexSpecified) - case .vertical: highlightVertical(at: indexSpecified) - @unknown default: break + case .horizontal: highlightHorizontal(at: indexSpecified) + case .vertical: highlightVertical(at: indexSpecified) + @unknown default: break } vCLog("\n" + candidateDataAll[highlightedIndex].charDescriptions) } @@ -206,102 +206,102 @@ extension CandidatePool { } private var rangeForLastHorizontalPageBlanked: Range { - 0..<(maxRowsPerPage - rangeForCurrentHorizontalPage.count) + 0 ..< (maxRowsPerPage - rangeForCurrentHorizontalPage.count) } private var rangeForLastVerticalPageBlanked: Range { - 0..<(maxColumnsPerPage - rangeForCurrentVerticalPage.count) + 0 ..< (maxColumnsPerPage - rangeForCurrentVerticalPage.count) } private var rangeForCurrentHorizontalPage: Range { - currentRowNumber.. { - currentColumnNumber..= candidateRows.count - 1 { currentRowNumber = candidateRows.count - 1 } - if candidateRows[currentRowNumber].count != candidateRows[currentRowNumber - 1].count { - let ratio: Double = min(1, Double(currentSubIndex) / Double(candidateRows[currentRowNumber].count)) - result = Int(floor(Double(candidateRows[currentRowNumber - 1].count) * ratio)) - } - let targetRow = candidateRows[currentRowNumber - 1] - let newSubIndex = min(result, targetRow.count - 1) - highlightHorizontal(at: targetRow[newSubIndex].index) - case .down: - if currentRowNumber >= candidateRows.count - 1 { - if candidateRows.isEmpty { break } - let finalRow = candidateRows[candidateRows.count - 1] - let newSubIndex = min(currentSubIndex, finalRow.count - 1) - highlightHorizontal(at: finalRow[newSubIndex].index) - break - } - if candidateRows[currentRowNumber].count != candidateRows[currentRowNumber + 1].count { - let ratio: Double = min(1, Double(currentSubIndex) / Double(candidateRows[currentRowNumber].count)) - result = Int(floor(Double(candidateRows[currentRowNumber + 1].count) * ratio)) - } - let targetRow = candidateRows[currentRowNumber + 1] - let newSubIndex = min(result, targetRow.count - 1) - highlightHorizontal(at: targetRow[newSubIndex].index) + case .up: + if currentRowNumber <= 0 { + if candidateRows.isEmpty { break } + let firstRow = candidateRows[0] + let newSubIndex = min(currentSubIndex, firstRow.count - 1) + highlightHorizontal(at: firstRow[newSubIndex].index) + break + } + if currentRowNumber >= candidateRows.count - 1 { currentRowNumber = candidateRows.count - 1 } + if candidateRows[currentRowNumber].count != candidateRows[currentRowNumber - 1].count { + let ratio: Double = min(1, Double(currentSubIndex) / Double(candidateRows[currentRowNumber].count)) + result = Int(floor(Double(candidateRows[currentRowNumber - 1].count) * ratio)) + } + let targetRow = candidateRows[currentRowNumber - 1] + let newSubIndex = min(result, targetRow.count - 1) + highlightHorizontal(at: targetRow[newSubIndex].index) + case .down: + if currentRowNumber >= candidateRows.count - 1 { + if candidateRows.isEmpty { break } + let finalRow = candidateRows[candidateRows.count - 1] + let newSubIndex = min(currentSubIndex, finalRow.count - 1) + highlightHorizontal(at: finalRow[newSubIndex].index) + break + } + if candidateRows[currentRowNumber].count != candidateRows[currentRowNumber + 1].count { + let ratio: Double = min(1, Double(currentSubIndex) / Double(candidateRows[currentRowNumber].count)) + result = Int(floor(Double(candidateRows[currentRowNumber + 1].count) * ratio)) + } + let targetRow = candidateRows[currentRowNumber + 1] + let newSubIndex = min(result, targetRow.count - 1) + highlightHorizontal(at: targetRow[newSubIndex].index) } } private mutating func selectNewNeighborColumn(direction: HorizontalDirection) { let currentSubIndex = candidateDataAll[highlightedIndex].subIndex switch direction { - case .left: - if currentColumnNumber <= 0 { - if candidateColumns.isEmpty { break } - let firstColumn = candidateColumns[0] - let newSubIndex = min(currentSubIndex, firstColumn.count - 1) - highlightVertical(at: firstColumn[newSubIndex].index) - break - } - if currentColumnNumber >= candidateColumns.count - 1 { currentColumnNumber = candidateColumns.count - 1 } - let targetColumn = candidateColumns[currentColumnNumber - 1] - let newSubIndex = min(currentSubIndex, targetColumn.count - 1) - highlightVertical(at: targetColumn[newSubIndex].index) - case .right: - if currentColumnNumber >= candidateColumns.count - 1 { - if candidateColumns.isEmpty { break } - let finalColumn = candidateColumns[candidateColumns.count - 1] - let newSubIndex = min(currentSubIndex, finalColumn.count - 1) - highlightVertical(at: finalColumn[newSubIndex].index) - break - } - let targetColumn = candidateColumns[currentColumnNumber + 1] - let newSubIndex = min(currentSubIndex, targetColumn.count - 1) - highlightVertical(at: targetColumn[newSubIndex].index) + case .left: + if currentColumnNumber <= 0 { + if candidateColumns.isEmpty { break } + let firstColumn = candidateColumns[0] + let newSubIndex = min(currentSubIndex, firstColumn.count - 1) + highlightVertical(at: firstColumn[newSubIndex].index) + break + } + if currentColumnNumber >= candidateColumns.count - 1 { currentColumnNumber = candidateColumns.count - 1 } + let targetColumn = candidateColumns[currentColumnNumber - 1] + let newSubIndex = min(currentSubIndex, targetColumn.count - 1) + highlightVertical(at: targetColumn[newSubIndex].index) + case .right: + if currentColumnNumber >= candidateColumns.count - 1 { + if candidateColumns.isEmpty { break } + let finalColumn = candidateColumns[candidateColumns.count - 1] + let newSubIndex = min(currentSubIndex, finalColumn.count - 1) + highlightVertical(at: finalColumn[newSubIndex].index) + break + } + let targetColumn = candidateColumns[currentColumnNumber + 1] + let newSubIndex = min(currentSubIndex, targetColumn.count - 1) + highlightVertical(at: targetColumn[newSubIndex].index) } } private mutating func highlightHorizontal(at indexSpecified: Int) { var indexSpecified = indexSpecified highlightedIndex = indexSpecified - if !(0.. Int { let arrCurrentLine = thePool.candidateLines[thePool.currentLineNumber] - if !(0.. 0 { ForEach(Array(thePool.rangeForLastPageBlanked.enumerated()), id: \.offset) { loopIndex, _ in VStack(alignment: .leading, spacing: 0) { - ForEach(0.. 0 { ForEach(Array(thePool.rangeForLastPageBlanked.enumerated()), id: \.offset) { loopIndex, _ in VStack(alignment: .leading, spacing: 0) { - ForEach(0.. String { +public extension NSApplication { + static func shell(_ command: String) throws -> String { let task = Process() let pipe = Pipe() @@ -59,10 +59,10 @@ extension NSApplication { } } -extension NSApplication { +public extension NSApplication { // MARK: - System Dark Mode Status Detector. - public static var isDarkMode: Bool { + static var isDarkMode: Bool { if #unavailable(macOS 10.14) { return false } if #available(macOS 10.15, *) { let appearanceDescription = NSApp.effectiveAppearance.debugDescription @@ -76,23 +76,23 @@ extension NSApplication { // MARK: - Tell whether this IME is running with Root privileges. - public static var isSudoMode: Bool { + static var isSudoMode: Bool { NSUserName() == "root" } } // MARK: - Real Home Dir for Sandboxed Apps -extension FileManager { - public static let realHomeDir = URL( +public extension FileManager { + static let realHomeDir = URL( fileURLWithFileSystemRepresentation: getpwuid(getuid()).pointee.pw_dir, isDirectory: true, relativeTo: nil ) } // MARK: - Trash a file if it exists. -extension FileManager { - @discardableResult public static func trashTargetIfExists(_ path: String) -> Bool { +public extension FileManager { + @discardableResult static func trashTargetIfExists(_ path: String) -> Bool { do { if FileManager.default.fileExists(atPath: path) { // 塞入垃圾桶 @@ -113,9 +113,9 @@ extension FileManager { // MARK: - Memory Footprint Calculator // Ref: https://developer.apple.com/forums/thread/105088?answerId=357415022#357415022 -extension NSApplication { +public extension NSApplication { /// The memory footprint of the current application in bytes. - public static var memoryFootprint: UInt64? { + static var memoryFootprint: UInt64? { // The `TASK_VM_INFO_COUNT` and `TASK_VM_INFO_REV1_COUNT` macros are too // complex for the Swift C importer, so we have to define them ourselves. let tskVMInfoCount = mach_msg_type_number_t( @@ -136,20 +136,20 @@ extension NSApplication { // MARK: - Check whether current date is the given date. -extension Date { +public extension Date { /// Check whether current date is the given date. /// - Parameter dateDigits: `yyyyMMdd`, 8-digit integer. If only `MMdd`, then the year will be the current year. /// - Returns: The result. Will return false if the given dateDigits is invalid. - public static func isTodayTheDate(from dateDigits: Int) -> Bool { + static func isTodayTheDate(from dateDigits: Int) -> Bool { let currentYear = Self.currentYear var dateDigits = dateDigits let strDateDigits = dateDigits.description switch strDateDigits.count { - case 3, 4: dateDigits = currentYear * 10000 + dateDigits - case 8: - if let theHighest = strDateDigits.first, "12".contains(theHighest) { break } - return false - default: return false + case 3, 4: dateDigits = currentYear * 10000 + dateDigits + case 8: + if let theHighest = strDateDigits.first, "12".contains(theHighest) { break } + return false + default: return false } let formatter = DateFormatter() formatter.dateFormat = "yyyyMMdd" @@ -162,7 +162,7 @@ extension Date { return false } - public static var currentYear: Int { + static var currentYear: Int { let formatter = DateFormatter() formatter.dateFormat = "yyyy" return (Int(formatter.string(from: Date())) ?? 1970) diff --git a/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_NSEvent.swift b/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_NSEvent.swift index d89edd7b..f5d93bc4 100644 --- a/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_NSEvent.swift +++ b/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_NSEvent.swift @@ -11,8 +11,8 @@ import IMKUtils // MARK: - NSEvent Extension - Reconstructors -extension NSEvent { - public func reinitiate( +public extension NSEvent { + func reinitiate( with type: NSEvent.EventType? = nil, location: NSPoint? = nil, modifierFlags: NSEvent.ModifierFlags? = nil, @@ -44,28 +44,28 @@ extension NSEvent { /// 自 Emacs 熱鍵的 NSEvent 翻譯回標準 NSEvent。失敗的話則會返回原始 NSEvent 自身。 /// - Parameter isVerticalTyping: 是否按照縱排來操作。 /// - Returns: 翻譯結果。失敗的話則返回翻譯原文。 - public func convertFromEmacsKeyEvent(isVerticalContext: Bool) -> NSEvent { + func convertFromEmacsKeyEvent(isVerticalContext: Bool) -> NSEvent { guard isEmacsKey else { return self } let newKeyCode: UInt16 = { switch isVerticalContext { - case false: return EmacsKey.charKeyMapHorizontal[charCode] ?? 0 - case true: return EmacsKey.charKeyMapVertical[charCode] ?? 0 + case false: return EmacsKey.charKeyMapHorizontal[charCode] ?? 0 + case true: return EmacsKey.charKeyMapVertical[charCode] ?? 0 } }() guard newKeyCode != 0 else { return self } let newCharScalar: Unicode.Scalar = { switch charCode { - case 6: - return isVerticalContext - ? NSEvent.SpecialKey.downArrow.unicodeScalar : NSEvent.SpecialKey.rightArrow.unicodeScalar - case 2: - return isVerticalContext - ? NSEvent.SpecialKey.upArrow.unicodeScalar : NSEvent.SpecialKey.leftArrow.unicodeScalar - case 1: return NSEvent.SpecialKey.home.unicodeScalar - case 5: return NSEvent.SpecialKey.end.unicodeScalar - case 4: return NSEvent.SpecialKey.deleteForward.unicodeScalar // Use "deleteForward" for PC delete. - case 22: return NSEvent.SpecialKey.pageDown.unicodeScalar - default: return .init(0) + case 6: + return isVerticalContext + ? NSEvent.SpecialKey.downArrow.unicodeScalar : NSEvent.SpecialKey.rightArrow.unicodeScalar + case 2: + return isVerticalContext + ? NSEvent.SpecialKey.upArrow.unicodeScalar : NSEvent.SpecialKey.leftArrow.unicodeScalar + case 1: return NSEvent.SpecialKey.home.unicodeScalar + case 5: return NSEvent.SpecialKey.end.unicodeScalar + case 4: return NSEvent.SpecialKey.deleteForward.unicodeScalar // Use "deleteForward" for PC delete. + case 22: return NSEvent.SpecialKey.pageDown.unicodeScalar + default: return .init(0) } }() let newChar = String(newCharScalar) @@ -76,15 +76,15 @@ extension NSEvent { // MARK: - NSEvent Extension - InputSignalProtocol -extension NSEvent { - public var isTypingVertical: Bool { charactersIgnoringModifiers == "Vertical" } - public var text: String { characters ?? "" } - public var inputTextIgnoringModifiers: String? { +public extension NSEvent { + var isTypingVertical: Bool { charactersIgnoringModifiers == "Vertical" } + var text: String { characters ?? "" } + var inputTextIgnoringModifiers: String? { guard charactersIgnoringModifiers != nil else { return nil } return charactersIgnoringModifiers ?? characters ?? "" } - public var charCode: UInt16 { + var charCode: UInt16 { guard type != .flagsChanged else { return 0 } guard characters != nil else { return 0 } // 這裡不用「count > 0」,因為該整數變數只要「!isEmpty」那就必定滿足這個條件。 @@ -94,9 +94,9 @@ extension NSEvent { return result <= UInt16.max ? UInt16(result) : UInt16.max } - public var isFlagChanged: Bool { type == .flagsChanged } + var isFlagChanged: Bool { type == .flagsChanged } - public var isEmacsKey: Bool { + var isEmacsKey: Bool { // 這裡不能只用 isControlHold,因為這裡對修飾鍵的要求有排他性。 [6, 2, 1, 5, 4, 22].contains(charCode) && modifierFlags == .control } @@ -104,86 +104,86 @@ extension NSEvent { // 摁 Alt+Shift+主鍵盤區域數字鍵 的話,根據不同的 macOS 鍵盤佈局種類,會出現不同的符號結果。 // 然而呢,KeyCode 卻是一致的。於是這裡直接準備一個換算表來用。 // 這句用來返回換算結果。 - public var mainAreaNumKeyChar: String? { mapMainAreaNumKey[keyCode] } + var mainAreaNumKeyChar: String? { mapMainAreaNumKey[keyCode] } // 除了 ANSI charCode 以外,其餘一律過濾掉,免得 InputHandler 被餵屎。 - public var isInvalid: Bool { - (0x20...0xFF).contains(charCode) ? false : !(isReservedKey && !isKeyCodeBlacklisted) + var isInvalid: Bool { + (0x20 ... 0xFF).contains(charCode) ? false : !(isReservedKey && !isKeyCodeBlacklisted) } - public var isKeyCodeBlacklisted: Bool { + var isKeyCodeBlacklisted: Bool { guard let code = KeyCodeBlackListed(rawValue: keyCode) else { return false } return code.rawValue != KeyCode.kNone.rawValue } - public var isReservedKey: Bool { + var isReservedKey: Bool { guard let code = KeyCode(rawValue: keyCode) else { return false } return code.rawValue != KeyCode.kNone.rawValue } /// 單獨用 flags 來判定數字小鍵盤輸入的方法已經失效了,所以必須再增補用 KeyCode 判定的方法。 - public var isJISAlphanumericalKey: Bool { KeyCode(rawValue: keyCode) == KeyCode.kJISAlphanumericalKey } - public var isJISKanaSwappingKey: Bool { KeyCode(rawValue: keyCode) == KeyCode.kJISKanaSwappingKey } - public var isNumericPadKey: Bool { arrNumpadKeyCodes.contains(keyCode) } - public var isMainAreaNumKey: Bool { arrMainAreaNumKey.contains(keyCode) } - public var isShiftHold: Bool { modifierFlags.contains([.shift]) } - public var isCommandHold: Bool { modifierFlags.contains([.command]) } - public var isControlHold: Bool { modifierFlags.contains([.control]) } - public var isControlHotKey: Bool { modifierFlags.contains([.control]) && text.first?.isLetter ?? false } - public var isOptionHold: Bool { modifierFlags.contains([.option]) } - public var isOptionHotKey: Bool { modifierFlags.contains([.option]) && text.first?.isLetter ?? false } - public var isCapsLockOn: Bool { modifierFlags.contains([.capsLock]) } - public var isFunctionKeyHold: Bool { modifierFlags.contains([.function]) } - public var isNonLaptopFunctionKey: Bool { modifierFlags.contains([.numericPad]) && !isNumericPadKey } - public var isEnter: Bool { [KeyCode.kCarriageReturn, KeyCode.kLineFeed].contains(KeyCode(rawValue: keyCode)) } - public var isTab: Bool { KeyCode(rawValue: keyCode) == KeyCode.kTab } - public var isUp: Bool { KeyCode(rawValue: keyCode) == KeyCode.kUpArrow } - public var isDown: Bool { KeyCode(rawValue: keyCode) == KeyCode.kDownArrow } - public var isLeft: Bool { KeyCode(rawValue: keyCode) == KeyCode.kLeftArrow } - public var isRight: Bool { KeyCode(rawValue: keyCode) == KeyCode.kRightArrow } - public var isPageUp: Bool { KeyCode(rawValue: keyCode) == KeyCode.kPageUp } - public var isPageDown: Bool { KeyCode(rawValue: keyCode) == KeyCode.kPageDown } - public var isSpace: Bool { KeyCode(rawValue: keyCode) == KeyCode.kSpace } - public var isBackSpace: Bool { KeyCode(rawValue: keyCode) == KeyCode.kBackSpace } - public var isEsc: Bool { KeyCode(rawValue: keyCode) == KeyCode.kEscape } - public var isHome: Bool { KeyCode(rawValue: keyCode) == KeyCode.kHome } - public var isEnd: Bool { KeyCode(rawValue: keyCode) == KeyCode.kEnd } - public var isDelete: Bool { KeyCode(rawValue: keyCode) == KeyCode.kWindowsDelete } + var isJISAlphanumericalKey: Bool { KeyCode(rawValue: keyCode) == KeyCode.kJISAlphanumericalKey } + var isJISKanaSwappingKey: Bool { KeyCode(rawValue: keyCode) == KeyCode.kJISKanaSwappingKey } + var isNumericPadKey: Bool { arrNumpadKeyCodes.contains(keyCode) } + var isMainAreaNumKey: Bool { arrMainAreaNumKey.contains(keyCode) } + var isShiftHold: Bool { modifierFlags.contains([.shift]) } + var isCommandHold: Bool { modifierFlags.contains([.command]) } + var isControlHold: Bool { modifierFlags.contains([.control]) } + var isControlHotKey: Bool { modifierFlags.contains([.control]) && text.first?.isLetter ?? false } + var isOptionHold: Bool { modifierFlags.contains([.option]) } + var isOptionHotKey: Bool { modifierFlags.contains([.option]) && text.first?.isLetter ?? false } + var isCapsLockOn: Bool { modifierFlags.contains([.capsLock]) } + var isFunctionKeyHold: Bool { modifierFlags.contains([.function]) } + var isNonLaptopFunctionKey: Bool { modifierFlags.contains([.numericPad]) && !isNumericPadKey } + var isEnter: Bool { [KeyCode.kCarriageReturn, KeyCode.kLineFeed].contains(KeyCode(rawValue: keyCode)) } + var isTab: Bool { KeyCode(rawValue: keyCode) == KeyCode.kTab } + var isUp: Bool { KeyCode(rawValue: keyCode) == KeyCode.kUpArrow } + var isDown: Bool { KeyCode(rawValue: keyCode) == KeyCode.kDownArrow } + var isLeft: Bool { KeyCode(rawValue: keyCode) == KeyCode.kLeftArrow } + var isRight: Bool { KeyCode(rawValue: keyCode) == KeyCode.kRightArrow } + var isPageUp: Bool { KeyCode(rawValue: keyCode) == KeyCode.kPageUp } + var isPageDown: Bool { KeyCode(rawValue: keyCode) == KeyCode.kPageDown } + var isSpace: Bool { KeyCode(rawValue: keyCode) == KeyCode.kSpace } + var isBackSpace: Bool { KeyCode(rawValue: keyCode) == KeyCode.kBackSpace } + var isEsc: Bool { KeyCode(rawValue: keyCode) == KeyCode.kEscape } + var isHome: Bool { KeyCode(rawValue: keyCode) == KeyCode.kHome } + var isEnd: Bool { KeyCode(rawValue: keyCode) == KeyCode.kEnd } + var isDelete: Bool { KeyCode(rawValue: keyCode) == KeyCode.kWindowsDelete } - public var isCursorBackward: Bool { + var isCursorBackward: Bool { isTypingVertical ? KeyCode(rawValue: keyCode) == .kUpArrow : KeyCode(rawValue: keyCode) == .kLeftArrow } - public var isCursorForward: Bool { + var isCursorForward: Bool { isTypingVertical ? KeyCode(rawValue: keyCode) == .kDownArrow : KeyCode(rawValue: keyCode) == .kRightArrow } - public var isCursorClockRight: Bool { + var isCursorClockRight: Bool { isTypingVertical ? KeyCode(rawValue: keyCode) == .kRightArrow : KeyCode(rawValue: keyCode) == .kUpArrow } - public var isCursorClockLeft: Bool { + var isCursorClockLeft: Bool { isTypingVertical ? KeyCode(rawValue: keyCode) == .kLeftArrow : KeyCode(rawValue: keyCode) == .kDownArrow } - public var isASCII: Bool { charCode < 0x80 } + var isASCII: Bool { charCode < 0x80 } // 這裡必須加上「flags == .shift」,否則會出現某些情況下輸入法「誤判當前鍵入的非 Shift 字符為大寫」的問題 - public var isUpperCaseASCIILetterKey: Bool { - (65...90).contains(charCode) && modifierFlags == .shift + var isUpperCaseASCIILetterKey: Bool { + (65 ... 90).contains(charCode) && modifierFlags == .shift } // 這裡必須用 KeyCode,這樣才不會受隨 macOS 版本更動的 Apple 動態注音鍵盤排列內容的影響。 // 只是必須得與 ![input isShiftHold] 搭配使用才可以(也就是僅判定 Shift 沒被摁下的情形)。 - public var isSymbolMenuPhysicalKey: Bool { + var isSymbolMenuPhysicalKey: Bool { [KeyCode.kSymbolMenuPhysicalKeyIntl, KeyCode.kSymbolMenuPhysicalKeyJIS].contains(KeyCode(rawValue: keyCode)) } } @@ -195,11 +195,11 @@ extension NSEvent { // Also: HIToolbox.framework/Versions/A/Headers/Events.h public enum KeyCode: UInt16 { case kNone = 0 - case kCarriageReturn = 36 // Renamed from "kReturn" to avoid nomenclatural confusions. + case kCarriageReturn = 36 // Renamed from "kReturn" to avoid nomenclatural confusions. case kTab = 48 case kSpace = 49 - case kSymbolMenuPhysicalKeyIntl = 50 // vChewing Specific (Non-JIS) - case kBackSpace = 51 // Renamed from "kDelete" to avoid nomenclatural confusions. + case kSymbolMenuPhysicalKeyIntl = 50 // vChewing Specific (Non-JIS) + case kBackSpace = 51 // Renamed from "kDelete" to avoid nomenclatural confusions. case kEscape = 53 case kCommand = 55 case kShift = 56 @@ -214,12 +214,12 @@ public enum KeyCode: UInt16 { case kVolumeUp = 72 case kVolumeDown = 73 case kMute = 74 - case kLineFeed = 76 // Another keyCode to identify the Enter Key, typable by Fn+Enter. + case kLineFeed = 76 // Another keyCode to identify the Enter Key, typable by Fn+Enter. case kF18 = 79 case kF19 = 80 case kF20 = 90 case kYen = 93 - case kSymbolMenuPhysicalKeyJIS = 94 // vChewing Specific (JIS) + case kSymbolMenuPhysicalKeyJIS = 94 // vChewing Specific (JIS) case kJISNumPadComma = 95 case kF5 = 96 case kF6 = 97 @@ -230,17 +230,17 @@ public enum KeyCode: UInt16 { case kJISAlphanumericalKey = 102 case kF11 = 103 case kJISKanaSwappingKey = 104 - case kF13 = 105 // PrtSc + case kF13 = 105 // PrtSc case kF16 = 106 case kF14 = 107 case kF10 = 109 case kContextMenu = 110 case kF12 = 111 case kF15 = 113 - case kHelp = 114 // Insert + case kHelp = 114 // Insert case kHome = 115 case kPageUp = 116 - case kWindowsDelete = 117 // Renamed from "kForwardDelete" to avoid nomenclatural confusions. + case kWindowsDelete = 117 // Renamed from "kForwardDelete" to avoid nomenclatural confusions. case kF4 = 118 case kEnd = 119 case kF2 = 120 @@ -267,13 +267,13 @@ enum KeyCodeBlackListed: UInt16 { case kF8 = 100 case kF9 = 101 case kF11 = 103 - case kF13 = 105 // PrtSc + case kF13 = 105 // PrtSc case kF16 = 106 case kF14 = 107 case kF10 = 109 case kF12 = 111 case kF15 = 113 - case kHelp = 114 // Insert + case kHelp = 114 // Insert case kF4 = 118 case kF2 = 120 case kF1 = 122 @@ -323,8 +323,8 @@ let arrAppleABCKeyboardMap: [UInt16: (String, String)] = [ 45: ("n", "N"), 46: ("m", "M"), 43: (",", "<"), 47: (".", ">"), 44: ("/", "?"), ] -extension NSEvent { - public var inAppleABCStaticForm: NSEvent { +public extension NSEvent { + var inAppleABCStaticForm: NSEvent { if type == .flagsChanged { return self } guard modifierFlags == .shift || modifierFlags == [] else { return self } if !arrAppleABCKeyboardMap.keys.contains(keyCode) { return self } diff --git a/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_NSSound.swift b/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_NSSound.swift index 56f9d7bc..3671a679 100644 --- a/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_NSSound.swift +++ b/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_NSSound.swift @@ -9,8 +9,8 @@ import AVFoundation import Cocoa -extension NSSound { - public static func buzz(fart: Bool = false) { +public extension NSSound { + static func buzz(fart: Bool = false) { let filePath = Bundle.main.path(forResource: fart ? "Fart" : "Beep", ofType: "m4a")! let fileURL = URL(fileURLWithPath: filePath) var soundID: SystemSoundID = 0 @@ -18,12 +18,12 @@ extension NSSound { AudioServicesPlaySystemSound(soundID) } - public static func buzz(fart: Bool = false, count: Int) { + static func buzz(fart: Bool = false, count: Int) { if count <= 1 { NSSound.buzz(fart: fart) return } - for _ in 0...count { + for _ in 0 ... count { NSSound.buzz(fart: fart) usleep(500_000) } diff --git a/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_NSWindowController.swift b/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_NSWindowController.swift index 0b73e077..52324ec2 100644 --- a/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_NSWindowController.swift +++ b/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_NSWindowController.swift @@ -9,8 +9,8 @@ import Cocoa import InputMethodKit -extension NSWindowController { - public func orderFront() { +public extension NSWindowController { + func orderFront() { window?.orderFront(self) } @@ -21,7 +21,7 @@ extension NSWindowController { /// - Parameters: /// - windowTopLeftPoint: 給定的視窗顯示位置。 /// - heightDelta: 為了「防止選字窗抻出螢幕下方」而給定的預留高度。 - public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight heightDelta: Double, useGCD: Bool) { + func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight heightDelta: Double, useGCD: Bool) { func doSet() { guard let window = window, var screenFrame = NSScreen.main?.visibleFrame else { return } let windowSize = window.frame.size @@ -48,8 +48,8 @@ extension NSWindowController { } } -extension NSWindow { - @discardableResult public func callAlert(title: String, text: String? = nil) -> NSApplication.ModalResponse { +public extension NSWindow { + @discardableResult func callAlert(title: String, text: String? = nil) -> NSApplication.ModalResponse { let alert = NSAlert() alert.messageText = title if let text = text { alert.informativeText = text } @@ -62,7 +62,7 @@ extension NSWindow { } } -extension IMKCandidates { +public extension IMKCandidates { /// 設定選字窗的顯示位置。 /// /// 需注意:該函式會藉由設定選字窗左上角頂點的方式、使選字窗始終位於某個螢幕之內。 @@ -70,7 +70,7 @@ extension IMKCandidates { /// - Parameters: /// - windowTopLeftPoint: 給定的視窗顯示位置。 /// - heightDelta: 為了「防止選字窗抻出螢幕下方」而給定的預留高度。 - public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight heightDelta: Double, useGCD: Bool) { + func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight heightDelta: Double, useGCD: Bool) { func doSet() { DispatchQueue.main.async { [self] in guard var screenFrame = NSScreen.main?.visibleFrame else { return } diff --git a/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_NSWindowPositioner.swift b/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_NSWindowPositioner.swift index 104521e3..b6d72de7 100644 --- a/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_NSWindowPositioner.swift +++ b/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_NSWindowPositioner.swift @@ -7,8 +7,8 @@ import SwiftUI // MARK: Model -extension NSWindow { - public struct Position { +public extension NSWindow { + struct Position { public static let defaultPadding: CGFloat = 16 public var vertical: Vertical @@ -17,28 +17,28 @@ extension NSWindow { } } -extension NSWindow.Position { - public enum Horizontal { +public extension NSWindow.Position { + enum Horizontal { case left, center, right } - public enum Vertical { + enum Vertical { case top, center, bottom } } // MARK: Logic -extension NSWindow.Position { - public func value(forWindow windowRect: CGRect, inScreen screenRect: CGRect) -> CGPoint { +public extension NSWindow.Position { + func value(forWindow windowRect: CGRect, inScreen screenRect: CGRect) -> CGPoint { let xPosition = horizontal.valueFor( - screenRange: screenRect.minX.., width: CGFloat, padding: CGFloat @@ -56,15 +56,15 @@ extension NSWindow.Position.Horizontal { -> CGFloat { switch self { - case .left: return screenRange.lowerBound + padding - case .center: return (screenRange.upperBound + screenRange.lowerBound - width) / 2 - case .right: return screenRange.upperBound - width - padding + case .left: return screenRange.lowerBound + padding + case .center: return (screenRange.upperBound + screenRange.lowerBound - width) / 2 + case .right: return screenRange.upperBound - width - padding } } } -extension NSWindow.Position.Vertical { - public func valueFor( +public extension NSWindow.Position.Vertical { + func valueFor( screenRange: Range, height: CGFloat, padding: CGFloat @@ -72,23 +72,23 @@ extension NSWindow.Position.Vertical { -> CGFloat { switch self { - case .top: return screenRange.upperBound - height - padding - case .center: return (screenRange.upperBound + screenRange.lowerBound - height) / 2 - case .bottom: return screenRange.lowerBound + padding + case .top: return screenRange.upperBound - height - padding + case .center: return (screenRange.upperBound + screenRange.lowerBound - height) / 2 + case .bottom: return screenRange.lowerBound + padding } } } // MARK: - AppKit extension -extension NSWindow { - public func setPosition(_ position: Position, in screen: NSScreen?) { +public extension NSWindow { + func setPosition(_ position: Position, in screen: NSScreen?) { guard let visibleFrame = (screen ?? self.screen)?.visibleFrame else { return } let origin = position.value(forWindow: frame, inScreen: visibleFrame) setFrameOrigin(origin) } - public func setPosition( + func setPosition( vertical: Position.Vertical, horizontal: Position.Horizontal, padding: CGFloat = Position.defaultPadding, @@ -136,8 +136,8 @@ extension NSWindow { } @available(macOS 10.15, *) - extension View { - public func hostingWindowPosition( + public extension View { + func hostingWindowPosition( vertical: NSWindow.Position.Vertical, horizontal: NSWindow.Position.Horizontal, padding: CGFloat = NSWindow.Position.defaultPadding, diff --git a/Packages/vChewing_Hotenka/Package.swift b/Packages/vChewing_Hotenka/Package.swift index 8bfa4d8d..38908028 100644 --- a/Packages/vChewing_Hotenka/Package.swift +++ b/Packages/vChewing_Hotenka/Package.swift @@ -4,13 +4,13 @@ import PackageDescription let package = Package( name: "Hotenka", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "Hotenka", targets: ["Hotenka"] - ) + ), ], dependencies: [], targets: [ diff --git a/Packages/vChewing_Hotenka/Sources/Hotenka/HotenkaChineseConverter.swift b/Packages/vChewing_Hotenka/Sources/Hotenka/HotenkaChineseConverter.swift index 08a4ff10..4913859d 100644 --- a/Packages/vChewing_Hotenka/Sources/Hotenka/HotenkaChineseConverter.swift +++ b/Packages/vChewing_Hotenka/Sources/Hotenka/HotenkaChineseConverter.swift @@ -127,18 +127,18 @@ public class HotenkaChineseConverter { var dictTypeKey: String switch dictType { - case .zhHantTW: - dictTypeKey = "zh2TW" - case .zhHantHK: - dictTypeKey = "zh2HK" - case .zhHansSG: - dictTypeKey = "zh2SG" - case .zhHansJP: - dictTypeKey = "zh2JP" - case .zhHantKX: - dictTypeKey = "zh2KX" - case .zhHansCN: - dictTypeKey = "zh2CN" + case .zhHantTW: + dictTypeKey = "zh2TW" + case .zhHantHK: + dictTypeKey = "zh2HK" + case .zhHansSG: + dictTypeKey = "zh2SG" + case .zhHansJP: + dictTypeKey = "zh2JP" + case .zhHantKX: + dictTypeKey = "zh2KX" + case .zhHansCN: + dictTypeKey = "zh2CN" } var result = "" @@ -153,7 +153,7 @@ public class HotenkaChineseConverter { innerloop: while j > 0 { let start = target.index(target.startIndex, offsetBy: i) let end = target.index(target.startIndex, offsetBy: i + j) - guard let useDictSubStr = useDict[String(target[start.. Range { +private extension String { + func range(of str: String) -> Range { var start = -1 withCString { bytes in str.withCString { sbytes in start = strstr(bytes, sbytes) - UnsafeMutablePointer(mutating: bytes) } } - return start < 0 ? 0..<0 : start.. String { + func substring(to index: Int) -> String { var out = self withCString { bytes in let bytes = UnsafeMutablePointer(mutating: bytes) @@ -198,7 +198,7 @@ extension String { return out } - fileprivate func substring(from index: Int) -> String { + func substring(from index: Int) -> String { var out = self withCString { bytes in out = String(cString: bytes + index) diff --git a/Packages/vChewing_Hotenka/makefile b/Packages/vChewing_Hotenka/makefile index 80f07f65..db02b56a 100644 --- a/Packages/vChewing_Hotenka/makefile +++ b/Packages/vChewing_Hotenka/makefile @@ -1,6 +1,7 @@ -.PHONY: format +.PHONY: lint format format: - swiftformat ./ --swiftversion 5.5 - @git ls-files --exclude-standard | grep -E '\.swift$$' | xargs swift-format format --in-place --configuration ./.clang-format-swift.json --parallel - @git ls-files --exclude-standard | grep -E '\.swift$$' | xargs swift-format lint --configuration ./.clang-format-swift.json --parallel + @swiftformat --swiftversion 5.5 --indent 2 ./ + +lint: + @git ls-files --exclude-standard | grep -E '\.swift$$' | swiftlint --fix --autocorrect \ No newline at end of file diff --git a/Packages/vChewing_IMKUtils/Package.swift b/Packages/vChewing_IMKUtils/Package.swift index 22592e10..1c011df8 100644 --- a/Packages/vChewing_IMKUtils/Package.swift +++ b/Packages/vChewing_IMKUtils/Package.swift @@ -4,19 +4,19 @@ import PackageDescription let package = Package( name: "IMKUtils", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "IMKUtils", targets: ["IMKUtils"] - ) + ), ], dependencies: [], targets: [ .target( name: "IMKUtils", dependencies: [] - ) + ), ] ) diff --git a/Packages/vChewing_IMKUtils/Sources/IMKUtils/IMKHelper.swift b/Packages/vChewing_IMKUtils/Sources/IMKUtils/IMKHelper.swift index c953aa7c..2116fb94 100644 --- a/Packages/vChewing_IMKUtils/Sources/IMKUtils/IMKHelper.swift +++ b/Packages/vChewing_IMKUtils/Sources/IMKUtils/IMKHelper.swift @@ -92,8 +92,8 @@ public enum IMKHelper { // MARK: - 與輸入法的具體的安裝過程有關的命令 -extension IMKHelper { - @discardableResult public static func registerInputMethod() -> Int32 { +public extension IMKHelper { + @discardableResult static func registerInputMethod() -> Int32 { TISInputSource.registerInputMethod() ? 0 : -1 } } diff --git a/Packages/vChewing_IMKUtils/Sources/IMKUtils/TISInputSourceExtension.swift b/Packages/vChewing_IMKUtils/Sources/IMKUtils/TISInputSourceExtension.swift index 77debe18..2b867abb 100644 --- a/Packages/vChewing_IMKUtils/Sources/IMKUtils/TISInputSourceExtension.swift +++ b/Packages/vChewing_IMKUtils/Sources/IMKUtils/TISInputSourceExtension.swift @@ -11,21 +11,21 @@ import InputMethodKit // MARK: - TISInputSource Extension by The vChewing Project (MIT-NTL License). -extension TISInputSource { - public static var allRegisteredInstancesOfThisInputMethod: [TISInputSource] { +public extension TISInputSource { + static var allRegisteredInstancesOfThisInputMethod: [TISInputSource] { TISInputSource.modes.compactMap { TISInputSource.generate(from: $0) } } - public static var modes: [String] { + static var modes: [String] { guard let components = Bundle.main.infoDictionary?["ComponentInputModeDict"] as? [String: Any], - let tsInputModeListKey = components["tsInputModeListKey"] as? [String: Any] + let tsInputModeListKey = components["tsInputModeListKey"] as? [String: Any] else { return [] } return tsInputModeListKey.keys.map { $0 } } - @discardableResult public static func registerInputMethod() -> Bool { + @discardableResult static func registerInputMethod() -> Bool { let instances = TISInputSource.allRegisteredInstancesOfThisInputMethod if instances.isEmpty { // 有實例尚未登記。執行登記手續。 @@ -46,15 +46,15 @@ extension TISInputSource { return succeeded } - @discardableResult public static func registerInputSource() -> Bool { + @discardableResult static func registerInputSource() -> Bool { TISRegisterInputSource(Bundle.main.bundleURL as CFURL) == noErr } - @discardableResult public func activate() -> Bool { + @discardableResult func activate() -> Bool { TISEnableInputSource(self) == noErr } - @discardableResult public func select() -> Bool { + @discardableResult func select() -> Bool { if !isSelectable { NSLog("Non-selectable: \(identifier)") return false @@ -66,35 +66,35 @@ extension TISInputSource { return true } - @discardableResult public func deactivate() -> Bool { + @discardableResult func deactivate() -> Bool { TISDisableInputSource(self) == noErr } - public var isActivated: Bool { + var isActivated: Bool { unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyInputSourceIsEnabled), to: CFBoolean.self) == kCFBooleanTrue } - public var isSelectable: Bool { + var isSelectable: Bool { unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyInputSourceIsSelectCapable), to: CFBoolean.self) == kCFBooleanTrue } - public static func generate(from identifier: String) -> TISInputSource? { + static func generate(from identifier: String) -> TISInputSource? { TISInputSource.rawTISInputSources(onlyASCII: false)[identifier] } - public var inputModeID: String { + var inputModeID: String { unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyInputModeID), to: NSString.self) as String? ?? "" } - public var vChewingLocalizedName: String { + var vChewingLocalizedName: String { switch identifier { - case "com.apple.keylayout.ZhuyinBopomofo": - return NSLocalizedString("Apple Zhuyin Bopomofo (Dachen)", comment: "") - case "com.apple.keylayout.ZhuyinEten": - return NSLocalizedString("Apple Zhuyin Eten (Traditional)", comment: "") - default: return localizedName + case "com.apple.keylayout.ZhuyinBopomofo": + return NSLocalizedString("Apple Zhuyin Bopomofo (Dachen)", comment: "") + case "com.apple.keylayout.ZhuyinEten": + return NSLocalizedString("Apple Zhuyin Eten (Traditional)", comment: "") + default: return localizedName } } } @@ -104,23 +104,23 @@ extension TISInputSource { // Ref: Original source codes are written in Swift 4 from Mzp's InputMethodKit textbook. // Note: Slightly modified by vChewing Project: Using Dictionaries when necessary. -extension TISInputSource { - public var localizedName: String { +public extension TISInputSource { + var localizedName: String { unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyLocalizedName), to: NSString.self) as String? ?? "" } - public var identifier: String { + var identifier: String { unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyInputSourceID), to: NSString.self) as String? ?? "" } - public var scriptCode: Int { + var scriptCode: Int { // Shiki's note: There is no "kTISPropertyScriptCode" in TextInputSources.h file. // Using Mzp's latest solution in his blog: https://mzp.hatenablog.com/entry/2018/07/16/212026 let r = TISGetInputSourceProperty(self, "TSMInputSourcePropertyScriptCode" as CFString) return unsafeBitCast(r, to: NSString.self).integerValue as Int? ?? 0 } - public static func rawTISInputSources(onlyASCII: Bool = false) -> [String: TISInputSource] { + static func rawTISInputSources(onlyASCII: Bool = false) -> [String: TISInputSource] { // 為了指定檢索條件,先構築 CFDictionary 辭典。 // 第二項代指辭典容量。 let conditions = CFDictionaryCreateMutable(nil, 2, nil, nil) diff --git a/Packages/vChewing_LangModelAssembly/Package.swift b/Packages/vChewing_LangModelAssembly/Package.swift index b6d9dd1a..90e4cc06 100644 --- a/Packages/vChewing_LangModelAssembly/Package.swift +++ b/Packages/vChewing_LangModelAssembly/Package.swift @@ -4,13 +4,13 @@ import PackageDescription let package = Package( name: "LangModelAssembly", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "LangModelAssembly", targets: ["LangModelAssembly"] - ) + ), ], dependencies: [ .package(path: "../RMJay_LineReader"), diff --git a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/LMConsolidator.swift b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/LMConsolidator.swift index 85569d3c..509a3515 100644 --- a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/LMConsolidator.swift +++ b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/LMConsolidator.swift @@ -10,8 +10,8 @@ import Foundation import LineReader import Shared -extension vChewingLM { - public enum LMConsolidator { +public extension vChewingLM { + enum LMConsolidator { public static let kPragmaHeader = "# 𝙵𝙾𝚁𝙼𝙰𝚃 𝚘𝚛𝚐.𝚊𝚝𝚎𝚕𝚒𝚎𝚛𝙸𝚗𝚖𝚞.𝚟𝚌𝚑𝚎𝚠𝚒𝚗𝚐.𝚞𝚜𝚎𝚛𝙻𝚊𝚗𝚐𝚞𝚊𝚐𝚎𝙼𝚘𝚍𝚎𝚕𝙳𝚊𝚝𝚊.𝚏𝚘𝚛𝚖𝚊𝚝𝚝𝚎𝚍" /// 檢查給定檔案的標頭是否正常。 @@ -24,7 +24,7 @@ extension vChewingLM { throw FileErrors.fileHandleError("") } let lineReader = try LineReader(file: fileHandle) - for strLine in lineReader { // 不需要 i=0,因為第一遍迴圈就出結果。 + for strLine in lineReader { // 不需要 i=0,因為第一遍迴圈就出結果。 if strLine != kPragmaHeader { vCLog("Header Mismatch, Starting In-Place Consolidation.") return false @@ -56,7 +56,7 @@ extension vChewingLM { if !strIncoming.hasSuffix("\n") { vCLog("EOF Fix Necessity Confirmed, Start Fixing.") if let writeFile = FileHandle(forUpdatingAtPath: path), - let endl = "\n".data(using: .utf8) + let endl = "\n".data(using: .utf8) { writeFile.seekToEndOfFile() writeFile.write(endl) @@ -85,7 +85,7 @@ extension vChewingLM { var pragmaResult: Bool { let realPragmaHeader = kPragmaHeader + "\n" if strProcessed.count <= kPragmaHeader.count { return false } - let range = 0..<(realPragmaHeader.count) + let range = 0 ..< (realPragmaHeader.count) let fetchedPragma = ContiguousArray(strProcessed.utf8CString[range]) return fetchedPragma == realPragmaHeader.utf8CString } @@ -104,14 +104,14 @@ extension vChewingLM { strProcessed.regReplace(pattern: #"(\n | \n)"#, replaceWith: "\n") // CR & FF to LF, 且去除重複行 strProcessed.regReplace(pattern: #"(\f+|\r+|\n+)+"#, replaceWith: "\n") - if strProcessed.prefix(1) == " " { // 去除檔案開頭空格 + if strProcessed.prefix(1) == " " { // 去除檔案開頭空格 strProcessed.removeFirst() } - if strProcessed.suffix(1) == " " { // 去除檔案結尾空格 + if strProcessed.suffix(1) == " " { // 去除檔案結尾空格 strProcessed.removeLast() } - strProcessed = kPragmaHeader + "\n" + strProcessed // Add Pragma Header + strProcessed = kPragmaHeader + "\n" + strProcessed // Add Pragma Header // Step 3: Deduplication. let arrData = strProcessed.split(separator: "\n") diff --git a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/LMInstantiator.swift b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/LMInstantiator.swift index a50921d5..0d590867 100644 --- a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/LMInstantiator.swift +++ b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/LMInstantiator.swift @@ -10,7 +10,7 @@ import Foundation import Megrez import Shared -extension vChewingLM { +public extension vChewingLM { /// 語言模組副本化模組(LMInstantiator,下稱「LMI」)自身為符合天權星組字引擎內 /// 的 LangModelProtocol 協定的模組、統籌且整理來自其它子模組的資料(包括使 /// 用者語彙、繪文字模組、語彙濾除表、原廠語言模組等)。 @@ -28,7 +28,7 @@ extension vChewingLM { /// /// LMI 會根據需要分別載入原廠語言模組和其他個別的子語言模組。LMI 本身不會記錄這些 /// 語言模組的相關資料的存放位置,僅藉由參數來讀取相關訊息。 - public class LMInstantiator: LangModelProtocol { + class LMInstantiator: LangModelProtocol { // 在函式內部用以記錄狀態的開關。 public var isCassetteEnabled = false public var isPhraseReplacementEnabled = false @@ -242,8 +242,8 @@ extension vChewingLM { let keyChain = keyArray.joined(separator: "-") _ = isFiltering - ? lmFiltered.temporaryMap[keyChain, default: []].append(unigram) - : lmUserPhrases.temporaryMap[keyChain, default: []].append(unigram) + ? lmFiltered.temporaryMap[keyChain, default: []].append(unigram) + : lmUserPhrases.temporaryMap[keyChain, default: []].append(unigram) } /// 自當前記憶體取得指定使用者子語言模組內的原始資料體。 @@ -251,11 +251,11 @@ extension vChewingLM { /// - targetType: 操作對象。 public func retrieveData(from targetType: ReplacableUserDataType) -> String { switch targetType { - case .thePhrases: return lmUserPhrases.strData - case .theFilter: return lmFiltered.strData - case .theReplacements: return lmReplacements.strData - case .theAssociates: return lmAssociates.strData - case .theSymbols: return lmUserSymbols.strData + case .thePhrases: return lmUserPhrases.strData + case .theFilter: return lmFiltered.strData + case .theReplacements: return lmReplacements.strData + case .theAssociates: return lmAssociates.strData + case .theSymbols: return lmUserSymbols.strData } } @@ -267,21 +267,21 @@ extension vChewingLM { var rawText = rawStrData LMConsolidator.consolidate(text: &rawText, pragma: true) switch targetType { - case .theAssociates: - lmAssociates.replaceData(textData: rawText) - if save { lmAssociates.saveData() } - case .theFilter: - lmFiltered.replaceData(textData: rawText) - if save { lmAssociates.saveData() } - case .theReplacements: - lmReplacements.replaceData(textData: rawText) - if save { lmAssociates.saveData() } - case .thePhrases: - lmUserPhrases.replaceData(textData: rawText) - if save { lmAssociates.saveData() } - case .theSymbols: - lmUserSymbols.replaceData(textData: rawText) - if save { lmAssociates.saveData() } + case .theAssociates: + lmAssociates.replaceData(textData: rawText) + if save { lmAssociates.saveData() } + case .theFilter: + lmFiltered.replaceData(textData: rawText) + if save { lmAssociates.saveData() } + case .theReplacements: + lmReplacements.replaceData(textData: rawText) + if save { lmAssociates.saveData() } + case .thePhrases: + lmUserPhrases.replaceData(textData: rawText) + if save { lmAssociates.saveData() } + case .theSymbols: + lmUserSymbols.replaceData(textData: rawText) + if save { lmAssociates.saveData() } } } @@ -348,7 +348,7 @@ extension vChewingLM { // 提前處理語彙置換 if isPhraseReplacementEnabled { - for i in 0.. String { + func convertCassetteKeyToDisplay(char: String) -> String { Self.lmCassette.convertKeyToDisplay(char: char) } /// 檢查當前的按鍵是否屬於目前的磁帶規定的允許的字根按鍵。 /// - Parameter key: 按鍵字元。 /// - Returns: 檢查結果。 - public func isThisCassetteKeyAllowed(key: String) -> Bool { + func isThisCassetteKeyAllowed(key: String) -> Bool { Self.lmCassette.allowedKeys.contains(key) } /// 檢查給定的索引鍵在搭上花牌鍵之後是否有匹配結果。 /// - Parameter key: 給定的索引鍵。 /// - Returns: 是否有批配結果。 - public func hasCassetteWildcardResultsFor(key: String) -> Bool { + func hasCassetteWildcardResultsFor(key: String) -> Bool { Self.lmCassette.hasUnigramsFor(key: key + Self.lmCassette.wildcard) } /// 提供磁帶反查結果。 /// - Parameter value: 要拿來反查的字詞。 /// - Returns: 反查結果字串陣列。 - public func cassetteReverseLookup(for value: String) -> [String] { + func cassetteReverseLookup(for value: String) -> [String] { var lookupResult = Self.lmCassette.reverseLookupMap[value] ?? [] guard !lookupResult.isEmpty else { return [] } lookupResult = lookupResult.map { $0.trimmingCharacters(in: .newlines) } diff --git a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/LMInstantiator_DateTimeExtension.swift b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/LMInstantiator_DateTimeExtension.swift index 87c62e9d..362604ea 100644 --- a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/LMInstantiator_DateTimeExtension.swift +++ b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/LMInstantiator_DateTimeExtension.swift @@ -22,56 +22,56 @@ extension vChewingLM.LMInstantiator { delta.year = max(min(deltaOfCalendarYears, 0), thisYear * -1) let currentDateShortened = Calendar.current.date(byAdding: delta, to: currentDate) switch key { - case "ㄖˋ-ㄑㄧ", "ㄖˋ-ㄑㄧˊ": - let formatterDate1 = DateFormatter() - let formatterDate2 = DateFormatter() - formatterDate1.dateFormat = "yyyy-MM-dd" - formatterDate2.dateFormat = "yyyy年MM月dd日" - let date1 = formatterDate1.string(from: currentDate) - let date2 = formatterDate2.string(from: currentDate) - var date3 = date2.convertArabicNumeralsToChinese - date3 = date3.replacingOccurrences(of: "年〇", with: "年") - date3 = date3.replacingOccurrences(of: "月〇", with: "月") - results.append(.init(value: date1, score: -94)) - results.append(.init(value: date2, score: -95)) - results.append(.init(value: date3, score: -96)) - if let currentDateShortened = currentDateShortened, delta.year != 0 { - var dateAlt1: String = formatterDate1.string(from: currentDateShortened) - dateAlt1.regReplace(pattern: #"^0+"#) - var dateAlt2: String = formatterDate2.string(from: currentDateShortened) - dateAlt2.regReplace(pattern: #"^0+"#) - var dateAlt3 = dateAlt2.convertArabicNumeralsToChinese - dateAlt3 = dateAlt3.replacingOccurrences(of: "年〇", with: "年") - dateAlt3 = dateAlt3.replacingOccurrences(of: "月〇", with: "月") - results.append(.init(value: dateAlt1, score: -97)) - results.append(.init(value: dateAlt2, score: -98)) - results.append(.init(value: dateAlt3, score: -99)) - } - case "ㄕˊ-ㄐㄧㄢ": - let formatterTime1 = DateFormatter() - let formatterTime2 = DateFormatter() - let formatterTime3 = DateFormatter() - formatterTime1.dateFormat = "HH:mm" - formatterTime2.dateFormat = isCHS ? "HH点mm分" : "HH點mm分" - formatterTime3.dateFormat = isCHS ? "HH时mm分" : "HH時mm分" - let time1 = formatterTime1.string(from: currentDate) - let time2 = formatterTime2.string(from: currentDate) - let time3 = formatterTime3.string(from: currentDate) - results.append(.init(value: time1, score: -97)) - results.append(.init(value: time2, score: -98)) - results.append(.init(value: time3, score: -99)) - case "ㄒㄧㄥ-ㄑㄧ", "ㄒㄧㄥ-ㄑㄧˊ": - let formatterWeek1 = DateFormatter() - let formatterWeek2 = DateFormatter() - formatterWeek1.dateFormat = "EEEE" - formatterWeek2.dateFormat = "EE" - formatterWeek1.locale = theLocale - formatterWeek2.locale = theLocale - let week1 = formatterWeek1.string(from: currentDate) - let week2 = formatterWeek2.string(from: currentDate) - results.append(.init(value: week1, score: -98)) - results.append(.init(value: week2, score: -99)) - default: return .init() + case "ㄖˋ-ㄑㄧ", "ㄖˋ-ㄑㄧˊ": + let formatterDate1 = DateFormatter() + let formatterDate2 = DateFormatter() + formatterDate1.dateFormat = "yyyy-MM-dd" + formatterDate2.dateFormat = "yyyy年MM月dd日" + let date1 = formatterDate1.string(from: currentDate) + let date2 = formatterDate2.string(from: currentDate) + var date3 = date2.convertArabicNumeralsToChinese + date3 = date3.replacingOccurrences(of: "年〇", with: "年") + date3 = date3.replacingOccurrences(of: "月〇", with: "月") + results.append(.init(value: date1, score: -94)) + results.append(.init(value: date2, score: -95)) + results.append(.init(value: date3, score: -96)) + if let currentDateShortened = currentDateShortened, delta.year != 0 { + var dateAlt1: String = formatterDate1.string(from: currentDateShortened) + dateAlt1.regReplace(pattern: #"^0+"#) + var dateAlt2: String = formatterDate2.string(from: currentDateShortened) + dateAlt2.regReplace(pattern: #"^0+"#) + var dateAlt3 = dateAlt2.convertArabicNumeralsToChinese + dateAlt3 = dateAlt3.replacingOccurrences(of: "年〇", with: "年") + dateAlt3 = dateAlt3.replacingOccurrences(of: "月〇", with: "月") + results.append(.init(value: dateAlt1, score: -97)) + results.append(.init(value: dateAlt2, score: -98)) + results.append(.init(value: dateAlt3, score: -99)) + } + case "ㄕˊ-ㄐㄧㄢ": + let formatterTime1 = DateFormatter() + let formatterTime2 = DateFormatter() + let formatterTime3 = DateFormatter() + formatterTime1.dateFormat = "HH:mm" + formatterTime2.dateFormat = isCHS ? "HH点mm分" : "HH點mm分" + formatterTime3.dateFormat = isCHS ? "HH时mm分" : "HH時mm分" + let time1 = formatterTime1.string(from: currentDate) + let time2 = formatterTime2.string(from: currentDate) + let time3 = formatterTime3.string(from: currentDate) + results.append(.init(value: time1, score: -97)) + results.append(.init(value: time2, score: -98)) + results.append(.init(value: time3, score: -99)) + case "ㄒㄧㄥ-ㄑㄧ", "ㄒㄧㄥ-ㄑㄧˊ": + let formatterWeek1 = DateFormatter() + let formatterWeek2 = DateFormatter() + formatterWeek1.dateFormat = "EEEE" + formatterWeek2.dateFormat = "EE" + formatterWeek1.locale = theLocale + formatterWeek2.locale = theLocale + let week1 = formatterWeek1.string(from: currentDate) + let week2 = formatterWeek2.string(from: currentDate) + results.append(.init(value: week1, score: -98)) + results.append(.init(value: week2, score: -99)) + default: return .init() } return results } @@ -83,10 +83,10 @@ private let tableMappingArabicNumeralsToChinese: [String: String] = [ "0": "〇", "1": "一", "2": "二", "3": "三", "4": "四", "5": "五", "6": "六", "7": "七", "8": "八", "9": "九", ] -extension String { +private extension String { /// 將給定的字串當中的阿拉伯數字轉為漢語小寫,逐字轉換。 /// - Parameter target: 要進行轉換操作的對象,會直接修改該對象。 - fileprivate var convertArabicNumeralsToChinese: String { + var convertArabicNumeralsToChinese: String { var target = self for key in tableMappingArabicNumeralsToChinese.keys { guard let result = tableMappingArabicNumeralsToChinese[key] else { continue } diff --git a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/RangeParserAPI.swift b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/RangeParserAPI.swift index ca5c7d4b..63254671 100644 --- a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/RangeParserAPI.swift +++ b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/RangeParserAPI.swift @@ -24,7 +24,7 @@ extension String { ) { var startIndex = startIndex split(separator: separator).forEach { substring in - let theRange = range(of: substring, range: startIndex.. [Range] { +private extension String { + func ranges(splitBy separator: Element) -> [Range] { var startIndex = startIndex return split(separator: separator).reduce(into: []) { ranges, substring in - _ = range(of: substring, range: startIndex.., Int)]] = [:] var strData: String = "" diff --git a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmCassette.swift b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmCassette.swift index 3945ed05..879dac68 100644 --- a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmCassette.swift +++ b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmCassette.swift @@ -12,9 +12,9 @@ import LineReader import Megrez import Shared -extension vChewingLM { +public extension vChewingLM { /// 磁帶模組,用來方便使用者自行擴充字根輸入法。 - @frozen public struct LMCassette { + @frozen struct LMCassette { public private(set) var filePath: String? public private(set) var nameShort: String = "" public private(set) var nameENG: String = "" @@ -115,9 +115,9 @@ extension vChewingLM { } else if loadingOctagramData, !strLine.contains("%octagram") { guard let countValue = Int(cells[1]) else { continue } switch cells.count { - case 2: octagramMap[strFirstCell] = countValue - case 3: octagramDividedMap[strFirstCell] = (countValue, cells[2].trimmingCharacters(in: .newlines)) - default: break + case 2: octagramMap[strFirstCell] = countValue + case 3: octagramDividedMap[strFirstCell] = (countValue, cells[2].trimmingCharacters(in: .newlines)) + default: break } norm += Self.fscale ** (Double(cells[0].count) / 3.0 - 1.0) * Double(countValue) } @@ -187,7 +187,7 @@ extension vChewingLM { let arrRaw = charDefMap[key]?.deduplicated ?? [] var arrRawWildcard: [String] = [] if let arrRawWildcardValues = charDefWildcardMap[key]?.deduplicated, - key.contains(wildcard), key.first?.description != wildcard + key.contains(wildcard), key.first?.description != wildcard { arrRawWildcard.append(contentsOf: arrRawWildcardValues) } @@ -236,18 +236,18 @@ extension vChewingLM { private func calculateWeight(count theCount: Int, phraseLength: Int) -> Double { var weight: Double = 0 switch theCount { - case -2: // 拗音假名 - weight = -13 - case -1: // 單個假名 - weight = -13 - case 0: // 墊底低頻漢字與詞語 - weight = log10( - Self.fscale ** (Double(phraseLength) / 3.0 - 1.0) * 0.25 / norm) - default: - weight = log10( - Self.fscale ** (Double(phraseLength) / 3.0 - 1.0) - * Double(theCount) / norm - ) + case -2: // 拗音假名 + weight = -13 + case -1: // 單個假名 + weight = -13 + case 0: // 墊底低頻漢字與詞語 + weight = log10( + Self.fscale ** (Double(phraseLength) / 3.0 - 1.0) * 0.25 / norm) + default: + weight = log10( + Self.fscale ** (Double(phraseLength) / 3.0 - 1.0) + * Double(theCount) / norm + ) } return weight } diff --git a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmCoreEX.swift b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmCoreEX.swift index 9166ba95..b92d2212 100644 --- a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmCoreEX.swift +++ b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmCoreEX.swift @@ -10,12 +10,12 @@ import Megrez import PinyinPhonaConverter import Shared -extension vChewingLM { +public extension vChewingLM { /// 與之前的 LMCore 不同,LMCoreEX 不在辭典內記錄實體,而是記錄 range 範圍。 /// 需要資料的時候,直接拿 range 去 strData 取資料。 /// 資料記錄原理與上游 C++ 的 ParselessLM 差不多,但用的是 Swift 原生手段。 /// 主要時間消耗仍在 For 迴圈,但這個算法可以顯著減少記憶體佔用。 - @frozen public struct LMCoreEX { + @frozen struct LMCoreEX { public private(set) var filePath: String? /// 資料庫辭典。索引內容為注音字串,資料內容則為字串首尾範圍、方便自 strData 取資料。 var rangeMap: [String: [Range]] = [:] @@ -97,7 +97,7 @@ extension vChewingLM { if strData == rawStrData { return } strData = rawStrData var newMap: [String: [Range]] = [:] - let shouldReverse = shouldReverse // 必需,否則下文的 closure 會出錯。 + let shouldReverse = shouldReverse // 必需,否則下文的 closure 會出錯。 strData.parse(splitee: "\n") { theRange in let theCells = rawStrData[theRange].split(separator: " ") if theCells.count >= 2, theCells[0].description.first != "#" { @@ -167,7 +167,7 @@ extension vChewingLM { theScore = .init(String(neta[2])) ?? defaultScore } if theScore > 0 { - theScore *= -1 // 應對可能忘記寫負號的情形 + theScore *= -1 // 應對可能忘記寫負號的情形 } grams.append(Megrez.Unigram(value: theValue, score: theScore)) } diff --git a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmCoreNS.swift b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmCoreNS.swift index 1509c977..45d015b0 100644 --- a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmCoreNS.swift +++ b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmCoreNS.swift @@ -10,11 +10,11 @@ import Foundation import Megrez import Shared -extension vChewingLM { +public extension vChewingLM { /// 與之前的 LMCore 不同,LMCoreNS 直接讀取 plist。 /// 這樣一來可以節省在舊 mac 機種內的資料讀入速度。 /// 目前僅針對輸入法原廠語彙資料檔案使用 plist 格式。 - @frozen public struct LMCoreNS { + @frozen struct LMCoreNS { public private(set) var filePath: String? /// 資料庫辭典。索引內容為經過加密的注音字串,資料內容則為 UTF8 資料陣列。 var dataMap: [String: [Data]] = [:] @@ -144,7 +144,7 @@ extension vChewingLM { theScore = .init(String(neta[1])) ?? defaultScore } if theScore > 0 { - theScore *= -1 // 應對可能忘記寫負號的情形 + theScore *= -1 // 應對可能忘記寫負號的情形 } grams.append(Megrez.Unigram(value: theValue, score: theScore)) if !key.contains("_punctuation") { continue } diff --git a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmPlainBopomofo.swift b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmPlainBopomofo.swift index 2f7cd1ed..c0c060e8 100644 --- a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmPlainBopomofo.swift +++ b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmPlainBopomofo.swift @@ -9,8 +9,8 @@ import Foundation import Shared -extension vChewingLM { - @frozen public struct LMPlainBopomofo { +public extension vChewingLM { + @frozen struct LMPlainBopomofo { public private(set) var filePath: String? var dataMap: [String: String] = [:] diff --git a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmReplacements.swift b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmReplacements.swift index 9fe00802..06286c40 100644 --- a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmReplacements.swift +++ b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmReplacements.swift @@ -8,8 +8,8 @@ import Shared -extension vChewingLM { - @frozen public struct LMReplacements { +public extension vChewingLM { + @frozen struct LMReplacements { public private(set) var filePath: String? var rangeMap: [String: Range] = [:] var strData: String = "" diff --git a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmRevLookup.swift b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmRevLookup.swift index 346c4bf1..99e97c26 100644 --- a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmRevLookup.swift +++ b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmRevLookup.swift @@ -9,8 +9,8 @@ import Foundation import Shared -extension vChewingLM { - @frozen public struct LMRevLookup { +public extension vChewingLM { + @frozen struct LMRevLookup { public private(set) var dataMap: [String: [Data]] = [:] public private(set) var filePath: String = "" diff --git a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmUserOverride.swift b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmUserOverride.swift index db0e47d8..b96d4254 100644 --- a/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmUserOverride.swift +++ b/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmUserOverride.swift @@ -11,21 +11,21 @@ import Foundation import Megrez import Shared -extension vChewingLM { - public class LMUserOverride { +public extension vChewingLM { + class LMUserOverride { // MARK: - Main var mutCapacity: Int var mutDecayExponent: Double var mutLRUList: [KeyObservationPair] = [] var mutLRUMap: [String: KeyObservationPair] = [:] - let kDecayThreshold: Double = 1.0 / 1_048_576.0 // 衰減二十次之後差不多就失效了。 + let kDecayThreshold: Double = 1.0 / 1_048_576.0 // 衰減二十次之後差不多就失效了。 var fileSaveLocationURL: URL - public static let kObservedOverrideHalfLife: Double = 3600.0 * 6 // 6 小時半衰一次,能持續不到六天的記憶。 + public static let kObservedOverrideHalfLife: Double = 3600.0 * 6 // 6 小時半衰一次,能持續不到六天的記憶。 public init(capacity: Int = 500, decayConstant: Double = LMUserOverride.kObservedOverrideHalfLife, dataURL: URL) { - mutCapacity = max(capacity, 1) // Ensures that this integer value is always > 0. + mutCapacity = max(capacity, 1) // Ensures that this integer value is always > 0. mutDecayExponent = log(0.5) / decayConstant fileSaveLocationURL = dataURL } @@ -43,7 +43,7 @@ extension vChewingLM { // 當前節點超過三個字的話,就不記憶了。在這種情形下,使用者可以考慮新增自訂語彙。 guard currentNode.spanLength <= 3 else { return } // 前一個節點得從前一次爬軌結果當中來找。 - guard actualCursor > 0 else { return } // 該情況應該不會出現。 + guard actualCursor > 0 else { return } // 該情況應該不會出現。 let currentNodeIndex = actualCursor actualCursor -= 1 var prevNodeIndex = 0 @@ -153,8 +153,8 @@ extension vChewingLM.LMUserOverride { // MARK: - Hash and Dehash the entire UOM data, etc. -extension vChewingLM.LMUserOverride { - public func bleachSpecifiedSuggestions(targets: [String], saveCallback: @escaping () -> Void) { +public extension vChewingLM.LMUserOverride { + func bleachSpecifiedSuggestions(targets: [String], saveCallback: @escaping () -> Void) { if targets.isEmpty { return } for neta in mutLRUMap { for target in targets { @@ -168,7 +168,7 @@ extension vChewingLM.LMUserOverride { } /// 自 LRU 辭典內移除所有的單元圖。 - public func bleachUnigrams(saveCallback: @escaping () -> Void) { + func bleachUnigrams(saveCallback: @escaping () -> Void) { for key in mutLRUMap.keys { if !key.contains("(),()") { continue } mutLRUMap.removeValue(forKey: key) @@ -184,7 +184,7 @@ extension vChewingLM.LMUserOverride { } } - public func clearData(withURL fileURL: URL) { + func clearData(withURL fileURL: URL) { mutLRUMap = .init() mutLRUList = .init() do { @@ -196,7 +196,7 @@ extension vChewingLM.LMUserOverride { } } - public func saveData(toURL fileURL: URL? = nil) { + func saveData(toURL fileURL: URL? = nil) { let encoder = JSONEncoder() do { guard let jsonData = try? encoder.encode(mutLRUMap) else { return } @@ -208,7 +208,7 @@ extension vChewingLM.LMUserOverride { } } - public func loadData(fromURL fileURL: URL) { + func loadData(fromURL fileURL: URL) { let decoder = JSONDecoder() do { let data = try Data(contentsOf: fileURL, options: .mappedIfSafe) @@ -225,7 +225,7 @@ extension vChewingLM.LMUserOverride { } } - public struct Suggestion { + struct Suggestion { public var candidates = [(String, Megrez.Unigram)]() public var forceHighScoreOverride = false public var isEmpty: Bool { candidates.isEmpty } @@ -282,7 +282,7 @@ extension vChewingLM.LMUserOverride { eventCount: theObservation.count, totalCount: observation.count, eventTimestamp: theObservation.timestamp, timestamp: timestamp, lambda: decayExp ) - if (0...currentHighScore).contains(overrideScore) { continue } + if (0 ... currentHighScore).contains(overrideScore) { continue } candidates.append((headReading, .init(value: i, score: overrideScore))) forceHighScoreOverride = theObservation.forceHighScoreOverride @@ -356,16 +356,16 @@ extension vChewingLM.LMUserOverride { } if arrNodes.count >= 2, - !kvPrevious.joinedKey().contains("_"), - kvPrevious.joinedKey().split(separator: "-").count == kvPrevious.value.count + !kvPrevious.joinedKey().contains("_"), + kvPrevious.joinedKey().split(separator: "-").count == kvPrevious.value.count { kvPrevious = arrNodes[1].currentPair readingStack = kvPrevious.joinedKey() + readingStack } if arrNodes.count >= 3, - !kvAnterior.joinedKey().contains("_"), - kvAnterior.joinedKey().split(separator: "-").count == kvAnterior.value.count + !kvAnterior.joinedKey().contains("_"), + kvAnterior.joinedKey().split(separator: "-").count == kvAnterior.value.count { kvAnterior = arrNodes[2].currentPair readingStack = kvAnterior.joinedKey() + readingStack diff --git a/Packages/vChewing_Megrez/Package.swift b/Packages/vChewing_Megrez/Package.swift index 349d9265..704ca0c5 100644 --- a/Packages/vChewing_Megrez/Package.swift +++ b/Packages/vChewing_Megrez/Package.swift @@ -4,13 +4,13 @@ import PackageDescription let package = Package( name: "Megrez", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "Megrez", targets: ["Megrez"] - ) + ), ], dependencies: [], targets: [ diff --git a/Packages/vChewing_Megrez/Sources/Megrez/1_Compositor.swift b/Packages/vChewing_Megrez/Sources/Megrez/1_Compositor.swift index 9b9dd3e1..40ccc7d9 100644 --- a/Packages/vChewing_Megrez/Sources/Megrez/1_Compositor.swift +++ b/Packages/vChewing_Megrez/Sources/Megrez/1_Compositor.swift @@ -5,7 +5,7 @@ import Foundation -extension Megrez { +public extension Megrez { /// 一個組字器用來在給定一系列的索引鍵的情況下(藉由一系列的觀測行為)返回一套資料值。 /// /// 用於輸入法的話,給定的索引鍵可以是注音、且返回的資料值都是漢語字詞組合。該組字器 @@ -15,7 +15,7 @@ extension Megrez { /// 簡單的貝氏推論:因為底層的語言模組只會提供單元圖資料。一旦將所有可以組字的單元圖 /// 作為節點塞到組字器內,就可以用一個簡單的有向無環圖爬軌過程、來利用這些隱性資料值 /// 算出最大相似估算結果。 - public struct Compositor { + struct Compositor { /// 就文字輸入方向而言的方向。 public enum TypingDirection { case front, rear } /// 軌格增減行為。 @@ -92,7 +92,7 @@ extension Megrez { spans = gridBackup return false } - cursor += 1 // 游標必須得在執行 update() 之後才可以變動。 + cursor += 1 // 游標必須得在執行 update() 之後才可以變動。 return true } @@ -106,7 +106,7 @@ extension Megrez { let isBackSpace: Bool = direction == .rear ? true : false guard cursor != (isBackSpace ? 0 : keys.count) else { return false } keys.remove(at: cursor - (isBackSpace ? 1 : 0)) - cursor -= isBackSpace ? 1 : 0 // 在縮節之前。 + cursor -= isBackSpace ? 1 : 0 // 在縮節之前。 resizeGrid(at: cursor, do: .shrink) update() return true @@ -129,36 +129,36 @@ extension Megrez { { var target = isMarker ? marker : cursor switch direction { - case .front: - if target == length { return false } - case .rear: - if target == 0 { return false } + case .front: + if target == length { return false } + case .rear: + if target == 0 { return false } } guard let currentRegion = walkedNodes.cursorRegionMap[target] else { return false } let aRegionForward = max(currentRegion - 1, 0) - let currentRegionBorderRear: Int = walkedNodes[0.. walkedNodes.count) - ? keys.count : walkedNodes[0...currentRegion].map(\.spanLength).reduce(0, +) - case .rear: - target = walkedNodes[0.. walkedNodes.count) + ? keys.count : walkedNodes[0 ... currentRegion].map(\.spanLength).reduce(0, +) + case .rear: + target = walkedNodes[0 ..< aRegionForward].map(\.spanLength).reduce(0, +) + } + default: + switch direction { + case .front: + target = currentRegionBorderRear + walkedNodes[currentRegion].spanLength + case .rear: + target = currentRegionBorderRear + } } switch isMarker { - case false: cursor = target - case true: marker = target + case false: cursor = target + case true: marker = target } return true } @@ -168,7 +168,7 @@ extension Megrez { // C# StringBuilder 與 Swift NSMutableString 能提供爆發性的效能。 let strOutput: NSMutableString = .init(string: "digraph {\ngraph [ rankdir=LR ];\nBOS;\n") for (p, span) in spans.enumerated() { - for ni in 0...(span.maxLength) { + for ni in 0 ... (span.maxLength) { guard let np = span.nodeOf(length: ni) else { continue } if p == 0 { strOutput.append("BOS -> \(np.value);\n") @@ -176,7 +176,7 @@ extension Megrez { strOutput.append("\(np.value);\n") if (p + ni) < spans.count { let destinationSpan = spans[p + ni] - for q in 0...(destinationSpan.maxLength) { + for q in 0 ... (destinationSpan.maxLength) { guard let dn = destinationSpan.nodeOf(length: q) else { continue } strOutput.append(np.value + " -> " + dn.value + ";\n") } @@ -199,14 +199,14 @@ extension Megrez.Compositor { /// - location: 給定的幅位座標。 /// - action: 指定是擴張還是縮減一個幅位。 mutating func resizeGrid(at location: Int, do action: ResizeBehavior) { - let location = max(min(location, spans.count), 0) // 防呆 + let location = max(min(location, spans.count), 0) // 防呆 switch action { - case .expand: - spans.insert(SpanUnit(), at: location) - if [0, spans.count].contains(location) { return } - case .shrink: - if spans.count == location { return } - spans.remove(at: location) + case .expand: + spans.insert(SpanUnit(), at: location) + if [0, spans.count].contains(location) { return } + case .shrink: + if spans.count == location { return } + spans.remove(at: location) } dropWreckedNodes(at: location) } @@ -243,12 +243,12 @@ extension Megrez.Compositor { /// ``` /// - Parameter location: 給定的幅位座標。 mutating func dropWreckedNodes(at location: Int) { - let location = max(min(location, spans.count), 0) // 防呆 + let location = max(min(location, spans.count), 0) // 防呆 guard !spans.isEmpty else { return } let affectedLength = Megrez.Compositor.maxSpanLength - 1 let begin = max(0, location - affectedLength) guard location >= begin else { return } - for i in begin.. Node? { - let location = max(min(location, spans.count - 1), 0) // 防呆 + let location = max(min(location, spans.count - 1), 0) // 防呆 guard let node = spans[location].nodeOf(length: length) else { return nil } return keyArray == node.keyArray ? node : nil } @@ -280,11 +280,11 @@ extension Megrez.Compositor { /// - Returns: 新增或影響了多少個節點。如果返回「0」則表示可能發生了錯誤。 @discardableResult public mutating func update(updateExisting: Bool = false) -> Int { let maxSpanLength = Megrez.Compositor.maxSpanLength - let range = max(0, cursor - maxSpanLength).. (walkedNode: [Node], succeeded: Bool) { + @discardableResult mutating func walk() -> (walkedNode: [Node], succeeded: Bool) { var result = [Node]() defer { walkedNodes = result } guard !spans.isEmpty else { return (result, true) } @@ -24,7 +24,7 @@ extension Megrez.Compositor { } for (i, span) in spans.enumerated() { - for j in 1...span.maxLength { + for j in 1 ... span.maxLength { if let theNode = span.nodeOf(length: j) { vertexSpans[i].append(.init(node: theNode)) } diff --git a/Packages/vChewing_Megrez/Sources/Megrez/3_KeyValuePaired.swift b/Packages/vChewing_Megrez/Sources/Megrez/3_KeyValuePaired.swift index 0b622c93..609179a6 100644 --- a/Packages/vChewing_Megrez/Sources/Megrez/3_KeyValuePaired.swift +++ b/Packages/vChewing_Megrez/Sources/Megrez/3_KeyValuePaired.swift @@ -5,9 +5,9 @@ import Foundation -extension Megrez.Compositor { +public extension Megrez.Compositor { /// 鍵值配對,乃索引鍵陣列與讀音的配對單元。 - public struct KeyValuePaired: Equatable, Hashable, Comparable, CustomStringConvertible { + struct KeyValuePaired: Equatable, Hashable, Comparable, CustomStringConvertible { /// 索引鍵陣列。一般情況下用來放置讀音等可以用來作為索引的內容。 public var keyArray: [String] /// 資料值。 @@ -77,17 +77,17 @@ extension Megrez.Compositor { /// - all: 不只包含其它兩類結果,還允許游標穿插候選字。 /// - beginAt: 僅獲取從當前游標位置開始的節點內的候選字。 /// - endAt 僅獲取在當前游標位置結束的節點內的候選字。 - public enum CandidateFetchFilter { case all, beginAt, endAt } + enum CandidateFetchFilter { case all, beginAt, endAt } /// 返回在當前位置的所有候選字詞(以詞音配對的形式)。如果組字器內有幅位、且游標 /// 位於組字器的(文字輸入順序的)最前方(也就是游標位置的數值是最大合規數值)的 /// 話,那麼這裡會用到 location - 1、以免去在呼叫該函式後再處理的麻煩。 /// - Parameter location: 游標位置。 /// - Returns: 候選字音配對陣列。 - public func fetchCandidates(at location: Int, filter: CandidateFetchFilter = .all) -> [KeyValuePaired] { + func fetchCandidates(at location: Int, filter: CandidateFetchFilter = .all) -> [KeyValuePaired] { var result = [KeyValuePaired]() guard !keys.isEmpty else { return result } - let location = max(min(location, keys.count - 1), 0) // 防呆 + let location = max(min(location, keys.count - 1), 0) // 防呆 let anchors: [NodeAnchor] = fetchOverlappingNodes(at: location).stableSorted { // 按照讀音的長度(幅位長度)來給節點排序。 $0.spanLength > $1.spanLength @@ -97,13 +97,13 @@ extension Megrez.Compositor { if theNode.keyArray.isEmpty { continue } for gram in theNode.unigrams { switch filter { - case .all: - // 得加上這道篩選,所以會出現很多無效結果。 - if !theNode.keyArray.contains(keyAtCursor) { continue } - case .beginAt: - if theNode.keyArray[0] != keyAtCursor { continue } - case .endAt: - if theNode.keyArray.reversed()[0] != keyAtCursor { continue } + case .all: + // 得加上這道篩選,所以會出現很多無效結果。 + if !theNode.keyArray.contains(keyAtCursor) { continue } + case .beginAt: + if theNode.keyArray[0] != keyAtCursor { continue } + case .endAt: + if theNode.keyArray.reversed()[0] != keyAtCursor { continue } } result.append(.init(keyArray: theNode.keyArray, value: gram.value)) } @@ -119,7 +119,7 @@ extension Megrez.Compositor { /// - location: 游標位置。 /// - overrideType: 指定覆寫行為。 /// - Returns: 該操作是否成功執行。 - @discardableResult public func overrideCandidate( + @discardableResult func overrideCandidate( _ candidate: KeyValuePaired, at location: Int, overrideType: Node.OverrideType = .withHighScore ) -> Bool @@ -135,7 +135,7 @@ extension Megrez.Compositor { /// - location: 游標位置。 /// - overrideType: 指定覆寫行為。 /// - Returns: 該操作是否成功執行。 - @discardableResult public func overrideCandidateLiteral( + @discardableResult func overrideCandidateLiteral( _ candidate: String, at location: Int, overrideType: Node.OverrideType = .withHighScore ) -> Bool { @@ -154,7 +154,7 @@ extension Megrez.Compositor { internal func overrideCandidateAgainst(keyArray: [String]?, at location: Int, value: String, type: Node.OverrideType) -> Bool { - let location = max(min(location, keys.count), 0) // 防呆 + let location = max(min(location, keys.count), 0) // 防呆 var arrOverlappedNodes: [NodeAnchor] = fetchOverlappingNodes(at: min(keys.count - 1, location)) var overridden: NodeAnchor? for anchor in arrOverlappedNodes { @@ -164,9 +164,9 @@ extension Megrez.Compositor { break } - guard let overridden = overridden else { return false } // 啥也不覆寫。 + guard let overridden = overridden else { return false } // 啥也不覆寫。 - for i in overridden.spanIndex.. Bool ) rethrows -> [Element] diff --git a/Packages/vChewing_Megrez/Sources/Megrez/4_SpanUnit.swift b/Packages/vChewing_Megrez/Sources/Megrez/4_SpanUnit.swift index d374dacb..c74d271f 100644 --- a/Packages/vChewing_Megrez/Sources/Megrez/4_SpanUnit.swift +++ b/Packages/vChewing_Megrez/Sources/Megrez/4_SpanUnit.swift @@ -15,7 +15,7 @@ extension Megrez.Compositor { /// (該變數為捷徑,代傳 Megrez.Compositor.maxSpanLength。) private var maxSpanLength: Int { Megrez.Compositor.maxSpanLength } /// 該幅位單元內的節點的幅位長度上限。 - private var allowedLengths: ClosedRange { 1...maxSpanLength } + private var allowedLengths: ClosedRange { 1 ... maxSpanLength } /// 幅位乃指一組共享起點的節點。 public init() { @@ -25,7 +25,7 @@ extension Megrez.Compositor { /// 清除該幅位單元的全部的節點,且重設最長節點長度為 0,然後再在節點陣列內預留空位。 public func clear() { nodes.removeAll() - for _ in 0.. 1 else { return false } let maxR = length - 2 - for i in 0...maxR { + for i in 0 ... maxR { if nodes[maxR - i] == nil { continue } maxLength = maxR - i + 1 break @@ -83,17 +83,17 @@ extension Megrez.Compositor { guard !spans.isEmpty, location < spans.count else { return results } // 先獲取該位置的所有單字節點。 - for theLocation in 1...spans[location].maxLength { + for theLocation in 1 ... spans[location].maxLength { guard let node = spans[location].nodeOf(length: theLocation) else { continue } results.append(.init(node: node, spanIndex: location)) } // 再獲取以當前位置結尾或開頭的節點。 let begin: Int = location - min(location, Megrez.Compositor.maxSpanLength - 1) - for theLocation in begin.. [String] { + func joinedKeys(by separator: String = Megrez.Compositor.theSeparator) -> [String] { map { $0.keyArray.lazy.joined(separator: separator) } } /// 從一個節點陣列當中取出目前的索引鍵陣列。 - public var keyArrays: [[String]] { map(\.keyArray) } + var keyArrays: [[String]] { map(\.keyArray) } /// 返回一連串的節點起點。結果為 (Result A, Result B) 辭典陣列。 /// Result A 以索引查座標,Result B 以座標查索引。 private var nodeBorderPointDictPair: (regionCursorMap: [Int: Int], cursorRegionMap: [Int: Int]) { // Result A 以索引查座標,Result B 以座標查索引。 var resultA = [Int: Int]() - var resultB: [Int: Int] = [-1: 0] // 防呆 + var resultB: [Int: Int] = [-1: 0] // 防呆 var cursorCounter = 0 for (nodeCounter, neta) in enumerated() { resultA[nodeCounter] = cursorCounter @@ -217,25 +217,25 @@ extension Array where Element == Megrez.Compositor.Node { } /// 返回一個辭典,以座標查索引。允許以游標位置查詢其屬於第幾個幅位座標(從 0 開始算)。 - public var cursorRegionMap: [Int: Int] { nodeBorderPointDictPair.cursorRegionMap } + var cursorRegionMap: [Int: Int] { nodeBorderPointDictPair.cursorRegionMap } /// 總讀音單元數量。在絕大多數情況下,可視為總幅位長度。 - public var totalKeyCount: Int { map(\.keyArray.count).reduce(0, +) } + var totalKeyCount: Int { map(\.keyArray.count).reduce(0, +) } /// 根據給定的游標,返回其前後最近的邊界點。 /// - Parameter cursor: 給定的游標。 - public func contextRange(ofGivenCursor cursor: Int) -> Range { - guard !isEmpty else { return 0..<0 } + func contextRange(ofGivenCursor cursor: Int) -> Range { + guard !isEmpty else { return 0 ..< 0 } let lastSpanningLength = reversed()[0].keyArray.count - var nilReturn = (totalKeyCount - lastSpanningLength)..= totalKeyCount { return nilReturn } // 防呆 - let cursor = Swift.max(0, cursor) // 防呆 - nilReturn = cursor..= totalKeyCount { return nilReturn } // 防呆 + let cursor = Swift.max(0, cursor) // 防呆 + nilReturn = cursor ..< cursor // 下文按道理來講不應該會出現 nilReturn。 guard let rearNodeID = nodeBorderPointDictPair.cursorRegionMap[cursor] else { return nilReturn } guard let rearIndex = nodeBorderPointDictPair.regionCursorMap[rearNodeID] else { return nilReturn } guard let frontIndex = nodeBorderPointDictPair.regionCursorMap[rearNodeID + 1] else { return nilReturn } - return rearIndex.. Megrez.Compositor.Node? { + func findNode(at cursor: Int, target outCursorPastNode: inout Int) -> Megrez.Compositor.Node? { guard !isEmpty else { return nil } - let cursor = Swift.max(0, Swift.min(cursor, totalKeyCount - 1)) // 防呆 + let cursor = Swift.max(0, Swift.min(cursor, totalKeyCount - 1)) // 防呆 let range = contextRange(ofGivenCursor: cursor) outCursorPastNode = range.upperBound guard let rearNodeID = nodeBorderPointDictPair.1[cursor] else { return nil } @@ -255,13 +255,13 @@ extension Array where Element == Megrez.Compositor.Node { /// 在陣列內以給定游標位置找出對應的節點。 /// - Parameter cursor: 給定游標位置。 /// - Returns: 查找結果。 - public func findNode(at cursor: Int) -> Megrez.Compositor.Node? { + func findNode(at cursor: Int) -> Megrez.Compositor.Node? { var useless = 0 return findNode(at: cursor, target: &useless) } /// 提供一組逐字的字音配對陣列(不使用 Megrez 的 KeyValuePaired 類型),但字音不匹配的節點除外。 - public var smashedPairs: [(key: String, value: String)] { + var smashedPairs: [(key: String, value: String)] { var arrData = [(key: String, value: String)]() let separator = Megrez.Compositor.theSeparator forEach { node in diff --git a/Packages/vChewing_Megrez/Sources/Megrez/7_LangModel.swift b/Packages/vChewing_Megrez/Sources/Megrez/7_LangModel.swift index 52f3a484..42c78baa 100644 --- a/Packages/vChewing_Megrez/Sources/Megrez/7_LangModel.swift +++ b/Packages/vChewing_Megrez/Sources/Megrez/7_LangModel.swift @@ -11,9 +11,9 @@ public protocol LangModelProtocol { func hasUnigramsFor(keyArray: [String]) -> Bool } -extension Megrez.Compositor { +public extension Megrez.Compositor { /// 一個套殼語言模型,用來始終返回經過排序的單元圖。 - public class LangModelRanked: LangModelProtocol { + class LangModelRanked: LangModelProtocol { private let langModel: LangModelProtocol /// 一個套殼語言模型,用來始終返回經過排序的單元圖。 /// - Parameter withLM: 用來對接的語言模型。 @@ -41,12 +41,12 @@ extension Megrez.Compositor { // Reference: https://stackoverflow.com/a/50545761/4162914 -extension Sequence { +private extension Sequence { /// Return a stable-sorted collection. /// /// - Parameter areInIncreasingOrder: Return nil when two element are equal. /// - Returns: The sorted collection. - fileprivate func stableSorted( + func stableSorted( by areInIncreasingOrder: (Element, Element) throws -> Bool ) rethrows -> [Element] diff --git a/Packages/vChewing_Megrez/Sources/Megrez/8_Unigram.swift b/Packages/vChewing_Megrez/Sources/Megrez/8_Unigram.swift index bf122c69..5f056be7 100644 --- a/Packages/vChewing_Megrez/Sources/Megrez/8_Unigram.swift +++ b/Packages/vChewing_Megrez/Sources/Megrez/8_Unigram.swift @@ -3,9 +3,9 @@ // ==================== // This code is released under the MIT license (SPDX-License-Identifier: MIT) -extension Megrez { +public extension Megrez { /// 單元圖。 - @frozen public struct Unigram: Equatable, CustomStringConvertible, Hashable { + @frozen struct Unigram: Equatable, CustomStringConvertible, Hashable { /// 資料值,通常是詞語或單個字。 public var value: String /// 權重。 @@ -43,9 +43,9 @@ extension Megrez { // MARK: - Array Extensions. -extension Array where Element == Megrez.Unigram { +public extension Array where Element == Megrez.Unigram { /// 給定過濾清單,讓單元圖陣列自我過濾。 - public mutating func consolidate(filter theFilter: Set = .init()) { + mutating func consolidate(filter theFilter: Set = .init()) { var inserted: [String: Double] = [:] var insertedArray: [Megrez.Unigram] = [] for neta in filter({ !theFilter.contains($0.value) }) { diff --git a/Packages/vChewing_Megrez/Tests/MegrezTests/LMDataForTests.swift b/Packages/vChewing_Megrez/Tests/MegrezTests/LMDataForTests.swift index 017721ac..6f9445a9 100644 --- a/Packages/vChewing_Megrez/Tests/MegrezTests/LMDataForTests.swift +++ b/Packages/vChewing_Megrez/Tests/MegrezTests/LMDataForTests.swift @@ -58,121 +58,121 @@ class MockLM: LangModelProtocol { // MARK: - 用以測試的詞頻數據 public let strStressData = #""" - yi1 一 -2.08170692 - yi1-yi1 一一 -4.38468400 +yi1 一 -2.08170692 +yi1-yi1 一一 -4.38468400 - """# +"""# public let strEmojiSampleData = #""" - gao1 高 -2.9396 - re4 熱 -3.6024 - gao1re4 高熱 -6.526 - huo3 火 -3.6966 - huo3 🔥 -8 - yan4 焰 -5.4466 - huo3yan4 火焰 -5.6231 - huo3yan4 🔥 -8 - wei2 危 -3.9832 - xian3 險 -3.7810 - wei2xian3 危險 -4.2623 - mi4feng1 蜜蜂 -3.6231 - mi4 蜜 -4.6231 - feng1 蜂 -4.6231 - feng1 🐝 -11 - mi4feng1 🐝 -11 +gao1 高 -2.9396 +re4 熱 -3.6024 +gao1re4 高熱 -6.526 +huo3 火 -3.6966 +huo3 🔥 -8 +yan4 焰 -5.4466 +huo3yan4 火焰 -5.6231 +huo3yan4 🔥 -8 +wei2 危 -3.9832 +xian3 險 -3.7810 +wei2xian3 危險 -4.2623 +mi4feng1 蜜蜂 -3.6231 +mi4 蜜 -4.6231 +feng1 蜂 -4.6231 +feng1 🐝 -11 +mi4feng1 🐝 -11 - """# +"""# public let strSampleData = #""" - # - # 下述詞頻資料取自 libTaBE 資料庫 (http://sourceforge.net/projects/libtabe/) - # (2002 最終版). 該專案於 1999 年由 Pai-Hsiang Hsiao 發起、以 BSD 授權發行。 - # - ni3 你 -6.000000 // Non-LibTaBE - zhe4 這 -6.000000 // Non-LibTaBE - yang4 樣 -6.000000 // Non-LibTaBE - si1 絲 -9.495858 - si1 思 -9.006414 - si1 私 -99.000000 - si1 斯 -8.091803 - si1 司 -99.000000 - si1 嘶 -13.513987 - si1 撕 -12.259095 - gao1 高 -7.171551 - ke1 顆 -10.574273 - ke1 棵 -11.504072 - ke1 刻 -10.450457 - ke1 科 -7.171052 - ke1 柯 -99.000000 - gao1 膏 -11.928720 - gao1 篙 -13.624335 - gao1 糕 -12.390804 - de5 的 -3.516024 - di2 的 -3.516024 - di4 的 -3.516024 - zhong1 中 -5.809297 - de5 得 -7.427179 - gong1 共 -8.381971 - gong1 供 -8.501463 - ji4 既 -99.000000 - jin1 今 -8.034095 - gong1 紅 -8.858181 - ji4 際 -7.608341 - ji4 季 -99.000000 - jin1 金 -7.290109 - ji4 騎 -10.939895 - zhong1 終 -99.000000 - ji4 記 -99.000000 - ji4 寄 -99.000000 - jin1 斤 -99.000000 - ji4 繼 -9.715317 - ji4 計 -7.926683 - ji4 暨 -8.373022 - zhong1 鐘 -9.877580 - jin1 禁 -10.711079 - gong1 公 -7.877973 - gong1 工 -7.822167 - gong1 攻 -99.000000 - gong1 功 -99.000000 - gong1 宮 -99.000000 - zhong1 鍾 -9.685671 - ji4 繫 -10.425662 - gong1 弓 -99.000000 - gong1 恭 -99.000000 - ji4 劑 -8.888722 - ji4 祭 -10.204425 - jin1 浸 -11.378321 - zhong1 盅 -99.000000 - ji4 忌 -99.000000 - ji4 技 -8.450826 - jin1 筋 -11.074890 - gong1 躬 -99.000000 - ji4 冀 -12.045357 - zhong1 忠 -99.000000 - ji4 妓 -99.000000 - ji4 濟 -9.517568 - ji4 薊 -12.021587 - jin1 巾 -99.000000 - jin1 襟 -12.784206 - nian2 年 -6.086515 - jiang3 講 -9.164384 - jiang3 獎 -8.690941 - jiang3 蔣 -10.127828 - nian2 黏 -11.336864 - nian2 粘 -11.285740 - jiang3 槳 -12.492933 - gong1si1 公司 -6.299461 - ke1ji4 科技 -6.736613 - ji4gong1 濟公 -13.336653 - jiang3jin1 獎金 -10.344678 - nian2zhong1 年終 -11.668947 - nian2zhong1 年中 -11.373044 - gao1ke1ji4 高科技 -9.842421 - zhe4yang4 這樣 -6.000000 // Non-LibTaBE - ni3zhe4 你這 -9.000000 // Non-LibTaBE - jiao4 教 -3.676169 - jiao4 較 -3.24869962 - jiao4yu4 教育 -3.32220565 - yu4 育 -3.30192952 +# +# 下述詞頻資料取自 libTaBE 資料庫 (http://sourceforge.net/projects/libtabe/) +# (2002 最終版). 該專案於 1999 年由 Pai-Hsiang Hsiao 發起、以 BSD 授權發行。 +# +ni3 你 -6.000000 // Non-LibTaBE +zhe4 這 -6.000000 // Non-LibTaBE +yang4 樣 -6.000000 // Non-LibTaBE +si1 絲 -9.495858 +si1 思 -9.006414 +si1 私 -99.000000 +si1 斯 -8.091803 +si1 司 -99.000000 +si1 嘶 -13.513987 +si1 撕 -12.259095 +gao1 高 -7.171551 +ke1 顆 -10.574273 +ke1 棵 -11.504072 +ke1 刻 -10.450457 +ke1 科 -7.171052 +ke1 柯 -99.000000 +gao1 膏 -11.928720 +gao1 篙 -13.624335 +gao1 糕 -12.390804 +de5 的 -3.516024 +di2 的 -3.516024 +di4 的 -3.516024 +zhong1 中 -5.809297 +de5 得 -7.427179 +gong1 共 -8.381971 +gong1 供 -8.501463 +ji4 既 -99.000000 +jin1 今 -8.034095 +gong1 紅 -8.858181 +ji4 際 -7.608341 +ji4 季 -99.000000 +jin1 金 -7.290109 +ji4 騎 -10.939895 +zhong1 終 -99.000000 +ji4 記 -99.000000 +ji4 寄 -99.000000 +jin1 斤 -99.000000 +ji4 繼 -9.715317 +ji4 計 -7.926683 +ji4 暨 -8.373022 +zhong1 鐘 -9.877580 +jin1 禁 -10.711079 +gong1 公 -7.877973 +gong1 工 -7.822167 +gong1 攻 -99.000000 +gong1 功 -99.000000 +gong1 宮 -99.000000 +zhong1 鍾 -9.685671 +ji4 繫 -10.425662 +gong1 弓 -99.000000 +gong1 恭 -99.000000 +ji4 劑 -8.888722 +ji4 祭 -10.204425 +jin1 浸 -11.378321 +zhong1 盅 -99.000000 +ji4 忌 -99.000000 +ji4 技 -8.450826 +jin1 筋 -11.074890 +gong1 躬 -99.000000 +ji4 冀 -12.045357 +zhong1 忠 -99.000000 +ji4 妓 -99.000000 +ji4 濟 -9.517568 +ji4 薊 -12.021587 +jin1 巾 -99.000000 +jin1 襟 -12.784206 +nian2 年 -6.086515 +jiang3 講 -9.164384 +jiang3 獎 -8.690941 +jiang3 蔣 -10.127828 +nian2 黏 -11.336864 +nian2 粘 -11.285740 +jiang3 槳 -12.492933 +gong1si1 公司 -6.299461 +ke1ji4 科技 -6.736613 +ji4gong1 濟公 -13.336653 +jiang3jin1 獎金 -10.344678 +nian2zhong1 年終 -11.668947 +nian2zhong1 年中 -11.373044 +gao1ke1ji4 高科技 -9.842421 +zhe4yang4 這樣 -6.000000 // Non-LibTaBE +ni3zhe4 你這 -9.000000 // Non-LibTaBE +jiao4 教 -3.676169 +jiao4 較 -3.24869962 +jiao4yu4 教育 -3.32220565 +yu4 育 -3.30192952 - """# +"""# diff --git a/Packages/vChewing_Megrez/Tests/MegrezTests/MegrezTests.swift b/Packages/vChewing_Megrez/Tests/MegrezTests/MegrezTests.swift index 45bf07ad..27daf0c3 100644 --- a/Packages/vChewing_Megrez/Tests/MegrezTests/MegrezTests.swift +++ b/Packages/vChewing_Megrez/Tests/MegrezTests/MegrezTests.swift @@ -335,7 +335,7 @@ final class MegrezTests: XCTestCase { func test13_Compositor_StressBench() throws { NSLog("// Stress test preparation begins.") var compositor = Megrez.Compositor(with: SimpleLM(input: strStressData)) - for _ in 0..<1919 { + for _ in 0 ..< 1919 { compositor.insertKey("yi") } NSLog("// Stress test started.") diff --git a/Packages/vChewing_Megrez/makefile b/Packages/vChewing_Megrez/makefile index 80f07f65..db02b56a 100644 --- a/Packages/vChewing_Megrez/makefile +++ b/Packages/vChewing_Megrez/makefile @@ -1,6 +1,7 @@ -.PHONY: format +.PHONY: lint format format: - swiftformat ./ --swiftversion 5.5 - @git ls-files --exclude-standard | grep -E '\.swift$$' | xargs swift-format format --in-place --configuration ./.clang-format-swift.json --parallel - @git ls-files --exclude-standard | grep -E '\.swift$$' | xargs swift-format lint --configuration ./.clang-format-swift.json --parallel + @swiftformat --swiftversion 5.5 --indent 2 ./ + +lint: + @git ls-files --exclude-standard | grep -E '\.swift$$' | swiftlint --fix --autocorrect \ No newline at end of file diff --git a/Packages/vChewing_NotifierUI/Package.swift b/Packages/vChewing_NotifierUI/Package.swift index 45cda7d5..2f604b11 100644 --- a/Packages/vChewing_NotifierUI/Package.swift +++ b/Packages/vChewing_NotifierUI/Package.swift @@ -4,19 +4,19 @@ import PackageDescription let package = Package( name: "NotifierUI", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "NotifierUI", targets: ["NotifierUI"] - ) + ), ], dependencies: [], targets: [ .target( name: "NotifierUI", dependencies: [] - ) + ), ] ) diff --git a/Packages/vChewing_NotifierUI/Sources/NotifierUI/NotifierUI.swift b/Packages/vChewing_NotifierUI/Sources/NotifierUI/NotifierUI.swift index 96352b71..9ec1d883 100644 --- a/Packages/vChewing_NotifierUI/Sources/NotifierUI/NotifierUI.swift +++ b/Packages/vChewing_NotifierUI/Sources/NotifierUI/NotifierUI.swift @@ -21,8 +21,8 @@ public class Notifier: NSWindowController { } } - private var currentMessage: String // 承載該副本在初期化時被傳入的訊息內容。 - private var isNew = true // 新通知標記。 + private var currentMessage: String // 承載該副本在初期化時被傳入的訊息內容。 + private var isNew = true // 新通知標記。 // MARK: - Private Declarations @@ -178,15 +178,15 @@ extension Notifier { } } -extension NSMutableOrderedSet { - fileprivate var arrayOfWindows: [NSWindow] { compactMap { ($0 as? Notifier)?.window } } +private extension NSMutableOrderedSet { + var arrayOfWindows: [NSWindow] { compactMap { ($0 as? Notifier)?.window } } - fileprivate var firstNotifier: Notifier? { + var firstNotifier: Notifier? { for neta in self { if let result = neta as? Notifier { return result } } return nil } - fileprivate var lastNotifier: Notifier? { + var lastNotifier: Notifier? { for neta in reversed { if let result = neta as? Notifier { return result } } return nil } diff --git a/Packages/vChewing_PhraseEditorUI/Package.swift b/Packages/vChewing_PhraseEditorUI/Package.swift index c76458ae..17097949 100644 --- a/Packages/vChewing_PhraseEditorUI/Package.swift +++ b/Packages/vChewing_PhraseEditorUI/Package.swift @@ -4,13 +4,13 @@ import PackageDescription let package = Package( name: "PhraseEditorUI", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "PhraseEditorUI", targets: ["PhraseEditorUI"] - ) + ), ], dependencies: [ .package(path: "../vChewing_LangModelAssembly"), @@ -25,6 +25,6 @@ let package = Package( .product(name: "SwiftUIBackports", package: "ShapsBenkau_SwiftUIBackports"), .product(name: "Shared", package: "vChewing_Shared"), ] - ) + ), ] ) diff --git a/Packages/vChewing_PhraseEditorUI/Sources/PhraseEditorUI/PhraseEditorUI.swift b/Packages/vChewing_PhraseEditorUI/Sources/PhraseEditorUI/PhraseEditorUI.swift index eaf273e7..3593d066 100644 --- a/Packages/vChewing_PhraseEditorUI/Sources/PhraseEditorUI/PhraseEditorUI.swift +++ b/Packages/vChewing_PhraseEditorUI/Sources/PhraseEditorUI/PhraseEditorUI.swift @@ -92,34 +92,34 @@ public struct VwrPhraseEditorUI: View { private func updateLabels() { clearAllFields() switch selUserDataType { - case .thePhrases: - lblAddPhraseTag1 = PETerms.AddPhrases.locPhrase.localized.0 - lblAddPhraseTag2 = PETerms.AddPhrases.locReadingOrStroke.localized.0 - lblAddPhraseTag3 = PETerms.AddPhrases.locWeight.localized.0 - lblAddPhraseTag4 = PETerms.AddPhrases.locComment.localized.0 - case .theFilter: - lblAddPhraseTag1 = PETerms.AddPhrases.locPhrase.localized.0 - lblAddPhraseTag2 = PETerms.AddPhrases.locReadingOrStroke.localized.0 - lblAddPhraseTag3 = "" - lblAddPhraseTag4 = PETerms.AddPhrases.locComment.localized.0 - case .theReplacements: - lblAddPhraseTag1 = PETerms.AddPhrases.locReplaceTo.localized.0 - lblAddPhraseTag2 = PETerms.AddPhrases.locReplaceTo.localized.1 - lblAddPhraseTag3 = "" - lblAddPhraseTag4 = PETerms.AddPhrases.locComment.localized.0 - case .theAssociates: - lblAddPhraseTag1 = PETerms.AddPhrases.locInitial.localized.0 - lblAddPhraseTag2 = { - let result = PETerms.AddPhrases.locPhrase.localized.0 - return (result == "Phrase") ? "Phrases" : result - }() - lblAddPhraseTag3 = "" - lblAddPhraseTag4 = "" - case .theSymbols: - lblAddPhraseTag1 = PETerms.AddPhrases.locPhrase.localized.0 - lblAddPhraseTag2 = PETerms.AddPhrases.locReadingOrStroke.localized.0 - lblAddPhraseTag3 = "" - lblAddPhraseTag4 = PETerms.AddPhrases.locComment.localized.0 + case .thePhrases: + lblAddPhraseTag1 = PETerms.AddPhrases.locPhrase.localized.0 + lblAddPhraseTag2 = PETerms.AddPhrases.locReadingOrStroke.localized.0 + lblAddPhraseTag3 = PETerms.AddPhrases.locWeight.localized.0 + lblAddPhraseTag4 = PETerms.AddPhrases.locComment.localized.0 + case .theFilter: + lblAddPhraseTag1 = PETerms.AddPhrases.locPhrase.localized.0 + lblAddPhraseTag2 = PETerms.AddPhrases.locReadingOrStroke.localized.0 + lblAddPhraseTag3 = "" + lblAddPhraseTag4 = PETerms.AddPhrases.locComment.localized.0 + case .theReplacements: + lblAddPhraseTag1 = PETerms.AddPhrases.locReplaceTo.localized.0 + lblAddPhraseTag2 = PETerms.AddPhrases.locReplaceTo.localized.1 + lblAddPhraseTag3 = "" + lblAddPhraseTag4 = PETerms.AddPhrases.locComment.localized.0 + case .theAssociates: + lblAddPhraseTag1 = PETerms.AddPhrases.locInitial.localized.0 + lblAddPhraseTag2 = { + let result = PETerms.AddPhrases.locPhrase.localized.0 + return (result == "Phrase") ? "Phrases" : result + }() + lblAddPhraseTag3 = "" + lblAddPhraseTag4 = "" + case .theSymbols: + lblAddPhraseTag1 = PETerms.AddPhrases.locPhrase.localized.0 + lblAddPhraseTag2 = PETerms.AddPhrases.locReadingOrStroke.localized.0 + lblAddPhraseTag3 = "" + lblAddPhraseTag4 = PETerms.AddPhrases.locComment.localized.0 } } @@ -140,9 +140,9 @@ public struct VwrPhraseEditorUI: View { } if !txtAddPhraseField4.isEmpty { arrResult.append("#" + txtAddPhraseField4) } if let delegate = delegate, - delegate.checkIfUserPhraseExist( - userPhrase: txtAddPhraseField1, mode: selInputMode, key: txtAddPhraseField2 - ) + delegate.checkIfUserPhraseExist( + userPhrase: txtAddPhraseField1, mode: selInputMode, key: txtAddPhraseField2 + ) { arrResult.append(" #𝙾𝚟𝚎𝚛𝚛𝚒𝚍𝚎") } @@ -178,7 +178,7 @@ public struct VwrPhraseEditorUI: View { guard let delegate = delegate, selInputMode != .imeModeNULL else { return } DispatchQueue.main.async { isLoading = true - delegate.consolidate(text: &txtContent, pragma: false) // 強制整理 + delegate.consolidate(text: &txtContent, pragma: false) // 強制整理 if selUserDataType == .thePhrases { delegate.tagOverrides(in: &txtContent, mode: selInputMode) } @@ -198,21 +198,21 @@ public struct VwrPhraseEditorUI: View { HStack { Picker("", selection: $selInputMode.onChange { dropDownMenuDidChange() }) { switch currentIMEInputMode { - case .imeModeCHS: + case .imeModeCHS: + Text(Shared.InputMode.imeModeCHS.localizedDescription).tag(Shared.InputMode.imeModeCHS) + Text(Shared.InputMode.imeModeCHT.localizedDescription).tag(Shared.InputMode.imeModeCHT) + case .imeModeCHT: + Text(Shared.InputMode.imeModeCHT.localizedDescription).tag(Shared.InputMode.imeModeCHT) + Text(Shared.InputMode.imeModeCHS.localizedDescription).tag(Shared.InputMode.imeModeCHS) + case .imeModeNULL: + Text(Shared.InputMode.imeModeNULL.localizedDescription).tag(Shared.InputMode.imeModeNULL) + if loc.contains("Hans") { Text(Shared.InputMode.imeModeCHS.localizedDescription).tag(Shared.InputMode.imeModeCHS) Text(Shared.InputMode.imeModeCHT.localizedDescription).tag(Shared.InputMode.imeModeCHT) - case .imeModeCHT: + } else { Text(Shared.InputMode.imeModeCHT.localizedDescription).tag(Shared.InputMode.imeModeCHT) Text(Shared.InputMode.imeModeCHS.localizedDescription).tag(Shared.InputMode.imeModeCHS) - case .imeModeNULL: - Text(Shared.InputMode.imeModeNULL.localizedDescription).tag(Shared.InputMode.imeModeNULL) - if loc.contains("Hans") { - Text(Shared.InputMode.imeModeCHS.localizedDescription).tag(Shared.InputMode.imeModeCHS) - Text(Shared.InputMode.imeModeCHT.localizedDescription).tag(Shared.InputMode.imeModeCHT) - } else { - Text(Shared.InputMode.imeModeCHT.localizedDescription).tag(Shared.InputMode.imeModeCHT) - Text(Shared.InputMode.imeModeCHS.localizedDescription).tag(Shared.InputMode.imeModeCHS) - } + } } } .labelsHidden() @@ -326,8 +326,8 @@ struct ContentView_Previews: PreviewProvider { } } -extension vChewingLM.ReplacableUserDataType { - public var localizedDescription: String { NSLocalizedString(rawValue, comment: "") } +public extension vChewingLM.ReplacableUserDataType { + var localizedDescription: String { NSLocalizedString(rawValue, comment: "") } } public enum PETerms { @@ -359,15 +359,15 @@ public enum PETerms { public static func sampleDictionaryContent(for type: vChewingLM.ReplacableUserDataType) -> String { var result = "" switch type { - case .thePhrases: - result = - "Example:\nCandidate Reading-Reading Weight #Comment\nCandidate Reading-Reading #Comment".localized + "\n\n" + case .thePhrases: + result = + "Example:\nCandidate Reading-Reading Weight #Comment\nCandidate Reading-Reading #Comment".localized + "\n\n" + weightInputBox.localized - case .theFilter: result = "Example:\nCandidate Reading-Reading #Comment".localized - case .theReplacements: result = "Example:\nOldPhrase NewPhrase #Comment".localized - case .theAssociates: - result = "Example:\nInitial RestPhrase\nInitial RestPhrase1 RestPhrase2 RestPhrase3...".localized - case .theSymbols: result = "Example:\nCandidate Reading-Reading #Comment".localized + case .theFilter: result = "Example:\nCandidate Reading-Reading #Comment".localized + case .theReplacements: result = "Example:\nOldPhrase NewPhrase #Comment".localized + case .theAssociates: + result = "Example:\nInitial RestPhrase\nInitial RestPhrase1 RestPhrase2 RestPhrase3...".localized + case .theSymbols: result = "Example:\nCandidate Reading-Reading #Comment".localized } return result } diff --git a/Packages/vChewing_PinyinPhonaConverter/Package.swift b/Packages/vChewing_PinyinPhonaConverter/Package.swift index 6bd6a429..2a4a2a5b 100644 --- a/Packages/vChewing_PinyinPhonaConverter/Package.swift +++ b/Packages/vChewing_PinyinPhonaConverter/Package.swift @@ -4,19 +4,19 @@ import PackageDescription let package = Package( name: "PinyinPhonaConverter", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "PinyinPhonaConverter", targets: ["PinyinPhonaConverter"] - ) + ), ], dependencies: [], targets: [ .target( name: "PinyinPhonaConverter", dependencies: [] - ) + ), ] ) diff --git a/Packages/vChewing_PinyinPhonaConverter/Sources/PinyinPhonaConverter/PinyinPhonaConverter.swift b/Packages/vChewing_PinyinPhonaConverter/Sources/PinyinPhonaConverter/PinyinPhonaConverter.swift index e1f106b3..9a05477a 100644 --- a/Packages/vChewing_PinyinPhonaConverter/Sources/PinyinPhonaConverter/PinyinPhonaConverter.swift +++ b/Packages/vChewing_PinyinPhonaConverter/Sources/PinyinPhonaConverter/PinyinPhonaConverter.swift @@ -8,8 +8,8 @@ import Cocoa -extension String { - public mutating func convertToPhonabets(newToneOne: String = "") { +public extension String { + mutating func convertToPhonabets(newToneOne: String = "") { if isEmpty || contains("_") || !isNotPureAlphanumerical { return } for key in arrHanyuPinyinToPhonabets { self = replacingOccurrences(of: key.0, with: key.1) @@ -19,8 +19,8 @@ extension String { } /// 檢測字串是否包含半形英數內容 -extension String { - fileprivate var isNotPureAlphanumerical: Bool { +private extension String { + var isNotPureAlphanumerical: Bool { let regex = ".*[^A-Za-z0-9].*" let testString = NSPredicate(format: "SELF MATCHES %@", regex) return testString.evaluate(with: self) diff --git a/Packages/vChewing_PopupCompositionBuffer/Package.swift b/Packages/vChewing_PopupCompositionBuffer/Package.swift index e8ac4343..854c61ee 100644 --- a/Packages/vChewing_PopupCompositionBuffer/Package.swift +++ b/Packages/vChewing_PopupCompositionBuffer/Package.swift @@ -4,23 +4,23 @@ import PackageDescription let package = Package( name: "PopupCompositionBuffer", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "PopupCompositionBuffer", targets: ["PopupCompositionBuffer"] - ) + ), ], dependencies: [ - .package(path: "../vChewing_Shared") + .package(path: "../vChewing_Shared"), ], targets: [ .target( name: "PopupCompositionBuffer", dependencies: [ - .product(name: "Shared", package: "vChewing_Shared") + .product(name: "Shared", package: "vChewing_Shared"), ] - ) + ), ] ) diff --git a/Packages/vChewing_PopupCompositionBuffer/Sources/PopupCompositionBuffer/PopupCompositionBuffer.swift b/Packages/vChewing_PopupCompositionBuffer/Sources/PopupCompositionBuffer/PopupCompositionBuffer.swift index eff04bb9..9ddb7a36 100644 --- a/Packages/vChewing_PopupCompositionBuffer/Sources/PopupCompositionBuffer/PopupCompositionBuffer.swift +++ b/Packages/vChewing_PopupCompositionBuffer/Sources/PopupCompositionBuffer/PopupCompositionBuffer.swift @@ -124,8 +124,8 @@ public class PopupCompositionBuffer: NSWindowController { let attrCursor: NSAttributedString = isTypingDirectionVertical - ? NSMutableAttributedString(string: "▔", attributes: cursorAttributes) - : NSMutableAttributedString(string: "_", attributes: cursorAttributes) + ? NSMutableAttributedString(string: "▔", attributes: cursorAttributes) + : NSMutableAttributedString(string: "_", attributes: cursorAttributes) attrString.insert(attrCursor, at: state.u16Cursor) textShown = attrString diff --git a/Packages/vChewing_Shared/Package.swift b/Packages/vChewing_Shared/Package.swift index 09b9cbf2..5d6e6afc 100644 --- a/Packages/vChewing_Shared/Package.swift +++ b/Packages/vChewing_Shared/Package.swift @@ -4,13 +4,13 @@ import PackageDescription let package = Package( name: "Shared", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "Shared", targets: ["Shared"] - ) + ), ], dependencies: [ .package(path: "../vChewing_CocoaExtension"), @@ -23,6 +23,6 @@ let package = Package( .product(name: "CocoaExtension", package: "vChewing_CocoaExtension"), .product(name: "SwiftExtension", package: "vChewing_SwiftExtension"), ] - ) + ), ] ) diff --git a/Packages/vChewing_Shared/Sources/Shared/Shared.swift b/Packages/vChewing_Shared/Sources/Shared/Shared.swift index 40cab1d4..edf35682 100644 --- a/Packages/vChewing_Shared/Sources/Shared/Shared.swift +++ b/Packages/vChewing_Shared/Sources/Shared/Shared.swift @@ -179,21 +179,21 @@ public enum KeyboardParser: Int, CaseIterable { public var localizedMenuName: String { let rawString: String = { switch self { - case .ofStandard: return "Dachen (Microsoft Standard / Wang / 01, etc.)" - case .ofETen: return "Eten Traditional" - case .ofIBM: return "IBM" - case .ofMiTAC: return "MiTAC" - case .ofSeigyou: return "Seigyou" - case .ofFakeSeigyou: return "Fake Seigyou" - case .ofDachen26: return "Dachen 26 (libChewing)" - case .ofETen26: return "Eten 26" - case .ofHsu: return "Hsu" - case .ofStarlight: return "Starlight" - case .ofHanyuPinyin: return "Hanyu Pinyin with Numeral Intonation" - case .ofSecondaryPinyin: return "Secondary Pinyin with Numeral Intonation" - case .ofYalePinyin: return "Yale Pinyin with Numeral Intonation" - case .ofHualuoPinyin: return "Hualuo Pinyin with Numeral Intonation" - case .ofUniversalPinyin: return "Universal Pinyin with Numeral Intonation" + case .ofStandard: return "Dachen (Microsoft Standard / Wang / 01, etc.)" + case .ofETen: return "Eten Traditional" + case .ofIBM: return "IBM" + case .ofMiTAC: return "MiTAC" + case .ofSeigyou: return "Seigyou" + case .ofFakeSeigyou: return "Fake Seigyou" + case .ofDachen26: return "Dachen 26 (libChewing)" + case .ofETen26: return "Eten 26" + case .ofHsu: return "Hsu" + case .ofStarlight: return "Starlight" + case .ofHanyuPinyin: return "Hanyu Pinyin with Numeral Intonation" + case .ofSecondaryPinyin: return "Secondary Pinyin with Numeral Intonation" + case .ofYalePinyin: return "Yale Pinyin with Numeral Intonation" + case .ofHualuoPinyin: return "Hualuo Pinyin with Numeral Intonation" + case .ofUniversalPinyin: return "Universal Pinyin with Numeral Intonation" } }() return NSLocalizedString(rawString, comment: "") @@ -201,36 +201,36 @@ public enum KeyboardParser: Int, CaseIterable { public var name: String { switch self { - case .ofStandard: - return "Standard" - case .ofETen: - return "ETen" - case .ofHsu: - return "Hsu" - case .ofETen26: - return "ETen26" - case .ofIBM: - return "IBM" - case .ofMiTAC: - return "MiTAC" - case .ofFakeSeigyou: - return "FakeSeigyou" - case .ofDachen26: - return "Dachen26" - case .ofSeigyou: - return "Seigyou" - case .ofStarlight: - return "Starlight" - case .ofHanyuPinyin: - return "HanyuPinyin" - case .ofSecondaryPinyin: - return "SecondaryPinyin" - case .ofYalePinyin: - return "YalePinyin" - case .ofHualuoPinyin: - return "HualuoPinyin" - case .ofUniversalPinyin: - return "UniversalPinyin" + case .ofStandard: + return "Standard" + case .ofETen: + return "ETen" + case .ofHsu: + return "Hsu" + case .ofETen26: + return "ETen26" + case .ofIBM: + return "IBM" + case .ofMiTAC: + return "MiTAC" + case .ofFakeSeigyou: + return "FakeSeigyou" + case .ofDachen26: + return "Dachen26" + case .ofSeigyou: + return "Seigyou" + case .ofStarlight: + return "Starlight" + case .ofHanyuPinyin: + return "HanyuPinyin" + case .ofSecondaryPinyin: + return "SecondaryPinyin" + case .ofYalePinyin: + return "YalePinyin" + case .ofHualuoPinyin: + return "HualuoPinyin" + case .ofUniversalPinyin: + return "UniversalPinyin" } } } @@ -249,19 +249,19 @@ public enum CandidateKey { public var description: String { switch self { - case .invalidCharacters: - return "- " - + NSLocalizedString( - "Candidate keys can only contain printable ASCII characters like alphanumericals.", - comment: "" - ) + "\n" + "- " + NSLocalizedString("Candidate keys cannot contain space.", comment: "") - case .countMismatch: - return "- " - + NSLocalizedString( - "Minimum 6 candidate keys allowed.", comment: "" - ) + "\n" + "- " + NSLocalizedString("Maximum 9 candidate keys allowed.", comment: "") - case .noError: - return "" + case .invalidCharacters: + return "- " + + NSLocalizedString( + "Candidate keys can only contain printable ASCII characters like alphanumericals.", + comment: "" + ) + "\n" + "- " + NSLocalizedString("Candidate keys cannot contain space.", comment: "") + case .countMismatch: + return "- " + + NSLocalizedString( + "Minimum 6 candidate keys allowed.", comment: "" + ) + "\n" + "- " + NSLocalizedString("Maximum 9 candidate keys allowed.", comment: "") + case .noError: + return "" } } } @@ -287,7 +287,7 @@ public enum CandidateKey { } } } - if !(6...9).contains(candidateKeys.count) { + if !(6 ... 9).contains(candidateKeys.count) { result = CandidateKey.ValidationError.countMismatch } return result == ValidationError.noError ? nil : result.description @@ -312,24 +312,24 @@ public enum Shared { case imeModeNULL = "" public var reversed: Shared.InputMode { switch self { - case .imeModeCHS: - return .imeModeCHT - case .imeModeCHT: - return .imeModeCHS - case .imeModeNULL: - return .imeModeNULL + case .imeModeCHS: + return .imeModeCHT + case .imeModeCHT: + return .imeModeCHS + case .imeModeNULL: + return .imeModeNULL } } public var localizedDescription: String { NSLocalizedString(description, comment: "") } public var description: String { switch self { - case .imeModeCHS: - return "Simplified Chinese" - case .imeModeCHT: - return "Traditional Chinese" - case .imeModeNULL: - return "Please select…" + case .imeModeCHS: + return "Simplified Chinese" + case .imeModeCHT: + return "Traditional Chinese" + case .imeModeNULL: + return "Please select…" } } } diff --git a/Packages/vChewing_SwiftExtension/Package.swift b/Packages/vChewing_SwiftExtension/Package.swift index b3e561a0..8dd734e9 100644 --- a/Packages/vChewing_SwiftExtension/Package.swift +++ b/Packages/vChewing_SwiftExtension/Package.swift @@ -4,19 +4,19 @@ import PackageDescription let package = Package( name: "SwiftExtension", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "SwiftExtension", targets: ["SwiftExtension"] - ) + ), ], dependencies: [], targets: [ .target( name: "SwiftExtension", dependencies: [] - ) + ), ] ) diff --git a/Packages/vChewing_SwiftExtension/Sources/SwiftExtension/SwiftExtension.swift b/Packages/vChewing_SwiftExtension/Sources/SwiftExtension/SwiftExtension.swift index d40005ef..b9a04a8e 100644 --- a/Packages/vChewing_SwiftExtension/Sources/SwiftExtension/SwiftExtension.swift +++ b/Packages/vChewing_SwiftExtension/Sources/SwiftExtension/SwiftExtension.swift @@ -10,16 +10,16 @@ import Foundation // MARK: - String.localized extension -extension StringLiteralType { - public var localized: String { NSLocalizedString(description, comment: "") } +public extension StringLiteralType { + var localized: String { NSLocalizedString(description, comment: "") } } // MARK: - Root Extensions // Extend the RangeReplaceableCollection to allow it clean duplicated characters. // Ref: https://stackoverflow.com/questions/25738817/ -extension RangeReplaceableCollection where Element: Hashable { - public var deduplicated: Self { +public extension RangeReplaceableCollection where Element: Hashable { + var deduplicated: Self { var set = Set() return filter { set.insert($0).inserted } } @@ -27,18 +27,18 @@ extension RangeReplaceableCollection where Element: Hashable { // MARK: - String charComponents Extension -extension String { - public var charComponents: [String] { map { String($0) } } +public extension String { + var charComponents: [String] { map { String($0) } } } -extension Array where Element == String.Element { - public var charComponents: [String] { map { String($0) } } +public extension Array where Element == String.Element { + var charComponents: [String] { map { String($0) } } } // MARK: - String Tildes Expansion Extension -extension String { - public var expandingTildeInPath: String { +public extension String { + var expandingTildeInPath: String { (self as NSString).expandingTildeInPath } } @@ -53,8 +53,8 @@ extension String: LocalizedError { // MARK: - Ensuring trailing slash of a string -extension String { - public mutating func ensureTrailingSlash() { +public extension String { + mutating func ensureTrailingSlash() { if !hasSuffix("/") { self += "/" } @@ -64,8 +64,8 @@ extension String { // MARK: - CharCode printability check // Ref: https://forums.swift.org/t/57085/5 -extension UniChar { - public var isPrintable: Bool { +public extension UniChar { + var isPrintable: Bool { guard Unicode.Scalar(UInt32(self)) != nil else { struct NotAWholeScalar: Error {} return false @@ -73,26 +73,26 @@ extension UniChar { return true } - public var isPrintableASCII: Bool { - (32...126).contains(self) + var isPrintableASCII: Bool { + (32 ... 126).contains(self) } } -extension Unicode.Scalar { - public var isPrintableASCII: Bool { - (32...126).contains(value) +public extension Unicode.Scalar { + var isPrintableASCII: Bool { + (32 ... 126).contains(value) } } // MARK: - Stable Sort Extension // Ref: https://stackoverflow.com/a/50545761/4162914 -extension Sequence { +public extension Sequence { /// Return a stable-sorted collection. /// /// - Parameter areInIncreasingOrder: Return nil when two element are equal. /// - Returns: The sorted collection. - public func stableSort( + func stableSort( by areInIncreasingOrder: (Element, Element) throws -> Bool ) rethrows -> [Element] @@ -108,8 +108,8 @@ extension Sequence { // MARK: - Return toggled value. -extension Bool { - public mutating func toggled() -> Bool { +public extension Bool { + mutating func toggled() -> Bool { toggle() return self } @@ -145,8 +145,8 @@ public struct AppProperty { // MARK: - 引入小數點位數控制函式 // Ref: https://stackoverflow.com/a/32581409/4162914 -extension Double { - public func rounded(toPlaces places: Int) -> Double { +public extension Double { + func rounded(toPlaces places: Int) -> Double { let divisor = pow(10.0, Double(places)) return (self * divisor).rounded() / divisor } @@ -155,8 +155,8 @@ extension Double { // MARK: - String RegReplace Extension // Ref: https://stackoverflow.com/a/40993403/4162914 && https://stackoverflow.com/a/71291137/4162914 -extension String { - public mutating func regReplace(pattern: String, replaceWith: String = "") { +public extension String { + mutating func regReplace(pattern: String, replaceWith: String = "") { do { let regex = try NSRegularExpression( pattern: pattern, options: [.caseInsensitive, .anchorsMatchLines] @@ -171,8 +171,8 @@ extension String { // MARK: - String CharName Extension -extension String { - public var charDescriptions: [String] { +public extension String { + var charDescriptions: [String] { flatMap(\.unicodeScalars).compactMap { let theName: String = $0.properties.name ?? "" return String(format: "U+%02X %@", $0.value, theName) @@ -182,14 +182,14 @@ extension String { // MARK: - String Ellipsis Extension -extension String { - public var withEllipsis: String { self + "…" } +public extension String { + var withEllipsis: String { self + "…" } } // MARK: - Localized String Extension for Integers and Floats -extension BinaryFloatingPoint { - public func i18n(loc: String) -> String { +public extension BinaryFloatingPoint { + func i18n(loc: String) -> String { let formatter = NumberFormatter() formatter.locale = Locale(identifier: loc) formatter.numberStyle = .spellOut @@ -197,8 +197,8 @@ extension BinaryFloatingPoint { } } -extension BinaryInteger { - public func i18n(loc: String) -> String { +public extension BinaryInteger { + func i18n(loc: String) -> String { let formatter = NumberFormatter() formatter.locale = Locale(identifier: loc) formatter.numberStyle = .spellOut @@ -223,24 +223,24 @@ extension FileHandle { // Further discussion: https://forums.swift.org/t/62847 -extension Array { - public func revolvedIndex(_ id: Int, clockwise: Bool = true, steps: Int = 1) -> Int { +public extension Array { + func revolvedIndex(_ id: Int, clockwise: Bool = true, steps: Int = 1) -> Int { if id < 0 || steps < 1 { return id } var result = id func revolvedIndexByOneStep(_ id: Int, clockwise: Bool = true) -> Int { let newID = clockwise ? id + 1 : id - 1 - if (0.. Void) -> Binding { +public extension Binding { + func onChange(_ action: @escaping () -> Void) -> Binding { Binding( get: { wrappedValue @@ -159,9 +159,9 @@ public struct TextEditorEX: NSViewRepresentable { // MARK: - Property Wrapper (Bindable Extension) -extension AppProperty { +public extension AppProperty { @available(macOS 10.15, *) - public var projectedValue: Binding { + var projectedValue: Binding { .init( get: { container.object(forKey: key) as? Value ?? defaultValue diff --git a/Packages/vChewing_Tekkon/Package.swift b/Packages/vChewing_Tekkon/Package.swift index 19652345..ff68866d 100644 --- a/Packages/vChewing_Tekkon/Package.swift +++ b/Packages/vChewing_Tekkon/Package.swift @@ -4,13 +4,13 @@ import PackageDescription let package = Package( name: "Tekkon", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "Tekkon", targets: ["Tekkon"] - ) + ), ], dependencies: [], targets: [ diff --git a/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Constants.swift b/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Constants.swift index fa67c37d..534525af 100644 --- a/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Constants.swift +++ b/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Constants.swift @@ -37,7 +37,7 @@ public enum Tekkon { } /// 原始轉換對照表資料貯存專用佇列(數字標調格式) - static let arrPhonaToHanyuPinyin: [[String]] = [ // 排序很重要。先處理最長的,再處理短的。不然會出亂子。 + static let arrPhonaToHanyuPinyin: [[String]] = [ // 排序很重要。先處理最長的,再處理短的。不然會出亂子。 [" ", "1"], ["ˊ", "2"], ["ˇ", "3"], ["ˋ", "4"], ["˙", "5"], ["ㄅㄧㄝ", "bie"], ["ㄅㄧㄠ", "biao"], ["ㄅㄧㄢ", "bian"], ["ㄅㄧㄣ", "bin"], ["ㄅㄧㄥ", "bing"], ["ㄆㄧㄚ", "pia"], ["ㄆㄧㄝ", "pie"], @@ -110,7 +110,7 @@ public enum Tekkon { ] /// 漢語拼音韻母轉換對照表資料貯存專用佇列 - static let arrHanyuPinyinTextbookStyleConversionTable: [[String]] = [ // 排序很重要。先處理最長的,再處理短的。不然會出亂子。 + static let arrHanyuPinyinTextbookStyleConversionTable: [[String]] = [ // 排序很重要。先處理最長的,再處理短的。不然會出亂子。 ["iang1", "iāng"], ["iang2", "iáng"], ["iang3", "iǎng"], ["iang4", "iàng"], ["iong1", "iōng"], ["iong2", "ióng"], ["iong3", "iǒng"], ["iong4", "iòng"], ["uang1", "uāng"], ["uang2", "uáng"], ["uang3", "uǎng"], ["uang4", "uàng"], ["uang5", "uang"], diff --git a/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Phonabets.swift b/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Phonabets.swift index 7943e912..4a5513fa 100644 --- a/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Phonabets.swift +++ b/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Phonabets.swift @@ -8,20 +8,20 @@ import Foundation -extension Tekkon { +public extension Tekkon { // MARK: - Dynamic Constants and Basic Enums /// 定義注音符號的種類 - public enum PhoneType: Int { - case null = 0 // 假 - case consonant = 1 // 聲 - case semivowel = 2 // 介 - case vowel = 3 // 韻 - case intonation = 4 // 調 + enum PhoneType: Int { + case null = 0 // 假 + case consonant = 1 // 聲 + case semivowel = 2 // 介 + case vowel = 3 // 韻 + case intonation = 4 // 調 } /// 定義注音排列的類型 - public enum MandarinParser: Int { + enum MandarinParser: Int { case ofDachen = 0 case ofDachen26 = 1 case ofETen = 2 @@ -40,36 +40,36 @@ extension Tekkon { var name: String { switch self { - case .ofDachen: - return "Dachen" - case .ofDachen26: - return "Dachen26" - case .ofETen: - return "ETen" - case .ofHsu: - return "Hsu" - case .ofETen26: - return "ETen26" - case .ofIBM: - return "IBM" - case .ofMiTAC: - return "MiTAC" - case .ofFakeSeigyou: - return "FakeSeigyou" - case .ofSeigyou: - return "Seigyou" - case .ofStarlight: - return "Starlight" - case .ofHanyuPinyin: - return "HanyuPinyin" - case .ofSecondaryPinyin: - return "SecondaryPinyin" - case .ofYalePinyin: - return "YalePinyin" - case .ofHualuoPinyin: - return "HualuoPinyin" - case .ofUniversalPinyin: - return "UniversalPinyin" + case .ofDachen: + return "Dachen" + case .ofDachen26: + return "Dachen26" + case .ofETen: + return "ETen" + case .ofHsu: + return "Hsu" + case .ofETen26: + return "ETen26" + case .ofIBM: + return "IBM" + case .ofMiTAC: + return "MiTAC" + case .ofFakeSeigyou: + return "FakeSeigyou" + case .ofSeigyou: + return "Seigyou" + case .ofStarlight: + return "Starlight" + case .ofHanyuPinyin: + return "HanyuPinyin" + case .ofSecondaryPinyin: + return "SecondaryPinyin" + case .ofYalePinyin: + return "YalePinyin" + case .ofHualuoPinyin: + return "HualuoPinyin" + case .ofUniversalPinyin: + return "UniversalPinyin" } } } @@ -81,7 +81,7 @@ extension Tekkon { /// 如果遇到被設為多個字符、或者字符不對的情況的話,value 會被清空、PhoneType 會變成 null。 /// 賦值時最好直接重新 init 且一直用 let 來初期化 Phonabet。 /// 其實 value 對外只讀,對內的話另有 valueStorage 代為存儲內容。這樣比較安全一些。 - @frozen public struct Phonabet: Equatable, Hashable, ExpressibleByStringLiteral { + @frozen struct Phonabet: Equatable, Hashable, ExpressibleByStringLiteral { public var type: PhoneType = .null private var valueStorage = "" public var value: String { valueStorage } diff --git a/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_SyllableComposer.swift b/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_SyllableComposer.swift index a92c1328..558e2da6 100644 --- a/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_SyllableComposer.swift +++ b/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_SyllableComposer.swift @@ -8,7 +8,7 @@ import Foundation -extension Tekkon { +public extension Tekkon { // MARK: - Syllable Composer /// 注音並擊處理的對外介面以注拼槽(Syllable Composer)的形式存在。 @@ -17,7 +17,7 @@ extension Tekkon { /// /// 因為是 String Literal,所以初期化時可以藉由 @input 參數指定初期已經傳入的按鍵訊號。 /// 還可以在初期化時藉由 @arrange 參數來指定注音排列(預設為「.ofDachen」大千佈局)。 - @frozen public struct Composer: Equatable, Hashable, ExpressibleByStringLiteral { + @frozen struct Composer: Equatable, Hashable, ExpressibleByStringLiteral { /// 聲母。 public var consonant: Phonabet = "" @@ -53,12 +53,12 @@ extension Tekkon { /// - isTextBookStyle: 是否將輸出的注音/拼音結果轉成教科書排版格式。 public func getComposition(isHanyuPinyin: Bool = false, isTextBookStyle: Bool = false) -> String { switch isHanyuPinyin { - case false: // 注音輸出的場合 - let valReturnZhuyin = value.replacingOccurrences(of: " ", with: "") - return isTextBookStyle ? cnvPhonaToTextbookReading(target: valReturnZhuyin) : valReturnZhuyin - case true: // 拼音輸出的場合 - let valReturnPinyin = Tekkon.cnvPhonaToHanyuPinyin(targetJoined: value) - return isTextBookStyle ? Tekkon.cnvHanyuPinyinToTextbookStyle(targetJoined: valReturnPinyin) : valReturnPinyin + case false: // 注音輸出的場合 + let valReturnZhuyin = value.replacingOccurrences(of: " ", with: "") + return isTextBookStyle ? cnvPhonaToTextbookReading(target: valReturnZhuyin) : valReturnZhuyin + case true: // 拼音輸出的場合 + let valReturnPinyin = Tekkon.cnvPhonaToHanyuPinyin(targetJoined: value) + return isTextBookStyle ? Tekkon.cnvHanyuPinyinToTextbookStyle(targetJoined: valReturnPinyin) : valReturnPinyin } } @@ -67,27 +67,27 @@ extension Tekkon { /// - isHanyuPinyin: 是否將輸出結果轉成漢語拼音。 public func getInlineCompositionForDisplay(isHanyuPinyin: Bool = false) -> String { switch parser { - case .ofHanyuPinyin, .ofSecondaryPinyin, .ofYalePinyin, .ofHualuoPinyin, .ofUniversalPinyin: - var toneReturned = "" - switch intonation.value { - case " ": toneReturned = "1" - case "ˊ": toneReturned = "2" - case "ˇ": toneReturned = "3" - case "ˋ": toneReturned = "4" - case "˙": toneReturned = "5" - default: break - } - return romajiBuffer + toneReturned - default: return getComposition(isHanyuPinyin: isHanyuPinyin) + case .ofHanyuPinyin, .ofSecondaryPinyin, .ofYalePinyin, .ofHualuoPinyin, .ofUniversalPinyin: + var toneReturned = "" + switch intonation.value { + case " ": toneReturned = "1" + case "ˊ": toneReturned = "2" + case "ˇ": toneReturned = "3" + case "ˋ": toneReturned = "4" + case "˙": toneReturned = "5" + default: break + } + return romajiBuffer + toneReturned + default: return getComposition(isHanyuPinyin: isHanyuPinyin) } } /// 注拼槽內容是否為空。 public var isEmpty: Bool { switch parser { - case .ofHanyuPinyin, .ofSecondaryPinyin, .ofYalePinyin, .ofHualuoPinyin, .ofUniversalPinyin: - return intonation.isEmpty && romajiBuffer.isEmpty - default: return intonation.isEmpty && vowel.isEmpty && semivowel.isEmpty && consonant.isEmpty + case .ofHanyuPinyin, .ofSecondaryPinyin, .ofYalePinyin, .ofHualuoPinyin, .ofUniversalPinyin: + return intonation.isEmpty && romajiBuffer.isEmpty + default: return intonation.isEmpty && vowel.isEmpty && semivowel.isEmpty && consonant.isEmpty } } @@ -131,28 +131,28 @@ extension Tekkon { if let scalar = UnicodeScalar(inputKey) { let input = String(scalar) switch parser { - case .ofDachen: - return Tekkon.mapQwertyDachen[input] != nil - case .ofDachen26: - return Tekkon.mapDachenCP26StaticKeys[input] != nil - case .ofETen: - return Tekkon.mapQwertyETenTraditional[input] != nil - case .ofHsu: - return Tekkon.mapHsuStaticKeys[input] != nil - case .ofETen26: - return Tekkon.mapETen26StaticKeys[input] != nil - case .ofIBM: - return Tekkon.mapQwertyIBM[input] != nil - case .ofMiTAC: - return Tekkon.mapQwertyMiTAC[input] != nil - case .ofSeigyou: - return Tekkon.mapSeigyou[input] != nil - case .ofFakeSeigyou: - return Tekkon.mapFakeSeigyou[input] != nil - case .ofStarlight: - return Tekkon.mapStarlightStaticKeys[input] != nil - case .ofHanyuPinyin, .ofSecondaryPinyin, .ofYalePinyin, .ofHualuoPinyin, .ofUniversalPinyin: - return Tekkon.mapArayuruPinyin.contains(input) + case .ofDachen: + return Tekkon.mapQwertyDachen[input] != nil + case .ofDachen26: + return Tekkon.mapDachenCP26StaticKeys[input] != nil + case .ofETen: + return Tekkon.mapQwertyETenTraditional[input] != nil + case .ofHsu: + return Tekkon.mapHsuStaticKeys[input] != nil + case .ofETen26: + return Tekkon.mapETen26StaticKeys[input] != nil + case .ofIBM: + return Tekkon.mapQwertyIBM[input] != nil + case .ofMiTAC: + return Tekkon.mapQwertyMiTAC[input] != nil + case .ofSeigyou: + return Tekkon.mapSeigyou[input] != nil + case .ofFakeSeigyou: + return Tekkon.mapFakeSeigyou[input] != nil + case .ofStarlight: + return Tekkon.mapStarlightStaticKeys[input] != nil + case .ofHanyuPinyin, .ofSecondaryPinyin, .ofYalePinyin, .ofHualuoPinyin, .ofUniversalPinyin: + return Tekkon.mapArayuruPinyin.contains(input) } } return false @@ -171,21 +171,21 @@ extension Tekkon { /// - fromString: 傳入的 String 內容。 public mutating func receiveKey(fromString input: String = "") { switch parser { - case .ofHanyuPinyin, .ofSecondaryPinyin, .ofYalePinyin, .ofHualuoPinyin, .ofUniversalPinyin: - if mapArayuruPinyinIntonation.keys.contains(input) { - if let theTone = mapArayuruPinyinIntonation[input] { - intonation = Phonabet(theTone) - } - } else { - // 為了防止 romajiBuffer 越敲越長帶來算力負擔,這裡讓它在要溢出時自動丟掉最早輸入的音頭。 - if romajiBuffer.count > 5 { - romajiBuffer = String(romajiBuffer.dropFirst()) - } - let romajiBufferBackup = romajiBuffer + input - receiveSequence(romajiBufferBackup, isRomaji: true) - romajiBuffer = romajiBufferBackup + case .ofHanyuPinyin, .ofSecondaryPinyin, .ofYalePinyin, .ofHualuoPinyin, .ofUniversalPinyin: + if mapArayuruPinyinIntonation.keys.contains(input) { + if let theTone = mapArayuruPinyinIntonation[input] { + intonation = Phonabet(theTone) } - default: receiveKey(fromPhonabet: translate(key: input)) + } else { + // 為了防止 romajiBuffer 越敲越長帶來算力負擔,這裡讓它在要溢出時自動丟掉最早輸入的音頭。 + if romajiBuffer.count > 5 { + romajiBuffer = String(romajiBuffer.dropFirst()) + } + let romajiBufferBackup = romajiBuffer + input + receiveSequence(romajiBufferBackup, isRomaji: true) + romajiBuffer = romajiBufferBackup + } + default: receiveKey(fromPhonabet: translate(key: input)) } } @@ -209,47 +209,47 @@ extension Tekkon { var thePhone: Phonabet = .init(phonabet) if phonabetCombinationCorrectionEnabled { switch phonabet { - case "ㄧ", "ㄩ": - if vowel.value == "ㄜ" { vowel = "ㄝ" } - case "ㄜ": - if "ㄨ".contains(semivowel.value) { semivowel = "ㄩ" } - if "ㄧㄩ".contains(semivowel.value) { thePhone = "ㄝ" } - case "ㄝ": - if "ㄨ".contains(semivowel.value) { semivowel = "ㄩ" } - case "ㄛ", "ㄥ": - if phonabet == "ㄛ", semivowel.value == "ㄩ" { semivowel = "ㄨ" } - if "ㄅㄆㄇㄈ".contains(consonant.value), semivowel.value == "ㄨ" { semivowel.clear() } - case "ㄟ": - if "ㄋㄌ".contains(consonant.value), semivowel.value == "ㄨ" { semivowel.clear() } - case "ㄨ": - if "ㄅㄆㄇㄈ".contains(consonant.value), "ㄛㄥ".contains(vowel.value) { vowel.clear() } - if "ㄋㄌ".contains(consonant.value), "ㄟ".contains(vowel.value) { vowel.clear() } - if "ㄜ".contains(vowel.value) { vowel = "ㄝ" } - if "ㄝ".contains(vowel.value) { thePhone = "ㄩ" } - case "ㄅ", "ㄆ", "ㄇ", "ㄈ": - if ["ㄨㄛ", "ㄨㄥ"].contains(semivowel.value + vowel.value) { semivowel.clear() } - default: break + case "ㄧ", "ㄩ": + if vowel.value == "ㄜ" { vowel = "ㄝ" } + case "ㄜ": + if "ㄨ".contains(semivowel.value) { semivowel = "ㄩ" } + if "ㄧㄩ".contains(semivowel.value) { thePhone = "ㄝ" } + case "ㄝ": + if "ㄨ".contains(semivowel.value) { semivowel = "ㄩ" } + case "ㄛ", "ㄥ": + if phonabet == "ㄛ", semivowel.value == "ㄩ" { semivowel = "ㄨ" } + if "ㄅㄆㄇㄈ".contains(consonant.value), semivowel.value == "ㄨ" { semivowel.clear() } + case "ㄟ": + if "ㄋㄌ".contains(consonant.value), semivowel.value == "ㄨ" { semivowel.clear() } + case "ㄨ": + if "ㄅㄆㄇㄈ".contains(consonant.value), "ㄛㄥ".contains(vowel.value) { vowel.clear() } + if "ㄋㄌ".contains(consonant.value), "ㄟ".contains(vowel.value) { vowel.clear() } + if "ㄜ".contains(vowel.value) { vowel = "ㄝ" } + if "ㄝ".contains(vowel.value) { thePhone = "ㄩ" } + case "ㄅ", "ㄆ", "ㄇ", "ㄈ": + if ["ㄨㄛ", "ㄨㄥ"].contains(semivowel.value + vowel.value) { semivowel.clear() } + default: break } if [.vowel, .intonation].contains(thePhone.type), "ㄓㄔㄕㄗㄘㄙ".contains(consonant.value) { switch semivowel.value { - case "ㄧ": semivowel.clear() - case "ㄩ": - switch consonant { - case _ where "ㄓㄗ".contains(consonant.value): consonant = "ㄐ" - case _ where "ㄔㄘ".contains(consonant.value): consonant = "ㄑ" - case _ where "ㄕㄙ".contains(consonant.value): consonant = "ㄒ" - default: break - } + case "ㄧ": semivowel.clear() + case "ㄩ": + switch consonant { + case _ where "ㄓㄗ".contains(consonant.value): consonant = "ㄐ" + case _ where "ㄔㄘ".contains(consonant.value): consonant = "ㄑ" + case _ where "ㄕㄙ".contains(consonant.value): consonant = "ㄒ" default: break + } + default: break } } } switch thePhone.type { - case .consonant: consonant = thePhone - case .semivowel: semivowel = thePhone - case .vowel: vowel = thePhone - case .intonation: intonation = thePhone - default: break + case .consonant: consonant = thePhone + case .semivowel: semivowel = thePhone + case .vowel: vowel = thePhone + case .intonation: intonation = thePhone + default: break } updateRomajiBuffer() } @@ -263,37 +263,37 @@ extension Tekkon { clear() if isRomaji { switch parser { - case .ofHanyuPinyin: - if let dictResult = mapHanyuPinyin[givenSequence] { - for phonabet in dictResult { - receiveKey(fromPhonabet: String(phonabet)) - } + case .ofHanyuPinyin: + if let dictResult = mapHanyuPinyin[givenSequence] { + for phonabet in dictResult { + receiveKey(fromPhonabet: String(phonabet)) } - case .ofSecondaryPinyin: - if let dictResult = mapSecondaryPinyin[givenSequence] { - for phonabet in dictResult { - receiveKey(fromPhonabet: String(phonabet)) - } + } + case .ofSecondaryPinyin: + if let dictResult = mapSecondaryPinyin[givenSequence] { + for phonabet in dictResult { + receiveKey(fromPhonabet: String(phonabet)) } - case .ofYalePinyin: - if let dictResult = mapYalePinyin[givenSequence] { - for phonabet in dictResult { - receiveKey(fromPhonabet: String(phonabet)) - } + } + case .ofYalePinyin: + if let dictResult = mapYalePinyin[givenSequence] { + for phonabet in dictResult { + receiveKey(fromPhonabet: String(phonabet)) } - case .ofHualuoPinyin: - if let dictResult = mapHualuoPinyin[givenSequence] { - for phonabet in dictResult { - receiveKey(fromPhonabet: String(phonabet)) - } + } + case .ofHualuoPinyin: + if let dictResult = mapHualuoPinyin[givenSequence] { + for phonabet in dictResult { + receiveKey(fromPhonabet: String(phonabet)) } - case .ofUniversalPinyin: - if let dictResult = mapUniversalPinyin[givenSequence] { - for phonabet in dictResult { - receiveKey(fromPhonabet: String(phonabet)) - } + } + case .ofUniversalPinyin: + if let dictResult = mapUniversalPinyin[givenSequence] { + for phonabet in dictResult { + receiveKey(fromPhonabet: String(phonabet)) } - default: break + } + default: break } } else { for key in givenSequence { @@ -316,7 +316,7 @@ extension Tekkon { /// 基本上就是按順序從游標前方開始往後刪。 public mutating func doBackSpace() { if [.ofHanyuPinyin, .ofSecondaryPinyin, .ofYalePinyin, .ofHualuoPinyin, .ofUniversalPinyin].contains(parser), - !romajiBuffer.isEmpty + !romajiBuffer.isEmpty { if !intonation.isEmpty { intonation.clear() @@ -362,28 +362,28 @@ extension Tekkon { /// - key: 傳入的 String 訊號。 public mutating func translate(key: String = "") -> String { switch parser { - case .ofDachen: - return Tekkon.mapQwertyDachen[key] ?? "" - case .ofDachen26: - return handleDachen26(key: key) - case .ofETen: - return Tekkon.mapQwertyETenTraditional[key] ?? "" - case .ofHsu: - return handleHsu(key: key) - case .ofETen26: - return handleETen26(key: key) - case .ofIBM: - return Tekkon.mapQwertyIBM[key] ?? "" - case .ofMiTAC: - return Tekkon.mapQwertyMiTAC[key] ?? "" - case .ofSeigyou: - return Tekkon.mapSeigyou[key] ?? "" - case .ofFakeSeigyou: - return Tekkon.mapFakeSeigyou[key] ?? "" - case .ofStarlight: - return handleStarlight(key: key) - case .ofHanyuPinyin, .ofSecondaryPinyin, .ofYalePinyin, .ofHualuoPinyin, .ofUniversalPinyin: - break // 漢語拼音單獨用另外的函式處理 + case .ofDachen: + return Tekkon.mapQwertyDachen[key] ?? "" + case .ofDachen26: + return handleDachen26(key: key) + case .ofETen: + return Tekkon.mapQwertyETenTraditional[key] ?? "" + case .ofHsu: + return handleHsu(key: key) + case .ofETen26: + return handleETen26(key: key) + case .ofIBM: + return Tekkon.mapQwertyIBM[key] ?? "" + case .ofMiTAC: + return Tekkon.mapQwertyMiTAC[key] ?? "" + case .ofSeigyou: + return Tekkon.mapSeigyou[key] ?? "" + case .ofFakeSeigyou: + return Tekkon.mapFakeSeigyou[key] ?? "" + case .ofStarlight: + return handleStarlight(key: key) + case .ofHanyuPinyin, .ofSecondaryPinyin, .ofYalePinyin, .ofHualuoPinyin, .ofUniversalPinyin: + break // 漢語拼音單獨用另外的函式處理 } return "" } @@ -393,45 +393,45 @@ extension Tekkon { public mutating func commonFixWhenHandlingDynamicArrangeInputs(target incomingPhonabet: Phonabet) { // 處理特殊情形。 switch incomingPhonabet.type { - case .semivowel: - switch consonant { - case "ㄍ": - switch incomingPhonabet { - case "ㄧ": consonant = "ㄑ" // ㄑㄧ - case "ㄨ": consonant = "ㄍ" // ㄍㄨ - case "ㄩ": consonant = "ㄑ" // ㄑㄩ - default: break - } - case "ㄓ": - switch incomingPhonabet { - case "ㄧ": consonant = "ㄐ" // ㄐㄧ - case "ㄨ": consonant = "ㄓ" // ㄓㄨ - case "ㄩ": consonant = "ㄐ" // ㄐㄩ - default: break - } - case "ㄔ": - switch incomingPhonabet { - case "ㄧ": consonant = "ㄑ" // ㄐㄧ - case "ㄨ": consonant = "ㄔ" // ㄓㄨ - case "ㄩ": consonant = "ㄑ" // ㄐㄩ - default: break - } - case "ㄕ": - switch incomingPhonabet { - case "ㄧ": consonant = "ㄒ" // ㄒㄧ - case "ㄨ": consonant = "ㄕ" // ㄕㄨ - case "ㄩ": consonant = "ㄒ" // ㄒㄩ - default: break - } - default: break + case .semivowel: + switch consonant { + case "ㄍ": + switch incomingPhonabet { + case "ㄧ": consonant = "ㄑ" // ㄑㄧ + case "ㄨ": consonant = "ㄍ" // ㄍㄨ + case "ㄩ": consonant = "ㄑ" // ㄑㄩ + default: break } - case .vowel: - if semivowel.isEmpty { - consonant.selfReplace("ㄐ", "ㄓ") - consonant.selfReplace("ㄑ", "ㄔ") - consonant.selfReplace("ㄒ", "ㄕ") + case "ㄓ": + switch incomingPhonabet { + case "ㄧ": consonant = "ㄐ" // ㄐㄧ + case "ㄨ": consonant = "ㄓ" // ㄓㄨ + case "ㄩ": consonant = "ㄐ" // ㄐㄩ + default: break + } + case "ㄔ": + switch incomingPhonabet { + case "ㄧ": consonant = "ㄑ" // ㄐㄧ + case "ㄨ": consonant = "ㄔ" // ㄓㄨ + case "ㄩ": consonant = "ㄑ" // ㄐㄩ + default: break + } + case "ㄕ": + switch incomingPhonabet { + case "ㄧ": consonant = "ㄒ" // ㄒㄧ + case "ㄨ": consonant = "ㄕ" // ㄕㄨ + case "ㄩ": consonant = "ㄒ" // ㄒㄩ + default: break } default: break + } + case .vowel: + if semivowel.isEmpty { + consonant.selfReplace("ㄐ", "ㄓ") + consonant.selfReplace("ㄑ", "ㄔ") + consonant.selfReplace("ㄒ", "ㄕ") + } + default: break } } @@ -445,35 +445,35 @@ extension Tekkon { let incomingPhonabet = Phonabet(strReturn) switch key { - case "d": if !isPronouncable { consonant = "ㄉ" } else { intonation = "˙" } - case "f": if !isPronouncable { consonant = "ㄈ" } else { intonation = "ˊ" } - case "j": if !isPronouncable { consonant = "ㄖ" } else { intonation = "ˇ" } - case "k": if !isPronouncable { consonant = "ㄎ" } else { intonation = "ˋ" } - case "h": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄏ" } else { vowel = "ㄦ" } - case "l": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄌ" } else { vowel = "ㄥ" } - case "m": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄇ" } else { vowel = "ㄢ" } - case "n": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄋ" } else { vowel = "ㄣ" } - case "q": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄗ" } else { vowel = "ㄟ" } - case "t": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄊ" } else { vowel = "ㄤ" } - case "w": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄘ" } else { vowel = "ㄝ" } - case "p": - if consonant.isEmpty, semivowel.isEmpty { - consonant = "ㄆ" - } else if consonant.isEmpty, semivowel == "ㄧ" { - vowel = "ㄡ" - } else if consonant.isEmpty { - vowel = "ㄆ" - } else { - vowel = "ㄡ" - } - default: break + case "d": if !isPronouncable { consonant = "ㄉ" } else { intonation = "˙" } + case "f": if !isPronouncable { consonant = "ㄈ" } else { intonation = "ˊ" } + case "j": if !isPronouncable { consonant = "ㄖ" } else { intonation = "ˇ" } + case "k": if !isPronouncable { consonant = "ㄎ" } else { intonation = "ˋ" } + case "h": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄏ" } else { vowel = "ㄦ" } + case "l": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄌ" } else { vowel = "ㄥ" } + case "m": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄇ" } else { vowel = "ㄢ" } + case "n": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄋ" } else { vowel = "ㄣ" } + case "q": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄗ" } else { vowel = "ㄟ" } + case "t": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄊ" } else { vowel = "ㄤ" } + case "w": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄘ" } else { vowel = "ㄝ" } + case "p": + if consonant.isEmpty, semivowel.isEmpty { + consonant = "ㄆ" + } else if consonant.isEmpty, semivowel == "ㄧ" { + vowel = "ㄡ" + } else if consonant.isEmpty { + vowel = "ㄆ" + } else { + vowel = "ㄡ" + } + default: break } // 處理特殊情形。 commonFixWhenHandlingDynamicArrangeInputs(target: incomingPhonabet) if "dfjk ".contains(key), - !consonant.isEmpty, semivowel.isEmpty, vowel.isEmpty + !consonant.isEmpty, semivowel.isEmpty, vowel.isEmpty { consonant.selfReplace("ㄆ", "ㄡ") consonant.selfReplace("ㄇ", "ㄢ") @@ -508,28 +508,28 @@ extension Tekkon { } switch key { - case "d": if isPronouncable { intonation = "ˊ" } else { consonant = "ㄉ" } - case "f": if isPronouncable { intonation = "ˇ" } else { consonant = "ㄈ" } - case "s": if isPronouncable { intonation = "˙" } else { consonant = "ㄙ" } - case "j": if isPronouncable { intonation = "ˋ" } else { consonant = "ㄓ" } - case "a": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄘ" } else { vowel = "ㄟ" } - case "v": if semivowel.isEmpty { consonant = "ㄔ" } else { consonant = "ㄑ" } - case "c": if semivowel.isEmpty { consonant = "ㄕ" } else { consonant = "ㄒ" } - case "e": if semivowel.isEmpty { semivowel = "ㄧ" } else { vowel = "ㄝ" } - case "g": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄍ" } else { vowel = "ㄜ" } - case "h": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄏ" } else { vowel = "ㄛ" } - case "k": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄎ" } else { vowel = "ㄤ" } - case "m": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄇ" } else { vowel = "ㄢ" } - case "n": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄋ" } else { vowel = "ㄣ" } - case "l": - if value.isEmpty, !consonant.isEmpty, !semivowel.isEmpty { - vowel = "ㄦ" - } else if consonant.isEmpty, semivowel.isEmpty { - consonant = "ㄌ" - } else { - vowel = "ㄥ" - } - default: break + case "d": if isPronouncable { intonation = "ˊ" } else { consonant = "ㄉ" } + case "f": if isPronouncable { intonation = "ˇ" } else { consonant = "ㄈ" } + case "s": if isPronouncable { intonation = "˙" } else { consonant = "ㄙ" } + case "j": if isPronouncable { intonation = "ˋ" } else { consonant = "ㄓ" } + case "a": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄘ" } else { vowel = "ㄟ" } + case "v": if semivowel.isEmpty { consonant = "ㄔ" } else { consonant = "ㄑ" } + case "c": if semivowel.isEmpty { consonant = "ㄕ" } else { consonant = "ㄒ" } + case "e": if semivowel.isEmpty { semivowel = "ㄧ" } else { vowel = "ㄝ" } + case "g": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄍ" } else { vowel = "ㄜ" } + case "h": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄏ" } else { vowel = "ㄛ" } + case "k": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄎ" } else { vowel = "ㄤ" } + case "m": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄇ" } else { vowel = "ㄢ" } + case "n": if consonant.isEmpty, semivowel.isEmpty { consonant = "ㄋ" } else { vowel = "ㄣ" } + case "l": + if value.isEmpty, !consonant.isEmpty, !semivowel.isEmpty { + vowel = "ㄦ" + } else if consonant.isEmpty, semivowel.isEmpty { + consonant = "ㄌ" + } else { + vowel = "ㄥ" + } + default: break } // 處理特殊情形。 @@ -581,15 +581,15 @@ extension Tekkon { var strReturn = Tekkon.mapStarlightStaticKeys[key] ?? "" let incomingPhonabet = Phonabet(strReturn) switch key { - case "e": return "ㄧㄩ".contains(semivowel.value) ? "ㄝ" : "ㄜ" - case "f": return vowel == "ㄠ" || !isPronouncable ? "ㄈ" : "ㄠ" - case "g": return vowel == "ㄥ" || !isPronouncable ? "ㄍ" : "ㄥ" - case "k": return vowel == "ㄤ" || !isPronouncable ? "ㄎ" : "ㄤ" - case "l": return vowel == "ㄦ" || !isPronouncable ? "ㄌ" : "ㄦ" - case "m": return vowel == "ㄢ" || !isPronouncable ? "ㄇ" : "ㄢ" - case "n": return vowel == "ㄣ" || !isPronouncable ? "ㄋ" : "ㄣ" - case "t": return vowel == "ㄟ" || !isPronouncable ? "ㄊ" : "ㄟ" - default: break + case "e": return "ㄧㄩ".contains(semivowel.value) ? "ㄝ" : "ㄜ" + case "f": return vowel == "ㄠ" || !isPronouncable ? "ㄈ" : "ㄠ" + case "g": return vowel == "ㄥ" || !isPronouncable ? "ㄍ" : "ㄥ" + case "k": return vowel == "ㄤ" || !isPronouncable ? "ㄎ" : "ㄤ" + case "l": return vowel == "ㄦ" || !isPronouncable ? "ㄌ" : "ㄦ" + case "m": return vowel == "ㄢ" || !isPronouncable ? "ㄇ" : "ㄢ" + case "n": return vowel == "ㄣ" || !isPronouncable ? "ㄋ" : "ㄣ" + case "t": return vowel == "ㄟ" || !isPronouncable ? "ㄊ" : "ㄟ" + default: break } // 處理特殊情形。 @@ -623,52 +623,52 @@ extension Tekkon { var strReturn = Tekkon.mapDachenCP26StaticKeys[key] ?? "" switch key { - case "e": if isPronouncable { intonation = "ˊ" } else { consonant = "ㄍ" } - case "r": if isPronouncable { intonation = "ˇ" } else { consonant = "ㄐ" } - case "d": if isPronouncable { intonation = "ˋ" } else { consonant = "ㄎ" } - case "y": if isPronouncable { intonation = "˙" } else { consonant = "ㄗ" } - case "b": if !consonant.isEmpty || !semivowel.isEmpty { vowel = "ㄝ" } else { consonant = "ㄖ" } - case "i": if vowel.isEmpty || vowel == "ㄞ" { vowel = "ㄛ" } else { vowel = "ㄞ" } - case "l": if vowel.isEmpty || vowel == "ㄤ" { vowel = "ㄠ" } else { vowel = "ㄤ" } - case "n": - if !consonant.isEmpty || !semivowel.isEmpty { - if consonant == "ㄙ", semivowel.isEmpty, vowel.isEmpty { consonant.clear() } - vowel = "ㄥ" - } else { - consonant = "ㄙ" - } - case "o": if vowel.isEmpty || vowel == "ㄢ" { vowel = "ㄟ" } else { vowel = "ㄢ" } - case "p": if vowel.isEmpty || vowel == "ㄦ" { vowel = "ㄣ" } else { vowel = "ㄦ" } - case "q": if consonant.isEmpty || consonant == "ㄅ" { consonant = "ㄆ" } else { consonant = "ㄅ" } - case "t": if consonant.isEmpty || consonant == "ㄓ" { consonant = "ㄔ" } else { consonant = "ㄓ" } - case "w": if consonant.isEmpty || consonant == "ㄉ" { consonant = "ㄊ" } else { consonant = "ㄉ" } - case "m": - if semivowel == "ㄩ", vowel != "ㄡ" { - semivowel.clear() - vowel = "ㄡ" - } else if semivowel != "ㄩ", vowel == "ㄡ" { - semivowel = "ㄩ" - vowel.clear() - } else if !semivowel.isEmpty { - vowel = "ㄡ" - } else { - receiveKey(fromPhonabet: "ㄐㄑㄒ".contains(consonant.value) ? "ㄩ" : "ㄡ") - } - case "u": - if semivowel == "ㄧ", vowel != "ㄚ" { - semivowel.clear() - vowel = "ㄚ" - } else if semivowel != "ㄧ", vowel == "ㄚ" { - semivowel = "ㄧ" - } else if semivowel == "ㄧ", vowel == "ㄚ" { - semivowel.clear() - vowel.clear() - } else if !semivowel.isEmpty { - vowel = "ㄚ" - } else { - semivowel = "ㄧ" - } - default: break + case "e": if isPronouncable { intonation = "ˊ" } else { consonant = "ㄍ" } + case "r": if isPronouncable { intonation = "ˇ" } else { consonant = "ㄐ" } + case "d": if isPronouncable { intonation = "ˋ" } else { consonant = "ㄎ" } + case "y": if isPronouncable { intonation = "˙" } else { consonant = "ㄗ" } + case "b": if !consonant.isEmpty || !semivowel.isEmpty { vowel = "ㄝ" } else { consonant = "ㄖ" } + case "i": if vowel.isEmpty || vowel == "ㄞ" { vowel = "ㄛ" } else { vowel = "ㄞ" } + case "l": if vowel.isEmpty || vowel == "ㄤ" { vowel = "ㄠ" } else { vowel = "ㄤ" } + case "n": + if !consonant.isEmpty || !semivowel.isEmpty { + if consonant == "ㄙ", semivowel.isEmpty, vowel.isEmpty { consonant.clear() } + vowel = "ㄥ" + } else { + consonant = "ㄙ" + } + case "o": if vowel.isEmpty || vowel == "ㄢ" { vowel = "ㄟ" } else { vowel = "ㄢ" } + case "p": if vowel.isEmpty || vowel == "ㄦ" { vowel = "ㄣ" } else { vowel = "ㄦ" } + case "q": if consonant.isEmpty || consonant == "ㄅ" { consonant = "ㄆ" } else { consonant = "ㄅ" } + case "t": if consonant.isEmpty || consonant == "ㄓ" { consonant = "ㄔ" } else { consonant = "ㄓ" } + case "w": if consonant.isEmpty || consonant == "ㄉ" { consonant = "ㄊ" } else { consonant = "ㄉ" } + case "m": + if semivowel == "ㄩ", vowel != "ㄡ" { + semivowel.clear() + vowel = "ㄡ" + } else if semivowel != "ㄩ", vowel == "ㄡ" { + semivowel = "ㄩ" + vowel.clear() + } else if !semivowel.isEmpty { + vowel = "ㄡ" + } else { + receiveKey(fromPhonabet: "ㄐㄑㄒ".contains(consonant.value) ? "ㄩ" : "ㄡ") + } + case "u": + if semivowel == "ㄧ", vowel != "ㄚ" { + semivowel.clear() + vowel = "ㄚ" + } else if semivowel != "ㄧ", vowel == "ㄚ" { + semivowel = "ㄧ" + } else if semivowel == "ㄧ", vowel == "ㄚ" { + semivowel.clear() + vowel.clear() + } else if !semivowel.isEmpty { + vowel = "ㄚ" + } else { + semivowel = "ㄧ" + } + default: break } // 這些按鍵在上文處理過了,就不要再回傳了。 diff --git a/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Utilities.swift b/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Utilities.swift index f106982d..262530a7 100644 --- a/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Utilities.swift +++ b/Packages/vChewing_Tekkon/Sources/Tekkon/Tekkon_Utilities.swift @@ -8,13 +8,13 @@ import Foundation -extension Tekkon { +public extension Tekkon { // MARK: - Phonabet to Hanyu-Pinyin Conversion Processing /// 注音轉拼音,要求陰平必須是空格。 /// - Parameters: /// - targetJoined: 傳入的 String 對象物件。 - public static func cnvPhonaToHanyuPinyin(targetJoined: String) -> String { + static func cnvPhonaToHanyuPinyin(targetJoined: String) -> String { var targetConverted = targetJoined for pair in arrPhonaToHanyuPinyin { targetConverted = targetConverted.replacingOccurrences(of: pair[0], with: pair[1]) @@ -25,7 +25,7 @@ extension Tekkon { /// 漢語拼音數字標調式轉漢語拼音教科書格式,要求陰平必須是數字 1。 /// - Parameters: /// - target: 傳入的 String 對象物件。 - public static func cnvHanyuPinyinToTextbookStyle(targetJoined: String) -> String { + static func cnvHanyuPinyinToTextbookStyle(targetJoined: String) -> String { var targetConverted = targetJoined for pair in arrHanyuPinyinTextbookStyleConversionTable { targetConverted = targetConverted.replacingOccurrences(of: pair[0], with: pair[1]) @@ -37,7 +37,7 @@ extension Tekkon { /// - Parameters: /// - target: 要拿來做轉換處理的讀音。 /// - Returns: 經過轉換處理的讀音。 - public static func cnvPhonaToTextbookReading(target: String) -> String { + static func cnvPhonaToTextbookReading(target: String) -> String { var newString = target if String(target.reversed()[0]) == "˙" { newString = String(target.dropLast()) @@ -50,7 +50,7 @@ extension Tekkon { /// - Parameters: /// - target: 要拿來做轉換處理的讀音。 /// - Returns: 經過轉換處理的讀音。 - public static func restoreToneOneInPhona( + static func restoreToneOneInPhona( target: String ) -> String { var newNeta = target @@ -63,7 +63,7 @@ extension Tekkon { /// - targetJoined: 要轉換的漢語拼音內容,要求必須帶有 12345 數字標調。 /// - newToneOne: 對陰平指定新的標記。預設情況下該標記為空字串。 /// - Returns: 轉換結果。 - public static func cnvHanyuPinyinToPhona(targetJoined: String, newToneOne: String = "") -> String { + static func cnvHanyuPinyinToPhona(targetJoined: String, newToneOne: String = "") -> String { /// 如果當前內容有任何除了半形英數內容以外的內容的話,就直接放棄轉換。 if targetJoined.contains("_") || !targetJoined.isNotPureAlphanumerical { return targetJoined } var result = targetJoined @@ -80,8 +80,8 @@ extension Tekkon { } /// 檢測字串是否包含半形英數內容 -extension String { - fileprivate var isNotPureAlphanumerical: Bool { +private extension String { + var isNotPureAlphanumerical: Bool { let regex = ".*[^A-Za-z0-9].*" let testString = NSPredicate(format: "SELF MATCHES %@", regex) return testString.evaluate(with: self) diff --git a/Packages/vChewing_Tekkon/Tests/TekkonTests/BasicTests.swift b/Packages/vChewing_Tekkon/Tests/TekkonTests/BasicTests.swift index a63ecc6d..81b4a14f 100644 --- a/Packages/vChewing_Tekkon/Tests/TekkonTests/BasicTests.swift +++ b/Packages/vChewing_Tekkon/Tests/TekkonTests/BasicTests.swift @@ -55,20 +55,20 @@ final class TekkonTestsBasic: XCTestCase { var toneMarkerIndicator = true // Test Key Receiving - composer.receiveKey(fromCharCode: 0x0032) // 2, ㄉ - composer.receiveKey(fromString: "j") // ㄨ - composer.receiveKey(fromString: "u") // ㄧ - composer.receiveKey(fromString: "l") // ㄠ + composer.receiveKey(fromCharCode: 0x0032) // 2, ㄉ + composer.receiveKey(fromString: "j") // ㄨ + composer.receiveKey(fromString: "u") // ㄧ + composer.receiveKey(fromString: "l") // ㄠ // Testing missing tone markers toneMarkerIndicator = composer.hasIntonation() XCTAssert(!toneMarkerIndicator) - composer.receiveKey(fromString: "3") // 上聲 + composer.receiveKey(fromString: "3") // 上聲 XCTAssertEqual(composer.value, "ㄉㄧㄠˇ") composer.doBackSpace() - composer.receiveKey(fromString: " ") // 陰平 - XCTAssertEqual(composer.value, "ㄉㄧㄠ ") // 這裡回傳的結果的陰平是空格 + composer.receiveKey(fromString: " ") // 陰平 + XCTAssertEqual(composer.value, "ㄉㄧㄠ ") // 這裡回傳的結果的陰平是空格 // Test Getting Displayed Composition XCTAssertEqual(composer.getComposition(), "ㄉㄧㄠ") @@ -77,7 +77,7 @@ final class TekkonTestsBasic: XCTestCase { XCTAssertEqual(composer.getInlineCompositionForDisplay(isHanyuPinyin: true), "diao1") // Test Tone 5 - composer.receiveKey(fromString: "7") // 輕聲 + composer.receiveKey(fromString: "7") // 輕聲 XCTAssertEqual(composer.getComposition(), "ㄉㄧㄠ˙") XCTAssertEqual(composer.getComposition(isTextBookStyle: true), "˙ㄉㄧㄠ") @@ -91,7 +91,7 @@ final class TekkonTestsBasic: XCTestCase { // Testing having only tone markers composer.clear() - composer.receiveKey(fromString: "3") // 上聲 + composer.receiveKey(fromString: "3") // 上聲 toneMarkerIndicator = composer.hasIntonation(withNothingElse: true) XCTAssert(toneMarkerIndicator) @@ -223,7 +223,7 @@ final class TekkonTestsPinyin: XCTestCase { var toneMarkerIndicator = true // Test Key Receiving - composer.receiveKey(fromCharCode: 100) // d + composer.receiveKey(fromCharCode: 100) // d composer.receiveKey(fromString: "i") composer.receiveKey(fromString: "a") composer.receiveKey(fromString: "o") @@ -232,11 +232,11 @@ final class TekkonTestsPinyin: XCTestCase { toneMarkerIndicator = composer.hasIntonation() XCTAssert(!toneMarkerIndicator) - composer.receiveKey(fromString: "3") // 上聲 + composer.receiveKey(fromString: "3") // 上聲 XCTAssertEqual(composer.value, "ㄉㄧㄠˇ") composer.doBackSpace() - composer.receiveKey(fromString: " ") // 陰平 - XCTAssertEqual(composer.value, "ㄉㄧㄠ ") // 這裡回傳的結果的陰平是空格 + composer.receiveKey(fromString: " ") // 陰平 + XCTAssertEqual(composer.value, "ㄉㄧㄠ ") // 這裡回傳的結果的陰平是空格 // Test Getting Displayed Composition XCTAssertEqual(composer.getComposition(), "ㄉㄧㄠ") @@ -245,7 +245,7 @@ final class TekkonTestsPinyin: XCTestCase { XCTAssertEqual(composer.getInlineCompositionForDisplay(isHanyuPinyin: true), "diao1") // Test Tone 5 - composer.receiveKey(fromString: "7") // 輕聲 + composer.receiveKey(fromString: "7") // 輕聲 XCTAssertEqual(composer.getComposition(), "ㄉㄧㄠ˙") XCTAssertEqual(composer.getComposition(isTextBookStyle: true), "˙ㄉㄧㄠ") @@ -259,7 +259,7 @@ final class TekkonTestsPinyin: XCTestCase { // Testing having only tone markers composer.clear() - composer.receiveKey(fromString: "3") // 上聲 + composer.receiveKey(fromString: "3") // 上聲 toneMarkerIndicator = composer.hasIntonation(withNothingElse: true) XCTAssert(toneMarkerIndicator) } @@ -269,7 +269,7 @@ final class TekkonTestsPinyin: XCTestCase { var toneMarkerIndicator = true // Test Key Receiving - composer.receiveKey(fromCharCode: 99) // c + composer.receiveKey(fromCharCode: 99) // c composer.receiveKey(fromString: "h") composer.receiveKey(fromString: "i") composer.receiveKey(fromString: "u") @@ -280,11 +280,11 @@ final class TekkonTestsPinyin: XCTestCase { toneMarkerIndicator = composer.hasIntonation() XCTAssert(!toneMarkerIndicator) - composer.receiveKey(fromString: "2") // 陽平 + composer.receiveKey(fromString: "2") // 陽平 XCTAssertEqual(composer.value, "ㄑㄩㄥˊ") composer.doBackSpace() - composer.receiveKey(fromString: " ") // 陰平 - XCTAssertEqual(composer.value, "ㄑㄩㄥ ") // 這裡回傳的結果的陰平是空格 + composer.receiveKey(fromString: " ") // 陰平 + XCTAssertEqual(composer.value, "ㄑㄩㄥ ") // 這裡回傳的結果的陰平是空格 // Test Getting Displayed Composition XCTAssertEqual(composer.getComposition(), "ㄑㄩㄥ") @@ -293,7 +293,7 @@ final class TekkonTestsPinyin: XCTestCase { XCTAssertEqual(composer.getInlineCompositionForDisplay(isHanyuPinyin: true), "chiung1") // Test Tone 5 - composer.receiveKey(fromString: "7") // 輕聲 + composer.receiveKey(fromString: "7") // 輕聲 XCTAssertEqual(composer.getComposition(), "ㄑㄩㄥ˙") XCTAssertEqual(composer.getComposition(isTextBookStyle: true), "˙ㄑㄩㄥ") @@ -307,7 +307,7 @@ final class TekkonTestsPinyin: XCTestCase { // Testing having only tone markers composer.clear() - composer.receiveKey(fromString: "3") // 上聲 + composer.receiveKey(fromString: "3") // 上聲 toneMarkerIndicator = composer.hasIntonation(withNothingElse: true) XCTAssert(toneMarkerIndicator) } @@ -317,7 +317,7 @@ final class TekkonTestsPinyin: XCTestCase { var toneMarkerIndicator = true // Test Key Receiving - composer.receiveKey(fromCharCode: 99) // c + composer.receiveKey(fromCharCode: 99) // c composer.receiveKey(fromString: "h") composer.receiveKey(fromString: "y") composer.receiveKey(fromString: "u") @@ -328,11 +328,11 @@ final class TekkonTestsPinyin: XCTestCase { toneMarkerIndicator = composer.hasIntonation() XCTAssert(!toneMarkerIndicator) - composer.receiveKey(fromString: "2") // 陽平 + composer.receiveKey(fromString: "2") // 陽平 XCTAssertEqual(composer.value, "ㄑㄩㄥˊ") composer.doBackSpace() - composer.receiveKey(fromString: " ") // 陰平 - XCTAssertEqual(composer.value, "ㄑㄩㄥ ") // 這裡回傳的結果的陰平是空格 + composer.receiveKey(fromString: " ") // 陰平 + XCTAssertEqual(composer.value, "ㄑㄩㄥ ") // 這裡回傳的結果的陰平是空格 // Test Getting Displayed Composition XCTAssertEqual(composer.getComposition(), "ㄑㄩㄥ") @@ -341,7 +341,7 @@ final class TekkonTestsPinyin: XCTestCase { XCTAssertEqual(composer.getInlineCompositionForDisplay(isHanyuPinyin: true), "chyung1") // Test Tone 5 - composer.receiveKey(fromString: "7") // 輕聲 + composer.receiveKey(fromString: "7") // 輕聲 XCTAssertEqual(composer.getComposition(), "ㄑㄩㄥ˙") XCTAssertEqual(composer.getComposition(isTextBookStyle: true), "˙ㄑㄩㄥ") @@ -355,7 +355,7 @@ final class TekkonTestsPinyin: XCTestCase { // Testing having only tone markers composer.clear() - composer.receiveKey(fromString: "3") // 上聲 + composer.receiveKey(fromString: "3") // 上聲 toneMarkerIndicator = composer.hasIntonation(withNothingElse: true) XCTAssert(toneMarkerIndicator) } @@ -365,7 +365,7 @@ final class TekkonTestsPinyin: XCTestCase { var toneMarkerIndicator = true // Test Key Receiving - composer.receiveKey(fromCharCode: 99) // c + composer.receiveKey(fromCharCode: 99) // c composer.receiveKey(fromString: "h") composer.receiveKey(fromString: "y") composer.receiveKey(fromString: "o") @@ -376,11 +376,11 @@ final class TekkonTestsPinyin: XCTestCase { toneMarkerIndicator = composer.hasIntonation() XCTAssert(!toneMarkerIndicator) - composer.receiveKey(fromString: "2") // 陽平 + composer.receiveKey(fromString: "2") // 陽平 XCTAssertEqual(composer.value, "ㄑㄩㄥˊ") composer.doBackSpace() - composer.receiveKey(fromString: " ") // 陰平 - XCTAssertEqual(composer.value, "ㄑㄩㄥ ") // 這裡回傳的結果的陰平是空格 + composer.receiveKey(fromString: " ") // 陰平 + XCTAssertEqual(composer.value, "ㄑㄩㄥ ") // 這裡回傳的結果的陰平是空格 // Test Getting Displayed Composition XCTAssertEqual(composer.getComposition(), "ㄑㄩㄥ") @@ -389,7 +389,7 @@ final class TekkonTestsPinyin: XCTestCase { XCTAssertEqual(composer.getInlineCompositionForDisplay(isHanyuPinyin: true), "chyong1") // Test Tone 5 - composer.receiveKey(fromString: "7") // 輕聲 + composer.receiveKey(fromString: "7") // 輕聲 XCTAssertEqual(composer.getComposition(), "ㄑㄩㄥ˙") XCTAssertEqual(composer.getComposition(isTextBookStyle: true), "˙ㄑㄩㄥ") @@ -403,7 +403,7 @@ final class TekkonTestsPinyin: XCTestCase { // Testing having only tone markers composer.clear() - composer.receiveKey(fromString: "3") // 上聲 + composer.receiveKey(fromString: "3") // 上聲 toneMarkerIndicator = composer.hasIntonation(withNothingElse: true) XCTAssert(toneMarkerIndicator) } @@ -413,7 +413,7 @@ final class TekkonTestsPinyin: XCTestCase { var toneMarkerIndicator = true // Test Key Receiving - composer.receiveKey(fromCharCode: 99) // c + composer.receiveKey(fromCharCode: 99) // c composer.receiveKey(fromString: "y") composer.receiveKey(fromString: "o") composer.receiveKey(fromString: "n") @@ -423,11 +423,11 @@ final class TekkonTestsPinyin: XCTestCase { toneMarkerIndicator = composer.hasIntonation() XCTAssert(!toneMarkerIndicator) - composer.receiveKey(fromString: "2") // 陽平 + composer.receiveKey(fromString: "2") // 陽平 XCTAssertEqual(composer.value, "ㄑㄩㄥˊ") composer.doBackSpace() - composer.receiveKey(fromString: " ") // 陰平 - XCTAssertEqual(composer.value, "ㄑㄩㄥ ") // 這裡回傳的結果的陰平是空格 + composer.receiveKey(fromString: " ") // 陰平 + XCTAssertEqual(composer.value, "ㄑㄩㄥ ") // 這裡回傳的結果的陰平是空格 // Test Getting Displayed Composition XCTAssertEqual(composer.getComposition(), "ㄑㄩㄥ") @@ -436,7 +436,7 @@ final class TekkonTestsPinyin: XCTestCase { XCTAssertEqual(composer.getInlineCompositionForDisplay(isHanyuPinyin: true), "cyong1") // Test Tone 5 - composer.receiveKey(fromString: "7") // 輕聲 + composer.receiveKey(fromString: "7") // 輕聲 XCTAssertEqual(composer.getComposition(), "ㄑㄩㄥ˙") XCTAssertEqual(composer.getComposition(isTextBookStyle: true), "˙ㄑㄩㄥ") @@ -450,7 +450,7 @@ final class TekkonTestsPinyin: XCTestCase { // Testing having only tone markers composer.clear() - composer.receiveKey(fromString: "3") // 上聲 + composer.receiveKey(fromString: "3") // 上聲 toneMarkerIndicator = composer.hasIntonation(withNothingElse: true) XCTAssert(toneMarkerIndicator) } diff --git a/Packages/vChewing_Tekkon/makefile b/Packages/vChewing_Tekkon/makefile index 80f07f65..db02b56a 100644 --- a/Packages/vChewing_Tekkon/makefile +++ b/Packages/vChewing_Tekkon/makefile @@ -1,6 +1,7 @@ -.PHONY: format +.PHONY: lint format format: - swiftformat ./ --swiftversion 5.5 - @git ls-files --exclude-standard | grep -E '\.swift$$' | xargs swift-format format --in-place --configuration ./.clang-format-swift.json --parallel - @git ls-files --exclude-standard | grep -E '\.swift$$' | xargs swift-format lint --configuration ./.clang-format-swift.json --parallel + @swiftformat --swiftversion 5.5 --indent 2 ./ + +lint: + @git ls-files --exclude-standard | grep -E '\.swift$$' | swiftlint --fix --autocorrect \ No newline at end of file diff --git a/Packages/vChewing_TooltipUI/Package.swift b/Packages/vChewing_TooltipUI/Package.swift index 6dd58c26..5a736fd9 100644 --- a/Packages/vChewing_TooltipUI/Package.swift +++ b/Packages/vChewing_TooltipUI/Package.swift @@ -4,13 +4,13 @@ import PackageDescription let package = Package( name: "TooltipUI", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "TooltipUI", targets: ["TooltipUI"] - ) + ), ], dependencies: [ .package(path: "../Fuziki_NSAttributedTextView"), @@ -25,6 +25,6 @@ let package = Package( .product(name: "CocoaExtension", package: "vChewing_CocoaExtension"), .product(name: "Shared", package: "vChewing_Shared"), ] - ) + ), ] ) diff --git a/Packages/vChewing_TooltipUI/Sources/TooltipUI/TooltipUI.swift b/Packages/vChewing_TooltipUI/Sources/TooltipUI/TooltipUI.swift index f4ef26c5..ffadcc99 100644 --- a/Packages/vChewing_TooltipUI/Sources/TooltipUI/TooltipUI.swift +++ b/Packages/vChewing_TooltipUI/Sources/TooltipUI/TooltipUI.swift @@ -73,49 +73,49 @@ public class TooltipUI: NSWindowController { var backgroundColor = NSColor.controlBackgroundColor var textColor = NSColor.textColor switch state { - case .normal: - backgroundColor = NSColor( - red: 0.18, green: 0.18, blue: 0.18, alpha: 1.00 - ) - textColor = NSColor.white - case .redAlert: - backgroundColor = NSColor( - red: 0.55, green: 0.00, blue: 0.00, alpha: 1.00 - ) - textColor = NSColor.white - case .warning: - backgroundColor = NSColor.purple - textColor = NSColor.white - case .succeeded: - backgroundColor = NSColor( - red: 0.21, green: 0.15, blue: 0.02, alpha: 1.00 - ) - textColor = NSColor.white - case .denialOverflow: - backgroundColor = NSColor( - red: 0.13, green: 0.08, blue: 0.00, alpha: 1.00 - ) - textColor = NSColor( - red: 1.00, green: 0.60, blue: 0.00, alpha: 1.00 - ) - case .denialInsufficiency: - backgroundColor = NSColor.windowBackgroundColor - textColor = NSColor.labelColor - case .prompt: - backgroundColor = NSColor( - red: 0.09, green: 0.15, blue: 0.15, alpha: 1.00 - ) - textColor = NSColor( - red: 0.91, green: 0.95, blue: 0.92, alpha: 1.00 - ) + case .normal: + backgroundColor = NSColor( + red: 0.18, green: 0.18, blue: 0.18, alpha: 1.00 + ) + textColor = NSColor.white + case .redAlert: + backgroundColor = NSColor( + red: 0.55, green: 0.00, blue: 0.00, alpha: 1.00 + ) + textColor = NSColor.white + case .warning: + backgroundColor = NSColor.purple + textColor = NSColor.white + case .succeeded: + backgroundColor = NSColor( + red: 0.21, green: 0.15, blue: 0.02, alpha: 1.00 + ) + textColor = NSColor.white + case .denialOverflow: + backgroundColor = NSColor( + red: 0.13, green: 0.08, blue: 0.00, alpha: 1.00 + ) + textColor = NSColor( + red: 1.00, green: 0.60, blue: 0.00, alpha: 1.00 + ) + case .denialInsufficiency: + backgroundColor = NSColor.windowBackgroundColor + textColor = NSColor.labelColor + case .prompt: + backgroundColor = NSColor( + red: 0.09, green: 0.15, blue: 0.15, alpha: 1.00 + ) + textColor = NSColor( + red: 0.91, green: 0.95, blue: 0.92, alpha: 1.00 + ) } if !NSApplication.isDarkMode { switch state { - case .denialInsufficiency: break - default: - let colorInterchange = backgroundColor - backgroundColor = textColor - textColor = colorInterchange + case .denialInsufficiency: break + default: + let colorInterchange = backgroundColor + backgroundColor = textColor + textColor = colorInterchange } } window?.backgroundColor = backgroundColor diff --git a/Packages/vChewing_Uninstaller/Package.swift b/Packages/vChewing_Uninstaller/Package.swift index 3c54805b..8a76a744 100644 --- a/Packages/vChewing_Uninstaller/Package.swift +++ b/Packages/vChewing_Uninstaller/Package.swift @@ -4,23 +4,23 @@ import PackageDescription let package = Package( name: "Uninstaller", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "Uninstaller", targets: ["Uninstaller"] - ) + ), ], dependencies: [ - .package(path: "../vChewing_CocoaExtension") + .package(path: "../vChewing_CocoaExtension"), ], targets: [ .target( name: "Uninstaller", dependencies: [ - .product(name: "CocoaExtension", package: "vChewing_CocoaExtension") + .product(name: "CocoaExtension", package: "vChewing_CocoaExtension"), ] - ) + ), ] ) diff --git a/Packages/vChewing_Uninstaller/Sources/Uninstaller/Uninstaller.swift b/Packages/vChewing_Uninstaller/Sources/Uninstaller/Uninstaller.swift index cfcb909b..a74afd93 100644 --- a/Packages/vChewing_Uninstaller/Sources/Uninstaller/Uninstaller.swift +++ b/Packages/vChewing_Uninstaller/Sources/Uninstaller/Uninstaller.swift @@ -35,12 +35,12 @@ public enum Uninstaller { let kTargetBundle = "/vChewing.app" let pathLibrary = isSudo - ? "/Library" - : realHomeDir.appendingPathComponent("Library/").path + ? "/Library" + : realHomeDir.appendingPathComponent("Library/").path let pathIMELibrary = isSudo - ? "/Library/Input Methods" - : realHomeDir.appendingPathComponent("Library/Input Methods/").path + ? "/Library/Input Methods" + : realHomeDir.appendingPathComponent("Library/Input Methods/").path let pathUnitKeyboardLayouts = "/Keyboard Layouts" let arrKeyLayoutFiles = [ "/vChewing ETen.keylayout", "/vChewingKeyLayout.bundle", "/vChewing MiTAC.keylayout", @@ -54,17 +54,17 @@ public enum Uninstaller { if !FileManager.trashTargetIfExists(objFullPath) { return -1 } } if CommandLine.arguments.count > 2, CommandLine.arguments[2] == "--all", - CommandLine.arguments[1] == "uninstall" + CommandLine.arguments[1] == "uninstall" { // 再處理是否需要移除放在預設使用者資料夾內的檔案的情況。 // 如果使用者有在輸入法偏好設定內將該目錄改到別的地方(而不是用 symbol link)的話,則不處理。 // 目前暫時無法應對 symbol link 的情況。 FileManager.trashTargetIfExists(defaultDataFolderPath) - FileManager.trashTargetIfExists(pathLibrary + "/Preferences/" + bundleID + ".plist") // 之後移除 App 偏好設定 - FileManager.trashTargetIfExists(pathLibrary + "/Receipts/org.atelierInmu.vChewing.bom") // pkg 垃圾 - FileManager.trashTargetIfExists(pathLibrary + "/Receipts/org.atelierInmu.vChewing.plist") // pkg 垃圾 + FileManager.trashTargetIfExists(pathLibrary + "/Preferences/" + bundleID + ".plist") // 之後移除 App 偏好設定 + FileManager.trashTargetIfExists(pathLibrary + "/Receipts/org.atelierInmu.vChewing.bom") // pkg 垃圾 + FileManager.trashTargetIfExists(pathLibrary + "/Receipts/org.atelierInmu.vChewing.plist") // pkg 垃圾 } - if !FileManager.trashTargetIfExists(pathIMELibrary + kTargetBundle) { return -1 } // 最後移除 App 自身 + if !FileManager.trashTargetIfExists(pathIMELibrary + kTargetBundle) { return -1 } // 最後移除 App 自身 // 幹掉殘留在記憶體內的執行緒。 if selfKill { NSApp.terminate(nil) diff --git a/Packages/vChewing_UpdateSputnik/Package.swift b/Packages/vChewing_UpdateSputnik/Package.swift index ed0e0ae2..dfa62829 100644 --- a/Packages/vChewing_UpdateSputnik/Package.swift +++ b/Packages/vChewing_UpdateSputnik/Package.swift @@ -4,19 +4,19 @@ import PackageDescription let package = Package( name: "UpdateSputnik", platforms: [ - .macOS(.v10_11) + .macOS(.v10_11), ], products: [ .library( name: "UpdateSputnik", targets: ["UpdateSputnik"] - ) + ), ], dependencies: [], targets: [ .target( name: "UpdateSputnik", dependencies: [] - ) + ), ] ) diff --git a/Packages/vChewing_UpdateSputnik/Sources/UpdateSputnik/UpdateSputnik.swift b/Packages/vChewing_UpdateSputnik/Sources/UpdateSputnik/UpdateSputnik.swift index ba900c24..9731de56 100644 --- a/Packages/vChewing_UpdateSputnik/Sources/UpdateSputnik/UpdateSputnik.swift +++ b/Packages/vChewing_UpdateSputnik/Sources/UpdateSputnik/UpdateSputnik.swift @@ -27,7 +27,7 @@ public class UpdateSputnik { return } } - isCurrentCheckForced = forced // 留著用來生成錯誤報告 + isCurrentCheckForced = forced // 留著用來生成錯誤報告 let request = URLRequest( url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 5 ) @@ -92,7 +92,7 @@ public class UpdateSputnik { NSLog("update check plist: \(plist)") guard let intRemoteVersion = Int(plist[kCFBundleVersionKey] as? String ?? ""), - let strRemoteVersionShortened = plist["CFBundleShortVersionString"] as? String + let strRemoteVersionShortened = plist["CFBundleShortVersionString"] as? String else { DispatchQueue.main.async { self.showError(message: NSLocalizedString("Plist downloaded cannot be parsed correctly.", comment: "")) @@ -102,9 +102,9 @@ public class UpdateSputnik { } guard let dicMainBundle = Bundle.main.infoDictionary, - let intCurrentVersion = Int(dicMainBundle[kCFBundleVersionKey as String] as? String ?? ""), - let strCurrentVersionShortened = dicMainBundle["CFBundleShortVersionString"] as? String - else { return } // Shouldn't happen. + let intCurrentVersion = Int(dicMainBundle[kCFBundleVersionKey as String] as? String ?? ""), + let strCurrentVersionShortened = dicMainBundle["CFBundleShortVersionString"] as? String + else { return } // Shouldn't happen. if intRemoteVersion <= intCurrentVersion { guard isCurrentCheckForced else { return } let alert = NSAlert() @@ -135,7 +135,7 @@ public class UpdateSputnik { NSApp.activate(ignoringOtherApps: true) if result == NSApplication.ModalResponse.alertFirstButtonReturn { if let siteInfoURLString = plist[kUpdateInfoPageURLKey] as? String, - let siteURL = URL(string: siteInfoURLString) + let siteURL = URL(string: siteInfoURLString) { DispatchQueue.main.async { NSWorkspace.shared.open(siteURL) diff --git a/Source/Modules/AppDelegate.swift b/Source/Modules/AppDelegate.swift index 376158fd..e30b2acc 100644 --- a/Source/Modules/AppDelegate.swift +++ b/Source/Modules/AppDelegate.swift @@ -41,12 +41,12 @@ extension AppDelegate { // MARK: - Public Functions -extension AppDelegate { - public func userNotificationCenter(_: NSUserNotificationCenter, shouldPresent _: NSUserNotification) -> Bool { +public extension AppDelegate { + func userNotificationCenter(_: NSUserNotificationCenter, shouldPresent _: NSUserNotification) -> Bool { true } - public func applicationDidFinishLaunching(_: Notification) { + func applicationDidFinishLaunching(_: Notification) { NSUserNotificationCenter.default.delegate = self // 一旦發現與使用者半衰模組的觀察行為有關的崩潰標記被開啟,就清空既有的半衰記憶資料檔案。 if PrefMgr.shared.failureFlagForUOMObservation { @@ -81,7 +81,7 @@ extension AppDelegate { } } - public func updateDirectoryMonitorPath() { + func updateDirectoryMonitorPath() { folderMonitor.stopMonitoring() folderMonitor = FolderMonitor( url: URL(fileURLWithPath: LMMgr.dataFolderPath(isDefaultFolder: false)) @@ -89,13 +89,13 @@ extension AppDelegate { folderMonitor.folderDidChange = { [weak self] in self?.reloadOnFolderChangeHappens() } - if LMMgr.userDataFolderExists { // 沒有資料夾的話,FolderMonitor 會崩潰。 + if LMMgr.userDataFolderExists { // 沒有資料夾的話,FolderMonitor 會崩潰。 folderMonitor.startMonitoring() reloadOnFolderChangeHappens(forced: true) } } - public func selfUninstall() { + func selfUninstall() { let content = String( format: NSLocalizedString( "This will remove vChewing Input Method from this user account, requiring your confirmation.", @@ -125,29 +125,29 @@ extension AppDelegate { /// 檢查該程式本身的記憶體佔用量。 /// - Returns: 記憶體佔用量(MiB)。 - @discardableResult public func checkMemoryUsage() -> Double { + @discardableResult func checkMemoryUsage() -> Double { guard let currentMemorySizeInBytes = NSApplication.memoryFootprint else { return 0 } let currentMemorySize: Double = (Double(currentMemorySizeInBytes) / 1024 / 1024).rounded(toPlaces: 1) switch currentMemorySize { - case 512...: - vCLog("WARNING: EXCESSIVE MEMORY FOOTPRINT (\(currentMemorySize)MB).") - let userNotification = NSUserNotification() - userNotification.title = NSLocalizedString("vChewing", comment: "") - userNotification.informativeText = NSLocalizedString( - "vChewing is rebooted due to a memory-excessive-usage problem. If convenient, please inform the developer that you are having this issue, stating whether you are using an Intel Mac or Apple Silicon Mac. An NSLog is generated with the current memory footprint size.", - comment: "" - ) - NSUserNotificationCenter.default.deliver(userNotification) - DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { - NSApp.terminate(self) - } - default: break + case 512...: + vCLog("WARNING: EXCESSIVE MEMORY FOOTPRINT (\(currentMemorySize)MB).") + let userNotification = NSUserNotification() + userNotification.title = NSLocalizedString("vChewing", comment: "") + userNotification.informativeText = NSLocalizedString( + "vChewing is rebooted due to a memory-excessive-usage problem. If convenient, please inform the developer that you are having this issue, stating whether you are using an Intel Mac or Apple Silicon Mac. An NSLog is generated with the current memory footprint size.", + comment: "" + ) + NSUserNotificationCenter.default.deliver(userNotification) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + NSApp.terminate(self) + } + default: break } return currentMemorySize } // New About Window - @IBAction public func about(_: Any) { + @IBAction func about(_: Any) { CtlAboutWindow.show() NSApp.activate(ignoringOtherApps: true) } diff --git a/Source/Modules/ChineseConverterBridge.swift b/Source/Modules/ChineseConverterBridge.swift index 2062c0b4..a015e58e 100644 --- a/Source/Modules/ChineseConverterBridge.swift +++ b/Source/Modules/ChineseConverterBridge.swift @@ -58,13 +58,13 @@ public enum ChineseConverter { for key in currencyNumeralDictTable.keys { guard let result = currencyNumeralDictTable[key] else { continue } if IMEApp.currentInputMode == .imeModeCHS { - target = target.replacingOccurrences(of: key, with: result.3) // Simplified Chinese + target = target.replacingOccurrences(of: key, with: result.3) // Simplified Chinese continue } switch (PrefMgr.shared.chineseConversionEnabled, PrefMgr.shared.shiftJISShinjitaiOutputEnabled) { - case (false, true), (true, true): target = target.replacingOccurrences(of: key, with: result.2) // JIS - case (true, false): target = target.replacingOccurrences(of: key, with: result.0) // KangXi - default: target = target.replacingOccurrences(of: key, with: result.1) // Contemporary + case (false, true), (true, true): target = target.replacingOccurrences(of: key, with: result.2) // JIS + case (true, false): target = target.replacingOccurrences(of: key, with: result.0) // KangXi + default: target = target.replacingOccurrences(of: key, with: result.1) // Contemporary } } } @@ -75,12 +75,12 @@ public enum ChineseConverter { /// - Returns: Text converted to Different Script. public static func crossConvert(_ string: String) -> String { switch IMEApp.currentInputMode { - case .imeModeCHS: - return shared.convert(string, to: .zhHantTW) - case .imeModeCHT: - return shared.convert(string, to: .zhHansCN) - default: - return string + case .imeModeCHS: + return shared.convert(string, to: .zhHantTW) + case .imeModeCHT: + return shared.convert(string, to: .zhHansCN) + default: + return string } } @@ -89,15 +89,15 @@ public enum ChineseConverter { public static func cassetteConvert(_ string: inout String) { // 0 為不轉換,1 為全轉換,2 為僅轉簡,3 為僅轉繁。 switch PrefMgr.shared.forceCassetteChineseConversion { - case 1: - switch IMEApp.currentInputMode { - case .imeModeCHS: string = shared.convert(string, to: .zhHansCN) - case .imeModeCHT: string = shared.convert(string, to: .zhHantTW) - case .imeModeNULL: break - } - case 2: if IMEApp.currentInputMode == .imeModeCHS { string = shared.convert(string, to: .zhHansCN) } - case 3: if IMEApp.currentInputMode == .imeModeCHT { string = shared.convert(string, to: .zhHantTW) } - default: return + case 1: + switch IMEApp.currentInputMode { + case .imeModeCHS: string = shared.convert(string, to: .zhHansCN) + case .imeModeCHT: string = shared.convert(string, to: .zhHantTW) + case .imeModeNULL: break + } + case 2: if IMEApp.currentInputMode == .imeModeCHS { string = shared.convert(string, to: .zhHansCN) } + case 3: if IMEApp.currentInputMode == .imeModeCHT { string = shared.convert(string, to: .zhHantTW) } + default: return } } @@ -118,11 +118,11 @@ public enum ChineseConverter { if PrefMgr.shared.cassetteEnabled { cassetteConvert(&text) } guard IMEApp.currentInputMode == .imeModeCHT else { return text } switch (PrefMgr.shared.chineseConversionEnabled, PrefMgr.shared.shiftJISShinjitaiOutputEnabled) { - case (false, true): return ChineseConverter.cnvTradToJIS(text) - case (true, false): return ChineseConverter.cnvTradToKangXi(text) - // 本來這兩個開關不該同時開啟的,但萬一被同時開啟了的話就這樣處理: - case (true, true): return ChineseConverter.cnvTradToJIS(text) - case (false, false): return text + case (false, true): return ChineseConverter.cnvTradToJIS(text) + case (true, false): return ChineseConverter.cnvTradToKangXi(text) + // 本來這兩個開關不該同時開啟的,但萬一被同時開啟了的話就這樣處理: + case (true, true): return ChineseConverter.cnvTradToJIS(text) + case (false, false): return text } } } diff --git a/Source/Modules/IMEState.swift b/Source/Modules/IMEState.swift index 062e3888..6f70b474 100644 --- a/Source/Modules/IMEState.swift +++ b/Source/Modules/IMEState.swift @@ -87,36 +87,36 @@ public struct IMEState: IMEStateProtocol { // MARK: - 針對不同的狀態,規定不同的構造器 -extension IMEState { - public static func ofDeactivated() -> IMEState { .init(type: .ofDeactivated) } - public static func ofEmpty() -> IMEState { .init(type: .ofEmpty) } - public static func ofAbortion() -> IMEState { .init(type: .ofAbortion) } +public extension IMEState { + static func ofDeactivated() -> IMEState { .init(type: .ofDeactivated) } + static func ofEmpty() -> IMEState { .init(type: .ofEmpty) } + static func ofAbortion() -> IMEState { .init(type: .ofAbortion) } /// 用以手動遞交指定內容的狀態。 /// - Remark: 直接切換至該狀態的話,會丟失上一個狀態的內容。 /// 如不想丟失的話,請先切換至 `.ofEmpty()` 再切換至 `.ofCommitting()`。 /// - Parameter textToCommit: 要遞交的文本。 /// - Returns: 要切換到的狀態。 - public static func ofCommitting(textToCommit: String) -> IMEState { + static func ofCommitting(textToCommit: String) -> IMEState { var result = IMEState(type: .ofCommitting) result.textToCommit = textToCommit ChineseConverter.ensureCurrencyNumerals(target: &result.data.textToCommit) return result } - public static func ofAssociates(candidates: [([String], String)]) -> IMEState { + static func ofAssociates(candidates: [([String], String)]) -> IMEState { var result = IMEState(type: .ofAssociates) result.candidates = candidates return result } - public static func ofInputting(displayTextSegments: [String], cursor: Int) -> IMEState { + static func ofInputting(displayTextSegments: [String], cursor: Int) -> IMEState { var result = IMEState(displayTextSegments: displayTextSegments, cursor: cursor) result.type = .ofInputting return result } - public static func ofMarking( + static func ofMarking( displayTextSegments: [String], markedReadings: [String], cursor: Int, marker: Int ) -> IMEState @@ -129,7 +129,7 @@ extension IMEState { return result } - public static func ofCandidates(candidates: [([String], String)], displayTextSegments: [String], cursor: Int) + static func ofCandidates(candidates: [([String], String)], displayTextSegments: [String], cursor: Int) -> IMEState { var result = IMEState(displayTextSegments: displayTextSegments, cursor: cursor) @@ -138,7 +138,7 @@ extension IMEState { return result } - public static func ofSymbolTable(node: CandidateNode) -> IMEState { + static func ofSymbolTable(node: CandidateNode) -> IMEState { var result = IMEState(node: node) result.type = .ofSymbolTable return result @@ -147,77 +147,77 @@ extension IMEState { // MARK: - 規定一個狀態該怎樣返回自己的資料值 -extension IMEState { - public var isFilterable: Bool { data.isFilterable } - public var isMarkedLengthValid: Bool { data.isMarkedLengthValid } - public var displayedText: String { data.displayedText } - public var displayedTextConverted: String { data.displayedTextConverted } - public var displayTextSegments: [String] { data.displayTextSegments } - public var markedRange: Range { data.markedRange } - public var u16MarkedRange: Range { data.u16MarkedRange } - public var u16Cursor: Int { data.u16Cursor } +public extension IMEState { + var isFilterable: Bool { data.isFilterable } + var isMarkedLengthValid: Bool { data.isMarkedLengthValid } + var displayedText: String { data.displayedText } + var displayedTextConverted: String { data.displayedTextConverted } + var displayTextSegments: [String] { data.displayTextSegments } + var markedRange: Range { data.markedRange } + var u16MarkedRange: Range { data.u16MarkedRange } + var u16Cursor: Int { data.u16Cursor } - public var cursor: Int { + var cursor: Int { get { data.cursor } set { data.cursor = newValue } } - public var marker: Int { + var marker: Int { get { data.marker } set { data.marker = newValue } } - public var convertedToInputting: IMEStateProtocol { + var convertedToInputting: IMEStateProtocol { if type == .ofInputting { return self } var result = Self.ofInputting(displayTextSegments: data.displayTextSegments, cursor: data.cursor) result.tooltip = data.tooltipBackupForInputting return result } - public var candidates: [([String], String)] { + var candidates: [([String], String)] { get { data.candidates } set { data.candidates = newValue } } - public var textToCommit: String { + var textToCommit: String { get { data.textToCommit } set { data.textToCommit = newValue } } - public var tooltip: String { + var tooltip: String { get { data.tooltip } set { data.tooltip = newValue } } - public var attributedString: NSAttributedString { + var attributedString: NSAttributedString { switch type { - case .ofMarking: return data.attributedStringMarking - case .ofAssociates, .ofSymbolTable: return data.attributedStringPlaceholder - default: return data.attributedStringNormal + case .ofMarking: return data.attributedStringMarking + case .ofAssociates, .ofSymbolTable: return data.attributedStringPlaceholder + default: return data.attributedStringNormal } } /// 該參數僅用作輔助判斷。在 InputHandler 內使用的話,必須再檢查 !compositor.isEmpty。 - public var hasComposition: Bool { + var hasComposition: Bool { switch type { - case .ofInputting, .ofMarking, .ofCandidates: return true - default: return false + case .ofInputting, .ofMarking, .ofCandidates: return true + default: return false } } - public var isCandidateContainer: Bool { + var isCandidateContainer: Bool { switch type { - case .ofCandidates, .ofAssociates, .ofSymbolTable: return true - default: return false + case .ofCandidates, .ofAssociates, .ofSymbolTable: return true + default: return false } } - public var tooltipBackupForInputting: String { + var tooltipBackupForInputting: String { get { data.tooltipBackupForInputting } set { data.tooltipBackupForInputting = newValue } } - public var tooltipDuration: Double { + var tooltipDuration: Double { get { type == .ofMarking ? 0 : data.tooltipDuration } set { data.tooltipDuration = newValue } } diff --git a/Source/Modules/IMEStateData.swift b/Source/Modules/IMEStateData.swift index 1ed8502f..b91d27e0 100644 --- a/Source/Modules/IMEStateData.swift +++ b/Source/Modules/IMEStateData.swift @@ -16,7 +16,7 @@ public struct IMEStateData: IMEStateDataProtocol { } static var allowedMarkLengthRange: ClosedRange { - Self.minCandidateLength...PrefMgr.shared.maxCandidateLength + Self.minCandidateLength ... PrefMgr.shared.maxCandidateLength } public var displayedText: String = "" { @@ -53,7 +53,7 @@ public struct IMEStateData: IMEStateDataProtocol { } public var markedRange: Range { - min(cursor, marker).. { - min(u16Cursor, u16Marker)..注音轉拼音->轉教科書式標調 subNeta = Tekkon.restoreToneOneInPhona(target: subNeta) @@ -205,19 +205,19 @@ extension IMEStateData { return arrOutput.joined(separator: "\u{A0}") } - public var userPhraseKVPair: (String, String) { + var userPhraseKVPair: (String, String) { let key = markedReadings.joined(separator: InputHandler.keySeparator) let value = displayedText.charComponents[markedRange].joined() return (key, value) } - public var userPhraseDumped: String { + var userPhraseDumped: String { let pair = userPhraseKVPair let nerfedScore = SessionCtl.areWeNerfing && markedTargetExists ? " -114.514" : "" return "\(pair.1) \(pair.0)\(nerfedScore)" } - public var userPhraseDumpedConverted: String { + var userPhraseDumpedConverted: String { let pair = userPhraseKVPair let text = ChineseConverter.crossConvert(pair.1) let nerfedScore = SessionCtl.areWeNerfing && markedTargetExists ? " -114.514" : "" @@ -225,7 +225,7 @@ extension IMEStateData { return "\(text) \(pair.0)\(nerfedScore) \(convertedMark)" } - public mutating func updateTooltipForMarking() { + mutating func updateTooltipForMarking() { var tooltipForMarking: String { let pair = userPhraseKVPair if PrefMgr.shared.phraseReplacementEnabled { diff --git a/Source/Modules/InputHandler_Core.swift b/Source/Modules/InputHandler_Core.swift index 81e2ea34..da597644 100644 --- a/Source/Modules/InputHandler_Core.swift +++ b/Source/Modules/InputHandler_Core.swift @@ -81,9 +81,9 @@ public class InputHandler: InputHandlerProtocol { /// 半衰模組的衰減指數 let kEpsilon: Double = 0.000_001 - public var calligrapher = "" // 磁帶專用組筆區 - public var composer: Tekkon.Composer = .init() // 注拼槽 - public var compositor: Megrez.Compositor // 組字器 + public var calligrapher = "" // 磁帶專用組筆區 + public var composer: Tekkon.Composer = .init() // 注拼槽 + public var compositor: Megrez.Compositor // 組字器 public var currentUOM: vChewingLM.LMUserOverride public var currentLM: vChewingLM.LMInstantiator { didSet { @@ -117,7 +117,7 @@ public class InputHandler: InputHandlerProtocol { /// 獲取當前標記得範圍。這個函式只能是函式、而非只讀變數。 /// - Returns: 當前標記範圍。 func currentMarkedRange() -> Range { - min(compositor.cursor, compositor.marker).. rearBoundary { compositor.jumpCursorBySpan(to: .rear) } rearBoundary = min(compositor.cursor, rearBoundary) - compositor.cursor = cursorBackup // 游標歸位,再接著計算。 + compositor.cursor = cursorBackup // 游標歸位,再接著計算。 while compositor.cursor < frontBoundary { compositor.jumpCursorBySpan(to: .front) } frontBoundary = min(max(compositor.cursor, frontBoundary), compositor.length) - compositor.cursor = cursorBackup // 計算結束,游標歸位。 + compositor.cursor = cursorBackup // 計算結束,游標歸位。 debugIntelToPrint.append("FIN: \(rearBoundary)..<\(frontBoundary)") vCLog(debugIntelToPrint) // 接下來獲取這個範圍內的媽的都不知道該怎麼講了。 - var nodeIndices = [Int]() // 僅作統計用。 + var nodeIndices = [Int]() // 僅作統計用。 - var position = rearBoundary // 臨時統計用 + var position = rearBoundary // 臨時統計用 while position < frontBoundary { guard let regionIndex = compositor.walkedNodes.cursorRegionMap[position] else { position += 1 continue } if !nodeIndices.contains(regionIndex) { - nodeIndices.append(regionIndex) // 新增統計 - guard compositor.walkedNodes.count > regionIndex else { break } // 防呆 + nodeIndices.append(regionIndex) // 新增統計 + guard compositor.walkedNodes.count > regionIndex else { break } // 防呆 let currentNode = compositor.walkedNodes[regionIndex] guard currentNode.keyArray.count == currentNode.value.count else { compositor.overrideCandidate(currentNode.currentPair, at: position) @@ -234,7 +234,7 @@ public class InputHandler: InputHandlerProtocol { } let values = currentNode.currentPair.value.charComponents for (subPosition, key) in currentNode.keyArray.enumerated() { - guard values.count > subPosition else { break } // 防呆,應該沒有發生的可能性 + guard values.count > subPosition else { break } // 防呆,應該沒有發生的可能性 let thePair = Megrez.Compositor.KeyValuePaired( keyArray: [key], value: values[subPosition] ) @@ -305,8 +305,8 @@ public class InputHandler: InputHandlerProtocol { /// 微軟新注音輸入法的游標後置風格也是不允許 nodeCrossing 的。 var arrCandidates: [Megrez.Compositor.KeyValuePaired] = { switch prefs.useRearCursorMode { - case false: return compositor.fetchCandidates(at: cursorForCandidate, filter: .endAt) - case true: return compositor.fetchCandidates(at: cursorForCandidate, filter: .beginAt) + case false: return compositor.fetchCandidates(at: cursorForCandidate, filter: .endAt) + case true: return compositor.fetchCandidates(at: cursorForCandidate, filter: .beginAt) } }() @@ -375,21 +375,21 @@ public class InputHandler: InputHandlerProtocol { /// 給注拼槽指定注音排列或拼音輸入種類之後,將注拼槽內容清空。 public func ensureKeyboardParser() { switch currentKeyboardParserType { - case KeyboardParser.ofStandard: composer.ensureParser(arrange: .ofDachen) - case KeyboardParser.ofDachen26: composer.ensureParser(arrange: .ofDachen26) - case KeyboardParser.ofETen: composer.ensureParser(arrange: .ofETen) - case KeyboardParser.ofHsu: composer.ensureParser(arrange: .ofHsu) - case KeyboardParser.ofETen26: composer.ensureParser(arrange: .ofETen26) - case KeyboardParser.ofIBM: composer.ensureParser(arrange: .ofIBM) - case KeyboardParser.ofMiTAC: composer.ensureParser(arrange: .ofMiTAC) - case KeyboardParser.ofFakeSeigyou: composer.ensureParser(arrange: .ofFakeSeigyou) - case KeyboardParser.ofSeigyou: composer.ensureParser(arrange: .ofSeigyou) - case KeyboardParser.ofStarlight: composer.ensureParser(arrange: .ofStarlight) - case KeyboardParser.ofHanyuPinyin: composer.ensureParser(arrange: .ofHanyuPinyin) - case KeyboardParser.ofSecondaryPinyin: composer.ensureParser(arrange: .ofSecondaryPinyin) - case KeyboardParser.ofYalePinyin: composer.ensureParser(arrange: .ofYalePinyin) - case KeyboardParser.ofHualuoPinyin: composer.ensureParser(arrange: .ofHualuoPinyin) - case KeyboardParser.ofUniversalPinyin: composer.ensureParser(arrange: .ofUniversalPinyin) + case KeyboardParser.ofStandard: composer.ensureParser(arrange: .ofDachen) + case KeyboardParser.ofDachen26: composer.ensureParser(arrange: .ofDachen26) + case KeyboardParser.ofETen: composer.ensureParser(arrange: .ofETen) + case KeyboardParser.ofHsu: composer.ensureParser(arrange: .ofHsu) + case KeyboardParser.ofETen26: composer.ensureParser(arrange: .ofETen26) + case KeyboardParser.ofIBM: composer.ensureParser(arrange: .ofIBM) + case KeyboardParser.ofMiTAC: composer.ensureParser(arrange: .ofMiTAC) + case KeyboardParser.ofFakeSeigyou: composer.ensureParser(arrange: .ofFakeSeigyou) + case KeyboardParser.ofSeigyou: composer.ensureParser(arrange: .ofSeigyou) + case KeyboardParser.ofStarlight: composer.ensureParser(arrange: .ofStarlight) + case KeyboardParser.ofHanyuPinyin: composer.ensureParser(arrange: .ofHanyuPinyin) + case KeyboardParser.ofSecondaryPinyin: composer.ensureParser(arrange: .ofSecondaryPinyin) + case KeyboardParser.ofYalePinyin: composer.ensureParser(arrange: .ofYalePinyin) + case KeyboardParser.ofHualuoPinyin: composer.ensureParser(arrange: .ofHualuoPinyin) + case KeyboardParser.ofUniversalPinyin: composer.ensureParser(arrange: .ofUniversalPinyin) } composer.clear() composer.phonabetCombinationCorrectionEnabled = prefs.autoCorrectReadingCombination @@ -426,7 +426,7 @@ public class InputHandler: InputHandlerProtocol { if !Tekkon.allowedPhonabets.contains(neta) || neta == " " { return nil } if Tekkon.allowedIntonations.contains(neta) { hasIntonation = true } } - if hasIntonation, components.count == 1 { return nil } // 剔除純聲調之情形 + if hasIntonation, components.count == 1 { return nil } // 剔除純聲調之情形 let rawDataSansIntonation = hasIntonation ? components.dropLast(1).joined() : rawData return (rawData, rawDataSansIntonation, hasIntonation) } @@ -435,8 +435,8 @@ public class InputHandler: InputHandlerProtocol { /// - Parameter input: 傳入的按鍵訊號。 /// - Returns: 判斷結果:是否為聲調鍵。 func isIntonationKey(_ input: InputSignalProtocol) -> Bool { - var theComposer = composer // 複製一份用來做實驗。 - theComposer.clear() // 清空各種槽的內容。 + var theComposer = composer // 複製一份用來做實驗。 + theComposer.clear() // 清空各種槽的內容。 theComposer.receiveKey(fromString: input.text) return theComposer.hasIntonation(withNothingElse: true) } @@ -470,10 +470,10 @@ public class InputHandler: InputHandlerProtocol { func generatePunctuationNamePrefix(withKeyCondition input: InputSignalProtocol) -> String { if prefs.halfWidthPunctuationEnabled { return "_half_punctuation_" } switch (input.isControlHold, input.isOptionHold) { - case (true, true): return "_alt_ctrl_punctuation_" - case (true, false): return "_ctrl_punctuation_" - case (false, true): return "_alt_punctuation_" - case (false, false): return "_punctuation_" + case (true, true): return "_alt_ctrl_punctuation_" + case (true, false): return "_ctrl_punctuation_" + case (false, true): return "_alt_punctuation_" + case (false, false): return "_punctuation_" } } } @@ -493,9 +493,9 @@ extension InputHandler { /// 用比較形象且生動卻有點噁心的解釋的話,蒼蠅一邊吃一邊屙。 var commitOverflownComposition: String { guard !compositor.walkedNodes.isEmpty, - compositor.length > compositorWidthLimit, - let identifier = delegate?.clientBundleIdentifier, - prefs.clientsIMKTextInputIncapable.contains(identifier) + compositor.length > compositorWidthLimit, + let identifier = delegate?.clientBundleIdentifier, + prefs.clientsIMKTextInputIncapable.contains(identifier) else { return "" } // 回頭在這裡插上對 Steam 的 Client Identifier 的要求。 var textToCommit = "" @@ -507,13 +507,13 @@ extension InputHandler { textToCommit += node.currentPair.value } else { delta = min(delta, node.keyArray.count) - textToCommit += node.currentPair.value.charComponents[0.. Bool? { guard let delegate = delegate else { return nil } - var wildcardKey: String { currentLM.cassetteWildcardKey } // 花牌鍵。 + var wildcardKey: String { currentLM.cassetteWildcardKey } // 花牌鍵。 let isWildcardKeyInput: Bool = (input.text == wildcardKey && !wildcardKey.isEmpty) var keyConsumedByStrokes = false let skipStrokeHandling = input.isReservedKey || input.isNumericPadKey || input.isNonLaptopFunctionKey - || input.isControlHold || input.isOptionHold || input.isShiftHold || input.isCommandHold + || input.isControlHold || input.isOptionHold || input.isShiftHold || input.isCommandHold let confirmCombination = input.isSpace || input.isEnter var isLongestPossibleKeyFormed: Bool { @@ -229,14 +229,14 @@ extension InputHandler { var combineStrokes = (isStrokesFull && prefs.autoCompositeWithLongestPossibleCassetteKey) - || (isWildcardKeyInput && !calligrapher.isEmpty) + || (isWildcardKeyInput && !calligrapher.isEmpty) // 如果當前的按鍵是 Enter 或 Space 的話,這時就可以取出 calligrapher 內的筆畫來做檢查了。 // 來看看詞庫內到底有沒有對應的讀音索引。這裡用了類似「|=」的判斷處理方式。 combineStrokes = combineStrokes || (!calligrapher.isEmpty && confirmCombination) if combineStrokes { if input.isControlHold, input.isCommandHold, input.isEnter, - !input.isOptionHold, !input.isShiftHold, composer.isEmpty + !input.isOptionHold, !input.isShiftHold, composer.isEmpty { return handleEnter(input: input, readingOnly: true) } @@ -247,10 +247,10 @@ extension InputHandler { calligrapher.removeAll() // 根據「組字器是否為空」來判定回呼哪一種狀態。 switch compositor.isEmpty { - case false: delegate.switchState(generateStateOfInputting()) - case true: delegate.switchState(IMEState.ofAbortion()) + case false: delegate.switchState(generateStateOfInputting()) + case true: delegate.switchState(IMEState.ofAbortion()) } - return true // 向 IMK 報告說這個按鍵訊號已經被輸入法攔截處理了。 + return true // 向 IMK 報告說這個按鍵訊號已經被輸入法攔截處理了。 } // 將該讀音插入至組字器內的軌格當中。 @@ -281,20 +281,20 @@ extension InputHandler { if prefs.useSCPCTypingMode { let candidateState: IMEStateProtocol = generateStateOfCandidates() switch candidateState.candidates.count { - case 2...: delegate.switchState(candidateState) - case 1: - let firstCandidate = candidateState.candidates.first! // 一定會有,所以強制拆包也無妨。 - let reading: String = firstCandidate.0.joined(separator: compositor.separator) - let text: String = firstCandidate.1 - delegate.switchState(IMEState.ofCommitting(textToCommit: text)) + case 2...: delegate.switchState(candidateState) + case 1: + let firstCandidate = candidateState.candidates.first! // 一定會有,所以強制拆包也無妨。 + let reading: String = firstCandidate.0.joined(separator: compositor.separator) + let text: String = firstCandidate.1 + delegate.switchState(IMEState.ofCommitting(textToCommit: text)) - if !prefs.associatedPhrasesEnabled { - delegate.switchState(IMEState.ofEmpty()) - } else { - let associatedPhrases = generateStateOfAssociates(withPair: .init(keyArray: [reading], value: text)) - delegate.switchState(associatedPhrases.candidates.isEmpty ? IMEState.ofEmpty() : associatedPhrases) - } - default: break + if !prefs.associatedPhrasesEnabled { + delegate.switchState(IMEState.ofEmpty()) + } else { + let associatedPhrases = generateStateOfAssociates(withPair: .init(keyArray: [reading], value: text)) + delegate.switchState(associatedPhrases.candidates.isEmpty ? IMEState.ofEmpty() : associatedPhrases) + } + default: break } } // 將「這個按鍵訊號已經被輸入法攔截處理了」的結果藉由 SessionCtl 回報給 IMK。 diff --git a/Source/Modules/InputHandler_HandleEvent.swift b/Source/Modules/InputHandler_HandleEvent.swift index 8205ac27..534807b7 100644 --- a/Source/Modules/InputHandler_HandleEvent.swift +++ b/Source/Modules/InputHandler_HandleEvent.swift @@ -48,8 +48,8 @@ extension InputHandler { // 聯想詞選字。 if let newChar = CtlCandidateIMK.defaultIMKSelectionKey[event.keyCode], - event.isShiftHold, delegate.state.type == .ofAssociates, - let newEvent = event.reinitiate(modifierFlags: [], characters: newChar) + event.isShiftHold, delegate.state.type == .ofAssociates, + let newEvent = event.reinitiate(modifierFlags: [], characters: newChar) { if #available(macOS 10.14, *) { imkCandidates.handleKeyboardEvent(newEvent) @@ -73,21 +73,21 @@ extension InputHandler { } else if event.isSymbolMenuPhysicalKey { // 符號鍵的行為是固定的,不受偏好設定影響。 switch imkC.currentLayout { - case .horizontal: _ = event.isShiftHold ? imkC.moveUp(self) : imkC.moveDown(self) - case .vertical: _ = event.isShiftHold ? imkC.moveLeft(self) : imkC.moveRight(self) - @unknown default: break + case .horizontal: _ = event.isShiftHold ? imkC.moveUp(self) : imkC.moveDown(self) + case .vertical: _ = event.isShiftHold ? imkC.moveLeft(self) : imkC.moveRight(self) + @unknown default: break } return true } else if event.isSpace { switch prefs.specifyShiftSpaceKeyBehavior { - case true: _ = event.isShiftHold ? imkC.highlightNextCandidate() : imkC.showNextPage() - case false: _ = event.isShiftHold ? imkC.showNextPage() : imkC.highlightNextCandidate() + case true: _ = event.isShiftHold ? imkC.highlightNextCandidate() : imkC.showNextPage() + case false: _ = event.isShiftHold ? imkC.showNextPage() : imkC.highlightNextCandidate() } return true } else if event.isTab { switch prefs.specifyShiftTabKeyBehavior { - case true: _ = event.isShiftHold ? imkC.showPreviousPage() : imkC.showNextPage() - case false: _ = event.isShiftHold ? imkC.highlightPreviousCandidate() : imkC.highlightNextCandidate() + case true: _ = event.isShiftHold ? imkC.showPreviousPage() : imkC.showNextPage() + case false: _ = event.isShiftHold ? imkC.highlightPreviousCandidate() : imkC.highlightNextCandidate() } return true } else { @@ -115,9 +115,9 @@ extension InputHandler { } if delegate.state.type == .ofAssociates, - !event.isPageUp, !event.isPageDown, !event.isCursorForward, !event.isCursorBackward, - !event.isCursorClockLeft, !event.isCursorClockRight, !event.isSpace, - !event.isEnter || !prefs.alsoConfirmAssociatedCandidatesByEnter + !event.isPageUp, !event.isPageDown, !event.isCursorForward, !event.isCursorBackward, + !event.isCursorClockLeft, !event.isCursorClockRight, !event.isSpace, + !event.isEnter || !prefs.alsoConfirmAssociatedCandidatesByEnter { return handleInput(event: event) } diff --git a/Source/Modules/InputHandler_HandleInput.swift b/Source/Modules/InputHandler_HandleInput.swift index 04ad530f..7b3f5db8 100644 --- a/Source/Modules/InputHandler_HandleInput.swift +++ b/Source/Modules/InputHandler_HandleInput.swift @@ -29,7 +29,7 @@ extension InputHandler { guard !input.text.isEmpty, input.charCode.isPrintable, let delegate = delegate else { return false } let inputText: String = input.text - var state: IMEStateProtocol { delegate.state } // 常數轉變數。 + var state: IMEStateProtocol { delegate.state } // 常數轉變數。 // 提前放行一些用不到的特殊按鍵輸入情形。 if input.isInvalid, state.type == .ofEmpty || state.type == .ofDeactivated { return false } @@ -109,9 +109,9 @@ extension InputHandler { // 僅憑藉 state.hasComposition 的話,並不能真實把握組字器的狀況。 // 另外,這裡不要用「!input.isFunctionKeyHold」,否則會導致對上下左右鍵與翻頁鍵的判斷失效。 if state.hasComposition, !compositor.isEmpty, isComposerOrCalligrapherEmpty, - !input.isOptionHold, !input.isShiftHold, !input.isCommandHold, !input.isControlHold, - input.isCursorClockLeft || input.isCursorClockRight || (input.isSpace && prefs.chooseCandidateUsingSpace) - || input.isPageDown || input.isPageUp || (input.isTab && prefs.specifyShiftTabKeyBehavior) + !input.isOptionHold, !input.isShiftHold, !input.isCommandHold, !input.isControlHold, + input.isCursorClockLeft || input.isCursorClockRight || (input.isSpace && prefs.chooseCandidateUsingSpace) + || input.isPageDown || input.isPageUp || (input.isTab && prefs.specifyShiftTabKeyBehavior) { // 開始決定是否切換至選字狀態。 let candidateState: IMEStateProtocol = generateStateOfCandidates() @@ -127,11 +127,11 @@ extension InputHandler { // 此處 JIS 鍵盤判定無法用於螢幕鍵盤。所以,螢幕鍵盤的場合,系統會依照 US 鍵盤的判定方案。 let isJIS: Bool = KBGetLayoutType(Int16(LMGetKbdType())) == kKeyboardJIS switch (input.keyCode, isJIS) { - case (30, true): return revolveCandidate(reverseOrder: true) - case (42, true): return revolveCandidate(reverseOrder: false) - case (33, false): return revolveCandidate(reverseOrder: true) - case (30, false): return revolveCandidate(reverseOrder: false) - default: break + case (30, true): return revolveCandidate(reverseOrder: true) + case (42, true): return revolveCandidate(reverseOrder: false) + case (33, false): return revolveCandidate(reverseOrder: true) + case (30, false): return revolveCandidate(reverseOrder: false) + default: break } } @@ -139,55 +139,55 @@ extension InputHandler { if let keyCodeType = KeyCode(rawValue: input.keyCode) { switch keyCodeType { - case .kEscape: return handleEsc() - case .kTab, .kContextMenu: return revolveCandidate(reverseOrder: input.isShiftHold) - case .kUpArrow, .kDownArrow, .kLeftArrow, .kRightArrow: - let rotation: Bool = (input.isOptionHold || input.isShiftHold) && state.type == .ofInputting - handleArrowKey: switch (keyCodeType, delegate.isVerticalTyping) { - case (.kLeftArrow, false), (.kUpArrow, true): return handleBackward(input: input) - case (.kRightArrow, false), (.kDownArrow, true): return handleForward(input: input) - case (.kUpArrow, false), (.kLeftArrow, true): - return rotation ? revolveCandidate(reverseOrder: true) : handleClockKey() - case (.kDownArrow, false), (.kRightArrow, true): - return rotation ? revolveCandidate(reverseOrder: false) : handleClockKey() - default: break handleArrowKey // 該情況應該不會發生,因為上面都有處理過。 + case .kEscape: return handleEsc() + case .kTab, .kContextMenu: return revolveCandidate(reverseOrder: input.isShiftHold) + case .kUpArrow, .kDownArrow, .kLeftArrow, .kRightArrow: + let rotation: Bool = (input.isOptionHold || input.isShiftHold) && state.type == .ofInputting + handleArrowKey: switch (keyCodeType, delegate.isVerticalTyping) { + case (.kLeftArrow, false), (.kUpArrow, true): return handleBackward(input: input) + case (.kRightArrow, false), (.kDownArrow, true): return handleForward(input: input) + case (.kUpArrow, false), (.kLeftArrow, true): + return rotation ? revolveCandidate(reverseOrder: true) : handleClockKey() + case (.kDownArrow, false), (.kRightArrow, true): + return rotation ? revolveCandidate(reverseOrder: false) : handleClockKey() + default: break handleArrowKey // 該情況應該不會發生,因為上面都有處理過。 + } + case .kHome: return handleHome() + case .kEnd: return handleEnd() + case .kBackSpace: return handleBackSpace(input: input) + case .kWindowsDelete: return handleDelete(input: input) + case .kCarriageReturn, .kLineFeed: return handleEnter(input: input) + case .kSpace: // 倘若沒有在偏好設定內將 Space 空格鍵設為選字窗呼叫用鍵的話……… + // 空格字符輸入行為處理。 + switch state.type { + case .ofEmpty: + if !input.isOptionHold, !input.isControlHold, !input.isCommandHold { + delegate.switchState(IMEState.ofCommitting(textToCommit: input.isShiftHold ? " " : " ")) + return true } - case .kHome: return handleHome() - case .kEnd: return handleEnd() - case .kBackSpace: return handleBackSpace(input: input) - case .kWindowsDelete: return handleDelete(input: input) - case .kCarriageReturn, .kLineFeed: return handleEnter(input: input) - case .kSpace: // 倘若沒有在偏好設定內將 Space 空格鍵設為選字窗呼叫用鍵的話……… - // 空格字符輸入行為處理。 - switch state.type { - case .ofEmpty: - if !input.isOptionHold, !input.isControlHold, !input.isCommandHold { - delegate.switchState(IMEState.ofCommitting(textToCommit: input.isShiftHold ? " " : " ")) - return true - } - case .ofInputting: - // 臉書等網站會攔截 Tab 鍵,所以用 Shift+Command+Space 對候選字詞做正向/反向輪替。 - if input.isShiftHold, !input.isControlHold, !input.isOptionHold { - return revolveCandidate(reverseOrder: input.isCommandHold) - } - if compositor.cursor < compositor.length, compositor.insertKey(" ") { - walk() - // 一邊吃一邊屙(僅對位列黑名單的 App 用這招限制組字區長度)。 - let textToCommit = commitOverflownComposition - var inputting = generateStateOfInputting() - inputting.textToCommit = textToCommit - delegate.switchState(inputting) - } else { - let displayedText = state.displayedText - if !displayedText.isEmpty { - delegate.switchState(IMEState.ofCommitting(textToCommit: displayedText)) - } - delegate.switchState(IMEState.ofCommitting(textToCommit: " ")) - } - return true - default: break + case .ofInputting: + // 臉書等網站會攔截 Tab 鍵,所以用 Shift+Command+Space 對候選字詞做正向/反向輪替。 + if input.isShiftHold, !input.isControlHold, !input.isOptionHold { + return revolveCandidate(reverseOrder: input.isCommandHold) } + if compositor.cursor < compositor.length, compositor.insertKey(" ") { + walk() + // 一邊吃一邊屙(僅對位列黑名單的 App 用這招限制組字區長度)。 + let textToCommit = commitOverflownComposition + var inputting = generateStateOfInputting() + inputting.textToCommit = textToCommit + delegate.switchState(inputting) + } else { + let displayedText = state.displayedText + if !displayedText.isEmpty { + delegate.switchState(IMEState.ofCommitting(textToCommit: displayedText)) + } + delegate.switchState(IMEState.ofCommitting(textToCommit: " ")) + } + return true default: break + } + default: break } } @@ -206,7 +206,7 @@ extension InputHandler { // 開始決定是否切換至選字狀態。 let newState = generateStateOfCandidates() _ = newState.candidates.isEmpty ? delegate.callError("B5127D8A") : delegate.switchState(newState) - } else { // 不要在注音沒敲完整的情況下叫出統合符號選單。 + } else { // 不要在注音沒敲完整的情況下叫出統合符號選單。 delegate.callError("17446655") } return true @@ -260,21 +260,21 @@ extension InputHandler { // MARK: 摁住 Shift+字母鍵 的處理 (Shift+Letter Processing) if input.isUpperCaseASCIILetterKey, !input.isCommandHold, !input.isControlHold { - if input.isShiftHold { // 這裡先不要判斷 isOptionHold。 + if input.isShiftHold { // 這裡先不要判斷 isOptionHold。 switch prefs.upperCaseLetterKeyBehavior { - case 1: - delegate.switchState(IMEState.ofEmpty()) - delegate.switchState(IMEState.ofCommitting(textToCommit: inputText.lowercased())) + case 1: + delegate.switchState(IMEState.ofEmpty()) + delegate.switchState(IMEState.ofCommitting(textToCommit: inputText.lowercased())) + return true + case 2: + delegate.switchState(IMEState.ofEmpty()) + delegate.switchState(IMEState.ofCommitting(textToCommit: inputText.uppercased())) + return true + default: // 包括 case 0,直接塞給組字區。 + let letter = "_letter_\(inputText)" + if handlePunctuation(letter) { return true - case 2: - delegate.switchState(IMEState.ofEmpty()) - delegate.switchState(IMEState.ofCommitting(textToCommit: inputText.uppercased())) - return true - default: // 包括 case 0,直接塞給組字區。 - let letter = "_letter_\(inputText)" - if handlePunctuation(letter) { - return true - } + } } } } diff --git a/Source/Modules/InputHandler_HandleStates.swift b/Source/Modules/InputHandler_HandleStates.swift index 2e3b2bc8..c8cce402 100644 --- a/Source/Modules/InputHandler_HandleStates.swift +++ b/Source/Modules/InputHandler_HandleStates.swift @@ -23,7 +23,7 @@ extension InputHandler { /// 換成由此處重新生成的原始資料在 IMEStateData 當中生成的 NSAttributeString。 var displayTextSegments: [String] = compositor.walkedNodes.values var cursor = convertCursorForDisplay(compositor.cursor) - let reading: String = sansReading ? "" : readingForDisplay // 先提出來,減輕運算負擔。 + let reading: String = sansReading ? "" : readingForDisplay // 先提出來,減輕運算負擔。 if !reading.isEmpty { var newDisplayTextSegments = [String]() var temporaryNode = "" @@ -45,7 +45,7 @@ extension InputHandler { displayTextSegments = newDisplayTextSegments cursor += reading.count } - for i in 0.. 0」,因為該整數變數只要「!isEmpty」那就必定滿足這個條件。 switch inputting.displayedText.isEmpty { - case false: delegate.switchState(inputting) - case true: delegate.switchState(IMEState.ofAbortion()) + case false: delegate.switchState(inputting) + case true: delegate.switchState(IMEState.ofAbortion()) } return true } @@ -554,8 +554,8 @@ extension InputHandler { /// 如果注拼槽或組筆區不是空的話,則清空之。 clearComposerAndCalligrapher() switch compositor.isEmpty { - case false: delegate.switchState(generateStateOfInputting()) - case true: delegate.switchState(IMEState.ofAbortion()) + case false: delegate.switchState(generateStateOfInputting()) + case true: delegate.switchState(IMEState.ofAbortion()) } } return true @@ -708,7 +708,7 @@ extension InputHandler { } guard let region = compositor.walkedNodes.cursorRegionMap[cursorForCandidate], - compositor.walkedNodes.count > region + compositor.walkedNodes.count > region else { delegate.callError("1CE6FFBD") return true @@ -730,7 +730,7 @@ extension InputHandler { result.revolveAsIndex(with: candidates, clockwise: !(candidate == currentPaired && reverseOrder)) if candidate == currentPaired { break } } - return (0.. 1 { diff --git a/Source/Modules/LMMgr.swift b/Source/Modules/LMMgr.swift index a126e03d..786b3223 100644 --- a/Source/Modules/LMMgr.swift +++ b/Source/Modules/LMMgr.swift @@ -32,23 +32,23 @@ public class LMMgr { public static var currentLM: vChewingLM.LMInstantiator { switch IMEApp.currentInputMode { - case .imeModeCHS: - return Self.lmCHS - case .imeModeCHT: - return Self.lmCHT - case .imeModeNULL: - return .init() + case .imeModeCHS: + return Self.lmCHS + case .imeModeCHT: + return Self.lmCHT + case .imeModeNULL: + return .init() } } public static var currentUOM: vChewingLM.LMUserOverride { switch IMEApp.currentInputMode { - case .imeModeCHS: - return Self.uomCHS - case .imeModeCHT: - return Self.uomCHT - case .imeModeNULL: - return .init(dataURL: Self.userOverrideModelDataURL(IMEApp.currentInputMode)) + case .imeModeCHS: + return Self.uomCHS + case .imeModeCHT: + return Self.uomCHT + case .imeModeNULL: + return .init(dataURL: Self.userOverrideModelDataURL(IMEApp.currentInputMode)) } } @@ -133,56 +133,56 @@ public class LMMgr { group.enter() globalQueue.async { switch mode { - case .imeModeCHS: - if !Self.lmCHS.isCNSDataLoaded { - Self.lmCHS.loadCNSData(plist: Self.getDictionaryData("data-cns")) - } - if !Self.lmCHS.isMiscDataLoaded { - Self.lmCHS.loadMiscData(plist: Self.getDictionaryData("data-zhuyinwen")) - } - if !Self.lmCHS.isSymbolDataLoaded { - Self.lmCHS.loadSymbolData(plist: Self.getDictionaryData("data-symbols")) - } - case .imeModeCHT: - if !Self.lmCHT.isCNSDataLoaded { - Self.lmCHT.loadCNSData(plist: Self.getDictionaryData("data-cns")) - } - if !Self.lmCHT.isMiscDataLoaded { - Self.lmCHT.loadMiscData(plist: Self.getDictionaryData("data-zhuyinwen")) - } - if !Self.lmCHT.isSymbolDataLoaded { - Self.lmCHT.loadSymbolData(plist: Self.getDictionaryData("data-symbols")) - } - default: break + case .imeModeCHS: + if !Self.lmCHS.isCNSDataLoaded { + Self.lmCHS.loadCNSData(plist: Self.getDictionaryData("data-cns")) + } + if !Self.lmCHS.isMiscDataLoaded { + Self.lmCHS.loadMiscData(plist: Self.getDictionaryData("data-zhuyinwen")) + } + if !Self.lmCHS.isSymbolDataLoaded { + Self.lmCHS.loadSymbolData(plist: Self.getDictionaryData("data-symbols")) + } + case .imeModeCHT: + if !Self.lmCHT.isCNSDataLoaded { + Self.lmCHT.loadCNSData(plist: Self.getDictionaryData("data-cns")) + } + if !Self.lmCHT.isMiscDataLoaded { + Self.lmCHT.loadMiscData(plist: Self.getDictionaryData("data-zhuyinwen")) + } + if !Self.lmCHT.isSymbolDataLoaded { + Self.lmCHT.loadSymbolData(plist: Self.getDictionaryData("data-symbols")) + } + default: break } group.leave() } switch mode { - case .imeModeCHS: - if !Self.lmCHS.isCoreLMLoaded { - showFinishNotification = true - Notifier.notify( - message: NSLocalizedString("Loading CHS Core Dict...", comment: "") - ) - group.enter() - globalQueue.async { - loadCoreLanguageModelFile(filenameSansExtension: "data-chs", langModel: &Self.lmCHS) - group.leave() - } + case .imeModeCHS: + if !Self.lmCHS.isCoreLMLoaded { + showFinishNotification = true + Notifier.notify( + message: NSLocalizedString("Loading CHS Core Dict...", comment: "") + ) + group.enter() + globalQueue.async { + loadCoreLanguageModelFile(filenameSansExtension: "data-chs", langModel: &Self.lmCHS) + group.leave() } - case .imeModeCHT: - if !Self.lmCHT.isCoreLMLoaded { - showFinishNotification = true - Notifier.notify( - message: NSLocalizedString("Loading CHT Core Dict...", comment: "") - ) - group.enter() - globalQueue.async { - loadCoreLanguageModelFile(filenameSansExtension: "data-cht", langModel: &Self.lmCHT) - group.leave() - } + } + case .imeModeCHT: + if !Self.lmCHT.isCoreLMLoaded { + showFinishNotification = true + Notifier.notify( + message: NSLocalizedString("Loading CHT Core Dict...", comment: "") + ) + group.enter() + globalQueue.async { + loadCoreLanguageModelFile(filenameSansExtension: "data-cht", langModel: &Self.lmCHT) + group.leave() } - default: break + } + default: break } group.notify(queue: DispatchQueue.main) { if showFinishNotification { @@ -234,26 +234,26 @@ public class LMMgr { return } switch type { - case .thePhrases, .theFilter: - Self.lmCHT.loadUserPhrasesData( - path: userDictDataURL(mode: .imeModeCHT, type: .thePhrases).path, - filterPath: userDictDataURL(mode: .imeModeCHT, type: .theFilter).path - ) - Self.lmCHS.loadUserPhrasesData( - path: userDictDataURL(mode: .imeModeCHS, type: .thePhrases).path, - filterPath: userDictDataURL(mode: .imeModeCHS, type: .theFilter).path - ) - case .theReplacements: - if PrefMgr.shared.phraseReplacementEnabled { Self.loadUserPhraseReplacement() } - case .theAssociates: - if PrefMgr.shared.associatedPhrasesEnabled { Self.loadUserAssociatesData() } - case .theSymbols: - Self.lmCHT.loadUserSymbolData( - path: Self.userDictDataURL(mode: .imeModeCHT, type: .theSymbols).path - ) - Self.lmCHS.loadUserSymbolData( - path: Self.userDictDataURL(mode: .imeModeCHS, type: .theSymbols).path - ) + case .thePhrases, .theFilter: + Self.lmCHT.loadUserPhrasesData( + path: userDictDataURL(mode: .imeModeCHT, type: .thePhrases).path, + filterPath: userDictDataURL(mode: .imeModeCHT, type: .theFilter).path + ) + Self.lmCHS.loadUserPhrasesData( + path: userDictDataURL(mode: .imeModeCHS, type: .thePhrases).path, + filterPath: userDictDataURL(mode: .imeModeCHS, type: .theFilter).path + ) + case .theReplacements: + if PrefMgr.shared.phraseReplacementEnabled { Self.loadUserPhraseReplacement() } + case .theAssociates: + if PrefMgr.shared.associatedPhrasesEnabled { Self.loadUserAssociatesData() } + case .theSymbols: + Self.lmCHT.loadUserSymbolData( + path: Self.userDictDataURL(mode: .imeModeCHT, type: .theSymbols).path + ) + Self.lmCHS.loadUserSymbolData( + path: Self.userDictDataURL(mode: .imeModeCHS, type: .theSymbols).path + ) } } @@ -291,15 +291,15 @@ public class LMMgr { factoryDictionaryOnly: Bool = false ) -> Bool { switch mode { - case .imeModeCHS: - return lmCHS.hasKeyValuePairFor( - keyArray: [unigramKey], value: userPhrase, factoryDictionaryOnly: factoryDictionaryOnly - ) - case .imeModeCHT: - return lmCHT.hasKeyValuePairFor( - keyArray: [unigramKey], value: userPhrase, factoryDictionaryOnly: factoryDictionaryOnly - ) - case .imeModeNULL: return false + case .imeModeCHS: + return lmCHS.hasKeyValuePairFor( + keyArray: [unigramKey], value: userPhrase, factoryDictionaryOnly: factoryDictionaryOnly + ) + case .imeModeCHT: + return lmCHT.hasKeyValuePairFor( + keyArray: [unigramKey], value: userPhrase, factoryDictionaryOnly: factoryDictionaryOnly + ) + case .imeModeNULL: return false } } @@ -386,8 +386,8 @@ public class LMMgr { let result = factory - ? getPlistData(url: factoryResultURL) - : getPlistData(url: containerResultURL) ?? getPlistData(url: factoryResultURL) + ? getPlistData(url: factoryResultURL) + : getPlistData(url: containerResultURL) ?? getPlistData(url: factoryResultURL) if result == nil { vCLog("↑ Exception happened when reading plist file at: \(lastReadPath).") } @@ -406,11 +406,11 @@ public class LMMgr { public static func userDictDataURL(mode: Shared.InputMode, type: vChewingLM.ReplacableUserDataType) -> URL { var fileName: String = { switch type { - case .thePhrases: return "userdata" - case .theFilter: return "exclude-phrases" - case .theReplacements: return "phrases-replacement" - case .theAssociates: return "associatedPhrases" - case .theSymbols: return "usersymbolphrases" + case .thePhrases: return "userdata" + case .theFilter: return "exclude-phrases" + case .theReplacements: return "phrases-replacement" + case .theAssociates: return "associatedPhrases" + case .theSymbols: return "usersymbolphrases" } }() fileName.append((mode == .imeModeCHT) ? "-cht.txt" : "-chs.txt") @@ -439,9 +439,9 @@ public class LMMgr { public static func userOverrideModelDataURL(_ mode: Shared.InputMode) -> URL { let fileName: String = { switch mode { - case .imeModeCHS: return "vChewing_override-model-data-chs.dat" - case .imeModeCHT: return "vChewing_override-model-data-cht.dat" - case .imeModeNULL: return "vChewing_override-model-data-dummy.dat" + case .imeModeCHS: return "vChewing_override-model-data-chs.dat" + case .imeModeCHT: return "vChewing_override-model-data-cht.dat" + case .imeModeNULL: return "vChewing_override-model-data-dummy.dat" } }() @@ -498,12 +498,12 @@ public class LMMgr { private static func templateName(for type: vChewingLM.ReplacableUserDataType, mode: Shared.InputMode) -> String { switch type { - case .thePhrases: return kTemplateNameUserPhrases - case .theFilter: return kTemplateNameUserFilterList - case .theReplacements: return kTemplateNameUserReplacements - case .theSymbols: return kTemplateNameUserSymbolPhrases - case .theAssociates: - return mode == .imeModeCHS ? kTemplateNameUserAssociatesCHS : kTemplateNameUserAssociatesCHT + case .thePhrases: return kTemplateNameUserPhrases + case .theFilter: return kTemplateNameUserFilterList + case .theReplacements: return kTemplateNameUserReplacements + case .theSymbols: return kTemplateNameUserSymbolPhrases + case .theAssociates: + return mode == .imeModeCHS ? kTemplateNameUserAssociatesCHS : kTemplateNameUserAssociatesCHT } } @@ -517,7 +517,7 @@ public class LMMgr { // 路徑沒有結尾斜槓的話,會導致目錄合規性判定失準。 // 出於每個型別每個函式的自我責任原則,這裡多檢查一遍也不壞。 - var folderPath = folderPath // Convert the incoming constant to a variable. + var folderPath = folderPath // Convert the incoming constant to a variable. if isFolder.boolValue { folderPath?.ensureTrailingSlash() } @@ -654,8 +654,8 @@ public class LMMgr { } if let writeFile = FileHandle(forUpdatingAtPath: theURL.path), - let data = currentMarkedPhrase.data(using: .utf8), - let endl = "\n".data(using: .utf8) + let data = currentMarkedPhrase.data(using: .utf8), + let endl = "\n".data(using: .utf8) { writeFile.seekToEndOfFile() writeFile.write(endl) @@ -725,35 +725,35 @@ public class LMMgr { if !Self.checkIfUserFilesExistBeforeOpening() { return } DispatchQueue.main.async { switch app { - case "vim": - let process = Process() - let pipe = Pipe() - process.executableURL = URL(fileURLWithPath: "/bin/sh/") - process.arguments = ["-c", "open '/usr/bin/vim'", "'\(url.path)'"] - process.standardOutput = pipe - process.standardError = pipe - process.terminationHandler = { process in - vCLog("\ndidFinish: \(!process.isRunning)") - } - let fileHandle = pipe.fileHandleForReading - do { - try process.run() - } catch { - NSWorkspace.shared.openFile(url.path, withApplication: "TextEdit") - } - do { - if let theData = try fileHandle.readToEnd(), - let outStr = String(data: theData, encoding: .utf8) - { - vCLog(outStr) - } - } catch {} - case "Finder": - NSWorkspace.shared.activateFileViewerSelecting([url]) - default: - if !NSWorkspace.shared.openFile(url.path, withApplication: app) { - NSWorkspace.shared.openFile(url.path, withApplication: "TextEdit") + case "vim": + let process = Process() + let pipe = Pipe() + process.executableURL = URL(fileURLWithPath: "/bin/sh/") + process.arguments = ["-c", "open '/usr/bin/vim'", "'\(url.path)'"] + process.standardOutput = pipe + process.standardError = pipe + process.terminationHandler = { process in + vCLog("\ndidFinish: \(!process.isRunning)") + } + let fileHandle = pipe.fileHandleForReading + do { + try process.run() + } catch { + NSWorkspace.shared.openFile(url.path, withApplication: "TextEdit") + } + do { + if let theData = try fileHandle.readToEnd(), + let outStr = String(data: theData, encoding: .utf8) + { + vCLog(outStr) } + } catch {} + case "Finder": + NSWorkspace.shared.activateFileViewerSelecting([url]) + default: + if !NSWorkspace.shared.openFile(url.path, withApplication: app) { + NSWorkspace.shared.openFile(url.path, withApplication: "TextEdit") + } } } } @@ -779,34 +779,34 @@ public class LMMgr { public static func bleachSpecifiedSuggestions(targets: [String], mode: Shared.InputMode) { switch mode { - case .imeModeCHS: - Self.uomCHT.bleachSpecifiedSuggestions(targets: targets, saveCallback: { Self.uomCHT.saveData() }) - case .imeModeCHT: - Self.uomCHS.bleachSpecifiedSuggestions(targets: targets, saveCallback: { Self.uomCHS.saveData() }) - case .imeModeNULL: - break + case .imeModeCHS: + Self.uomCHT.bleachSpecifiedSuggestions(targets: targets, saveCallback: { Self.uomCHT.saveData() }) + case .imeModeCHT: + Self.uomCHS.bleachSpecifiedSuggestions(targets: targets, saveCallback: { Self.uomCHS.saveData() }) + case .imeModeNULL: + break } } public static func removeUnigramsFromUserOverrideModel(_ mode: Shared.InputMode) { switch mode { - case .imeModeCHS: - Self.uomCHT.bleachUnigrams(saveCallback: { Self.uomCHT.saveData() }) - case .imeModeCHT: - Self.uomCHS.bleachUnigrams(saveCallback: { Self.uomCHS.saveData() }) - case .imeModeNULL: - break + case .imeModeCHS: + Self.uomCHT.bleachUnigrams(saveCallback: { Self.uomCHT.saveData() }) + case .imeModeCHT: + Self.uomCHS.bleachUnigrams(saveCallback: { Self.uomCHS.saveData() }) + case .imeModeNULL: + break } } public static func clearUserOverrideModelData(_ mode: Shared.InputMode = .imeModeNULL) { switch mode { - case .imeModeCHS: - Self.uomCHS.clearData(withURL: userOverrideModelDataURL(.imeModeCHS)) - case .imeModeCHT: - Self.uomCHT.clearData(withURL: userOverrideModelDataURL(.imeModeCHT)) - case .imeModeNULL: - break + case .imeModeCHS: + Self.uomCHS.clearData(withURL: userOverrideModelDataURL(.imeModeCHS)) + case .imeModeCHT: + Self.uomCHT.clearData(withURL: userOverrideModelDataURL(.imeModeCHT)) + case .imeModeNULL: + break } } } @@ -863,31 +863,31 @@ extension LMMgr: PhraseEditorDelegate { public func tagOverrides(in strProcessed: inout String, mode: Shared.InputMode) { let outputStack: NSMutableString = .init() switch mode { - case .imeModeCHT: - if !Self.lmCHT.isCoreLMLoaded { - Notifier.notify( - message: NSLocalizedString("Loading CHT Core Dict...", comment: "") - ) - Self.loadCoreLanguageModelFile( - filenameSansExtension: "data-cht", langModel: &Self.lmCHT - ) - Notifier.notify( - message: NSLocalizedString("Core Dict loading complete.", comment: "") - ) - } - case .imeModeCHS: - if !Self.lmCHS.isCoreLMLoaded { - Notifier.notify( - message: NSLocalizedString("Loading CHS Core Dict...", comment: "") - ) - Self.loadCoreLanguageModelFile( - filenameSansExtension: "data-chs", langModel: &Self.lmCHS - ) - Notifier.notify( - message: NSLocalizedString("Core Dict loading complete.", comment: "") - ) - } - case .imeModeNULL: return + case .imeModeCHT: + if !Self.lmCHT.isCoreLMLoaded { + Notifier.notify( + message: NSLocalizedString("Loading CHT Core Dict...", comment: "") + ) + Self.loadCoreLanguageModelFile( + filenameSansExtension: "data-cht", langModel: &Self.lmCHT + ) + Notifier.notify( + message: NSLocalizedString("Core Dict loading complete.", comment: "") + ) + } + case .imeModeCHS: + if !Self.lmCHS.isCoreLMLoaded { + Notifier.notify( + message: NSLocalizedString("Loading CHS Core Dict...", comment: "") + ) + Self.loadCoreLanguageModelFile( + filenameSansExtension: "data-chs", langModel: &Self.lmCHS + ) + Notifier.notify( + message: NSLocalizedString("Core Dict loading complete.", comment: "") + ) + } + case .imeModeNULL: return } for currentLine in strProcessed.split(separator: "\n") { let arr = currentLine.split(separator: " ") diff --git a/Source/Modules/PrefMgr_Core.swift b/Source/Modules/PrefMgr_Core.swift index ad82a1d8..5a24d1e8 100644 --- a/Source/Modules/PrefMgr_Core.swift +++ b/Source/Modules/PrefMgr_Core.swift @@ -69,7 +69,7 @@ public class PrefMgr: PrefMgrProtocol { public var candidateListTextSize: Double { didSet { // 必須確立條件,否則就會是無限迴圈。 - if !(12...196).contains(candidateListTextSize) { + if !(12 ... 196).contains(candidateListTextSize) { candidateListTextSize = max(12, min(candidateListTextSize, 196)) } } @@ -190,21 +190,21 @@ public class PrefMgr: PrefMgrProtocol { @AppProperty(key: UserDef.kCNS11643Enabled.rawValue, defaultValue: false) public var cns11643Enabled: Bool { didSet { - LMMgr.setCNSEnabled(cns11643Enabled) // 很重要 + LMMgr.setCNSEnabled(cns11643Enabled) // 很重要 } } @AppProperty(key: UserDef.kSymbolInputEnabled.rawValue, defaultValue: true) public var symbolInputEnabled: Bool { didSet { - LMMgr.setSymbolEnabled(symbolInputEnabled) // 很重要 + LMMgr.setSymbolEnabled(symbolInputEnabled) // 很重要 } } @AppProperty(key: UserDef.kCassetteEnabled.rawValue, defaultValue: false) public var cassetteEnabled: Bool { didSet { - LMMgr.setCassetteEnabled(cassetteEnabled) // 很重要 + LMMgr.setCassetteEnabled(cassetteEnabled) // 很重要 } } @@ -272,7 +272,7 @@ public class PrefMgr: PrefMgrProtocol { if candidateKeys != candidateKeys.deduplicated { candidateKeys = candidateKeys.deduplicated } - if !(6...9).contains(candidateKeys.count) { + if !(6 ... 9).contains(candidateKeys.count) { candidateKeys = Self.kDefaultCandidateKeys } } diff --git a/Source/Modules/PrefMgr_Extension.swift b/Source/Modules/PrefMgr_Extension.swift index 6e696f17..894e824c 100644 --- a/Source/Modules/PrefMgr_Extension.swift +++ b/Source/Modules/PrefMgr_Extension.swift @@ -10,8 +10,8 @@ import Shared // MARK: Auto parameter fix procedures, executed everytime on SessionCtl.activateServer(). -extension PrefMgr { - public func fixOddPreferences() { +public extension PrefMgr { + func fixOddPreferences() { // macOS 10.15 開始才能使用 SwiftUI 構建的田所選字窗。 if #unavailable(macOS 10.15) { useIMKCandidateWindow = true diff --git a/Source/Modules/SessionCtl_Core.swift b/Source/Modules/SessionCtl_Core.swift index ceb3149a..13f989c6 100644 --- a/Source/Modules/SessionCtl_Core.swift +++ b/Source/Modules/SessionCtl_Core.swift @@ -23,7 +23,7 @@ import UpdateSputnik /// 檢查委任物件是否實現了方法:若存在的話,就調用委任物件內的版本。 /// - Remark: 在輸入法的主函式中分配的 IMKServer 型別為客體應用程式創建的每個 /// 輸入會話創建一個控制器型別。因此,對於每個輸入會話,都有一個對應的 IMKInputController。 -@objc(SessionCtl) // 必須加上 ObjC,因為 IMK 是用 ObjC 寫的。 +@objc(SessionCtl) // 必須加上 ObjC,因為 IMK 是用 ObjC 寫的。 public class SessionCtl: IMKInputController { /// 標記狀態來聲明目前新增的詞彙是否需要賦以非常低的權重。 public static var areWeNerfing = false @@ -65,8 +65,8 @@ public class SessionCtl: IMKInputController { } } - private var isASCIIModeForThisClient = false // 給每個副本用的。 - private static var isASCIIModeForAllClients = false // 給所有副本共用的。 + private var isASCIIModeForThisClient = false // 給每個副本用的。 + private static var isASCIIModeForAllClients = false // 給所有副本共用的。 /// 輸入調度模組的副本。 var inputHandler: InputHandlerProtocol? @@ -130,7 +130,7 @@ public class SessionCtl: IMKInputController { resetInputHandler(forceComposerCleanup: true) // ---------------------------- /// 重設所有語言模組。這裡不需要做按需重設,因為對運算量沒有影響。 - inputHandler?.currentLM = LMMgr.currentLM // 會自動更新組字引擎內的模組。 + inputHandler?.currentLM = LMMgr.currentLM // 會自動更新組字引擎內的模組。 inputHandler?.currentUOM = LMMgr.currentUOM /// 清空注拼槽+同步最新的注拼槽排列設定。 inputHandler?.ensureKeyboardParser() @@ -175,9 +175,9 @@ public class SessionCtl: IMKInputController { // MARK: - 工具函式 -extension SessionCtl { +public extension SessionCtl { /// 強制重設當前鍵盤佈局、使其與偏好設定同步。 - public func setKeyLayout() { + func setKeyLayout() { guard let client = client(), !isServingIMEItself else { return } DispatchQueue.main.async { [self] in @@ -190,7 +190,7 @@ extension SessionCtl { } /// 重設輸入調度模組,會將當前尚未遞交的內容遞交出去。 - public func resetInputHandler(forceComposerCleanup forceCleanup: Bool = false) { + func resetInputHandler(forceComposerCleanup forceCleanup: Bool = false) { guard let inputHandler = inputHandler else { return } var textToCommit = "" // 過濾掉尚未完成拼寫的注音。 @@ -207,11 +207,11 @@ extension SessionCtl { // MARK: - IMKStateSetting 協定規定的方法 -extension SessionCtl { +public extension SessionCtl { /// 啟用輸入法時,會觸發該函式。 /// - Parameter sender: 呼叫了該函式的客體。 - public override func activateServer(_ sender: Any!) { - _ = sender // 防止格式整理工具毀掉與此對應的參數。 + override func activateServer(_ sender: Any!) { + _ = sender // 防止格式整理工具毀掉與此對應的參數。 DispatchQueue.main.async { [self] in if let senderBundleID: String = (sender as? IMKTextInput)?.bundleIdentifier() { vCLog("activateServer(\(senderBundleID))") @@ -247,18 +247,18 @@ extension SessionCtl { } state = IMEState.ofEmpty() - isActivated = true // 登記啟用狀態。 + isActivated = true // 登記啟用狀態。 setKeyLayout() } } /// 停用輸入法時,會觸發該函式。 /// - Parameter sender: 呼叫了該函式的客體(無須使用)。 - public override func deactivateServer(_ sender: Any!) { - _ = sender // 防止格式整理工具毀掉與此對應的參數。 + override func deactivateServer(_ sender: Any!) { + _ = sender // 防止格式整理工具毀掉與此對應的參數。 DispatchQueue.main.async { [self] in isActivated = false - resetInputHandler() // 這條會自動搞定 Empty 狀態。 + resetInputHandler() // 這條會自動搞定 Empty 狀態。 switchState(IMEState.ofDeactivated()) inputHandler = nil // IMK 選字窗可以不用 nil,不然反而會出問題。反正 IMK 選字窗記憶體開銷可以不計。 @@ -275,16 +275,16 @@ extension SessionCtl { /// - value: 輸入法在系統偏好設定當中的副本的 identifier,與 bundle identifier 類似。在輸入法的 info.plist 內定義。 /// - tag: 標記(無須使用)。 /// - sender: 呼叫了該函式的客體(無須使用)。 - public override func setValue(_ value: Any!, forTag tag: Int, client sender: Any!) { - _ = tag // 防止格式整理工具毀掉與此對應的參數。 - _ = sender // 防止格式整理工具毀掉與此對應的參數。 + override func setValue(_ value: Any!, forTag tag: Int, client sender: Any!) { + _ = tag // 防止格式整理工具毀掉與此對應的參數。 + _ = sender // 防止格式整理工具毀掉與此對應的參數。 DispatchQueue.main.async { [self] in inputMode = .init(rawValue: value as? String ?? PrefMgr.shared.mostRecentInputMode) ?? .imeModeNULL } } /// 將輸入法偏好設定同步至語言模組內。 - public func syncBaseLMPrefs() { + func syncBaseLMPrefs() { LMMgr.currentLM.isPhraseReplacementEnabled = PrefMgr.shared.phraseReplacementEnabled LMMgr.currentLM.isCNSEnabled = PrefMgr.shared.cns11643Enabled LMMgr.currentLM.isSymbolEnabled = PrefMgr.shared.symbolInputEnabled @@ -298,7 +298,7 @@ extension SessionCtl { // 註:handle(_ event:) 位於 SessionCtl_HandleEvent.swift。 -extension SessionCtl { +public extension SessionCtl { /// 該函式的回饋結果決定了輸入法會攔截且捕捉哪些類型的輸入裝置操作事件。 /// /// 一個客體應用會與輸入法共同確認某個輸入裝置操作事件是否可以觸發輸入法內的某個方法。預設情況下, @@ -308,8 +308,8 @@ extension SessionCtl { /// 「`commitComposition(_ message)`」遞交給客體。 /// - Parameter sender: 呼叫了該函式的客體(無須使用)。 /// - Returns: 返回一個 uint,其中承載了與系統 NSEvent 操作事件有關的掩碼集合(詳見 NSEvent.h)。 - public override func recognizedEvents(_ sender: Any!) -> Int { - _ = sender // 防止格式整理工具毀掉與此對應的參數。 + override func recognizedEvents(_ sender: Any!) -> Int { + _ = sender // 防止格式整理工具毀掉與此對應的參數。 let events: NSEvent.EventTypeMask = [.keyDown, .flagsChanged] return Int(events.rawValue) } @@ -318,8 +318,8 @@ extension SessionCtl { /// 也就是說 handle(event:) 完全抓不到這個 Event。 /// 這時需要在 commitComposition 這一關做一些收尾處理。 /// - Parameter sender: 呼叫了該函式的客體(無須使用)。 - public override func commitComposition(_ sender: Any!) { - _ = sender // 防止格式整理工具毀掉與此對應的參數。 + override func commitComposition(_ sender: Any!) { + _ = sender // 防止格式整理工具毀掉與此對應的參數。 resetInputHandler() clearInlineDisplay() // super.commitComposition(sender) // 這句不要引入,否則每次切出輸入法時都會死當。 @@ -328,15 +328,15 @@ extension SessionCtl { /// 指定輸入法要遞交出去的內容(雖然 InputMethodKit 可能並不會真的用到這個函式)。 /// - Parameter sender: 呼叫了該函式的客體(無須使用)。 /// - Returns: 字串內容,或者 nil。 - public override func composedString(_ sender: Any!) -> Any! { - _ = sender // 防止格式整理工具毀掉與此對應的參數。 + override func composedString(_ sender: Any!) -> Any! { + _ = sender // 防止格式整理工具毀掉與此對應的參數。 guard state.hasComposition else { return "" } return state.displayedTextConverted } /// 輸入法要被換掉或關掉的時候,要做的事情。 /// 不過好像因為 IMK 的 Bug 而並不會被執行。 - public override func inputControllerWillClose() { + override func inputControllerWillClose() { // 下述兩行用來防止尚未完成拼寫的注音內容被遞交出去。 resetInputHandler() super.inputControllerWillClose() diff --git a/Source/Modules/SessionCtl_Delegates.swift b/Source/Modules/SessionCtl_Delegates.swift index 2b6db1f8..51ede93c 100644 --- a/Source/Modules/SessionCtl_Delegates.swift +++ b/Source/Modules/SessionCtl_Delegates.swift @@ -85,8 +85,8 @@ extension SessionCtl: CtlCandidateDelegate { let blankResult: [String] = [] // 這一段專門處理「反查」。 if !PrefMgr.shared.showReverseLookupInCandidateUI { return blankResult } - if isVerticalTyping { return blankResult } // 縱排輸入的場合,選字窗沒有足夠的空間顯示反查結果。 - if value.isEmpty { return blankResult } // 空字串沒有需要反查的東西。 + if isVerticalTyping { return blankResult } // 縱排輸入的場合,選字窗沒有足夠的空間顯示反查結果。 + if value.isEmpty { return blankResult } // 空字串沒有需要反查的東西。 if value.contains("_") { return blankResult } // 因為威注音輸入法的反查結果僅由磁帶模組負責,所以相關運算挪至 LMInstantiator 內處理。 return LMMgr.currentLM.cassetteReverseLookup(for: value) @@ -112,10 +112,10 @@ extension SessionCtl: CtlCandidateDelegate { public func candidatePairSelected(at index: Int) { guard let inputHandler = inputHandler else { return } - if state.type == .ofSymbolTable, (0.. NSRect { + func lineHeightRect(zeroCursor: Bool = false) -> NSRect { var lineHeightRect = NSRect.seniorTheBeast guard let client = client() else { return lineHeightRect @@ -40,7 +40,7 @@ extension SessionCtl { return lineHeightRect } - public func showTooltip(_ tooltip: String, duration: Double = 0) { + func showTooltip(_ tooltip: String, duration: Double = 0) { guard client() != nil else { return } if tooltip.isEmpty { tooltipInstance.hide() @@ -49,7 +49,7 @@ extension SessionCtl { updateVerticalTypingStatus() let lineHeightRect = lineHeightRect() var finalOrigin: NSPoint = lineHeightRect.origin - let delta: Double = lineHeightRect.size.height + 4.0 // bottomOutOfScreenAdjustmentHeight + let delta: Double = lineHeightRect.size.height + 4.0 // bottomOutOfScreenAdjustmentHeight if isVerticalTyping { finalOrigin = NSPoint( x: lineHeightRect.origin.x + lineHeightRect.size.width + 5, y: lineHeightRect.origin.y @@ -72,7 +72,7 @@ extension SessionCtl { ) } - public func showCandidates() { + func showCandidates() { guard client() != nil else { return } updateVerticalTypingStatus() isVerticalCandidateWindow = (isVerticalTyping || !PrefMgr.shared.useHorizontalCandidateList) @@ -80,8 +80,8 @@ extension SessionCtl { /// 無論是田所選字窗還是 IMK 選字窗,在這裡都有必要重新初期化。 let candidateLayout: NSUserInterfaceLayoutOrientation = ((isVerticalTyping || !PrefMgr.shared.useHorizontalCandidateList) - ? .vertical - : .horizontal) + ? .vertical + : .horizontal) /// 先取消既有的選字窗的內容顯示。否則可能會重複生成選字窗的 NSWindow()。 candidateUI?.visible = false @@ -89,7 +89,7 @@ extension SessionCtl { if #available(macOS 10.15, *) { candidateUI = PrefMgr.shared.useIMKCandidateWindow - ? CtlCandidateIMK(candidateLayout) : CtlCandidateTDK(candidateLayout) + ? CtlCandidateIMK(candidateLayout) : CtlCandidateTDK(candidateLayout) if let candidateTDK = candidateUI as? CtlCandidateTDK { candidateTDK.maxLinesPerPage = isVerticalTyping ? 1 : 3 } @@ -114,13 +114,13 @@ extension SessionCtl { candidateUI?.useLangIdentifier = PrefMgr.shared.handleDefaultCandidateFontsByLangIdentifier candidateUI?.locale = { switch inputMode { - case .imeModeCHS: return "zh-Hans" - case .imeModeCHT: - if !PrefMgr.shared.shiftJISShinjitaiOutputEnabled, !PrefMgr.shared.chineseConversionEnabled { - return "zh-Hant" - } - return "ja" - default: return "" + case .imeModeCHS: return "zh-Hans" + case .imeModeCHT: + if !PrefMgr.shared.shiftJISShinjitaiOutputEnabled, !PrefMgr.shared.chineseConversionEnabled { + return "zh-Hant" + } + return "ja" + default: return "" } }() @@ -131,7 +131,7 @@ extension SessionCtl { } } - candidateUI?.delegate = self // 會自動觸發田所選字窗的資料重載。 + candidateUI?.delegate = self // 會自動觸發田所選字窗的資料重載。 candidateUI?.visible = true if isVerticalTyping { @@ -170,20 +170,19 @@ extension SessionCtl { /// 5) Do NOT enable either KangXi conversion mode nor JIS conversion mode. They are disabled by default. /// 6) Expecting the glyph differences of the candidate "骨" between PingFang SC and PingFang TC when rendering /// the candidate window in different "vChewing-CHS" and "vChewing-CHT" input modes. - public static func candidateFont(name: String? = nil, size: Double) -> NSFont { - let finalReturnFont: NSFont = - { - switch IMEApp.currentInputMode { - case .imeModeCHS: - return CTFontCreateUIFontForLanguage(.system, size, "zh-Hans" as CFString) - case .imeModeCHT: - return (PrefMgr.shared.shiftJISShinjitaiOutputEnabled || PrefMgr.shared.chineseConversionEnabled) - ? CTFontCreateUIFontForLanguage(.system, size, "ja" as CFString) - : CTFontCreateUIFontForLanguage(.system, size, "zh-Hant" as CFString) - default: - return CTFontCreateUIFontForLanguage(.system, size, nil) - } - }() + static func candidateFont(name: String? = nil, size: Double) -> NSFont { + let finalReturnFont: NSFont = { + switch IMEApp.currentInputMode { + case .imeModeCHS: + return CTFontCreateUIFontForLanguage(.system, size, "zh-Hans" as CFString) + case .imeModeCHT: + return (PrefMgr.shared.shiftJISShinjitaiOutputEnabled || PrefMgr.shared.chineseConversionEnabled) + ? CTFontCreateUIFontForLanguage(.system, size, "ja" as CFString) + : CTFontCreateUIFontForLanguage(.system, size, "zh-Hant" as CFString) + default: + return CTFontCreateUIFontForLanguage(.system, size, nil) + } + }() ?? NSFont.systemFont(ofSize: size) if let name = name, !name.isEmpty { return NSFont(name: name, size: size) ?? finalReturnFont diff --git a/Source/Modules/SessionCtl_HandleEvent.swift b/Source/Modules/SessionCtl_HandleEvent.swift index dfd406bc..b485d95a 100644 --- a/Source/Modules/SessionCtl_HandleEvent.swift +++ b/Source/Modules/SessionCtl_HandleEvent.swift @@ -14,15 +14,15 @@ import Shared // MARK: - Facade -extension SessionCtl { +public extension SessionCtl { /// 接受所有鍵鼠事件為 NSEvent,讓輸入法判斷是否要處理、該怎樣處理。 /// 然後再交給 InputHandler.handleEvent() 分診。 /// - Parameters: /// - event: 裝置操作輸入事件,可能會是 nil。 /// - sender: 呼叫了該函式的客體(無須使用)。 /// - Returns: 回「`true`」以將該按鍵已攔截處理的訊息傳遞給 IMK;回「`false`」則放行、不作處理。 - @objc(handleEvent:client:) public override func handle(_ event: NSEvent!, client sender: Any!) -> Bool { - _ = sender // 防止格式整理工具毀掉與此對應的參數。 + @objc(handleEvent:client:) override func handle(_ event: NSEvent!, client sender: Any!) -> Bool { + _ = sender // 防止格式整理工具毀掉與此對應的參數。 // MARK: 前置處理 @@ -111,7 +111,7 @@ extension SessionCtl { // 如果是方向鍵輸入的話,就想辦法帶上標記資訊、來說明當前是縱排還是橫排。 if event.isUp || event.isDown || event.isLeft || event.isRight { - updateVerticalTypingStatus() // 檢查當前環境是否是縱排輸入。 + updateVerticalTypingStatus() // 檢查當前環境是否是縱排輸入。 eventToDeal = event.reinitiate(charactersIgnoringModifiers: isVerticalTyping ? "Vertical" : "Horizontal") ?? event } @@ -130,7 +130,7 @@ extension SessionCtl { // Apple 數字小鍵盤處理 if eventToDeal.isNumericPadKey, - let eventCharConverted = eventToDeal.characters?.applyingTransform(.fullwidthToHalfwidth, reverse: false) + let eventCharConverted = eventToDeal.characters?.applyingTransform(.fullwidthToHalfwidth, reverse: false) { eventToDeal = eventToDeal.reinitiate(characters: eventCharConverted) ?? eventToDeal } diff --git a/Source/Modules/SessionCtl_HandleStates.swift b/Source/Modules/SessionCtl_HandleStates.swift index a889eb90..5fb7f550 100644 --- a/Source/Modules/SessionCtl_HandleStates.swift +++ b/Source/Modules/SessionCtl_HandleStates.swift @@ -11,7 +11,7 @@ import Shared // MARK: - 狀態調度 (State Handling) -extension SessionCtl { +public extension SessionCtl { /// 針對傳入的新狀態進行調度、且將當前會話控制器的狀態切換至新狀態。 /// /// 先將舊狀態單獨記錄起來,再將新舊狀態作為參數, @@ -22,7 +22,7 @@ extension SessionCtl { /// 不必要的互相干涉、打斷彼此的工作。 /// - Note: 本來不用這麼複雜的,奈何 Swift Protocol 不允許給參數指定預設值。 /// - Parameter newState: 新狀態。 - public func switchState(_ newState: IMEStateProtocol) { + func switchState(_ newState: IMEStateProtocol) { handle(state: newState, replace: true) } @@ -37,50 +37,50 @@ extension SessionCtl { /// - Parameters: /// - newState: 新狀態。 /// - replace: 是否取代現有狀態。 - public func handle(state newState: IMEStateProtocol, replace: Bool) { + func handle(state newState: IMEStateProtocol, replace: Bool) { var previous = state if replace { state = newState } switch newState.type { - case .ofDeactivated: - // 這裡移除一些處理,轉而交給 commitComposition() 代為執行。 - // 這裡不需要 clearInlineDisplay() ,否則會觸發無限迴圈。 - // 對於 IMK 選字窗的顯示狀態糾正的行為交給 inputMode.didSet() 來處理。 - candidateUI?.visible = false - popupCompositionBuffer.hide() - tooltipInstance.hide() - case .ofEmpty, .ofAbortion, .ofCommitting: - innerCircle: switch newState.type { - case .ofAbortion: - previous = IMEState.ofEmpty() - if replace { state = previous } - case .ofCommitting: - commit(text: newState.textToCommit) - if replace { state = IMEState.ofEmpty() } - default: break innerCircle - } - candidateUI?.visible = false - // 全專案用以判斷「.Abortion」的地方僅此一處。 - if previous.hasComposition, ![.ofAbortion, .ofCommitting].contains(newState.type) { - commit(text: previous.displayedText) - } - // 會在工具提示為空的時候自動消除顯示。 - showTooltip(newState.tooltip, duration: newState.tooltipDuration) - clearInlineDisplay() - inputHandler?.clear() - case .ofInputting: - candidateUI?.visible = false + case .ofDeactivated: + // 這裡移除一些處理,轉而交給 commitComposition() 代為執行。 + // 這裡不需要 clearInlineDisplay() ,否則會觸發無限迴圈。 + // 對於 IMK 選字窗的顯示狀態糾正的行為交給 inputMode.didSet() 來處理。 + candidateUI?.visible = false + popupCompositionBuffer.hide() + tooltipInstance.hide() + case .ofEmpty, .ofAbortion, .ofCommitting: + innerCircle: switch newState.type { + case .ofAbortion: + previous = IMEState.ofEmpty() + if replace { state = previous } + case .ofCommitting: commit(text: newState.textToCommit) - setInlineDisplayWithCursor() - // 會在工具提示為空的時候自動消除顯示。 - showTooltip(newState.tooltip, duration: newState.tooltipDuration) - case .ofMarking: - candidateUI?.visible = false - setInlineDisplayWithCursor() - showTooltip(newState.tooltip) - case .ofCandidates, .ofAssociates, .ofSymbolTable: - tooltipInstance.hide() - setInlineDisplayWithCursor() - showCandidates() + if replace { state = IMEState.ofEmpty() } + default: break innerCircle + } + candidateUI?.visible = false + // 全專案用以判斷「.Abortion」的地方僅此一處。 + if previous.hasComposition, ![.ofAbortion, .ofCommitting].contains(newState.type) { + commit(text: previous.displayedText) + } + // 會在工具提示為空的時候自動消除顯示。 + showTooltip(newState.tooltip, duration: newState.tooltipDuration) + clearInlineDisplay() + inputHandler?.clear() + case .ofInputting: + candidateUI?.visible = false + commit(text: newState.textToCommit) + setInlineDisplayWithCursor() + // 會在工具提示為空的時候自動消除顯示。 + showTooltip(newState.tooltip, duration: newState.tooltipDuration) + case .ofMarking: + candidateUI?.visible = false + setInlineDisplayWithCursor() + showTooltip(newState.tooltip) + case .ofCandidates, .ofAssociates, .ofSymbolTable: + tooltipInstance.hide() + setInlineDisplayWithCursor() + showCandidates() } // 浮動組字窗的顯示判定 if newState.hasComposition, PrefMgr.shared.clientsIMKTextInputIncapable.contains(clientBundleIdentifier) { @@ -95,7 +95,7 @@ extension SessionCtl { } /// 如果當前狀態含有「組字結果內容」、或者有選字窗內容、或者存在正在輸入的字根/讀音,則在組字區內顯示游標。 - public func setInlineDisplayWithCursor() { + func setInlineDisplayWithCursor() { /// 所謂選區「selectionRange」,就是「可見游標位置」的位置,只不過長度 /// 是 0 且取代範圍(replacementRange)為「NSNotFound」罷了。 /// 也就是說,內文組字區該在哪裡出現,得由客體軟體來作主。 @@ -106,7 +106,7 @@ extension SessionCtl { } /// 在處理某些「沒有組字區內容顯示」且「不需要攔截某些按鍵處理」的狀態時使用的函式,會清空螢幕上顯示的組字區。 - public func clearInlineDisplay() { + func clearInlineDisplay() { doSetMarkedText( "", selectionRange: NSRange(location: 0, length: 0), replacementRange: NSRange(location: NSNotFound, length: NSNotFound) @@ -136,7 +136,7 @@ extension SessionCtl { } /// 把 setMarkedText 包裝一下,按需啟用 GCD。 - public func doSetMarkedText(_ string: Any!, selectionRange: NSRange, replacementRange: NSRange) { + func doSetMarkedText(_ string: Any!, selectionRange: NSRange, replacementRange: NSRange) { guard isActivated, let client = client() else { return } if isServingIMEItself { DispatchQueue.main.async { diff --git a/Source/Modules/SessionCtl_IMKCandidatesData.swift b/Source/Modules/SessionCtl_IMKCandidatesData.swift index d0b5cecb..8f560807 100644 --- a/Source/Modules/SessionCtl_IMKCandidatesData.swift +++ b/Source/Modules/SessionCtl_IMKCandidatesData.swift @@ -12,12 +12,12 @@ import Tekkon // MARK: - IMKCandidates 功能擴充 -extension SessionCtl { +public extension SessionCtl { /// 生成 IMK 選字窗專用的候選字串陣列。 /// - Parameter sender: 呼叫了該函式的客體(無須使用)。 /// - Returns: IMK 選字窗專用的候選字串陣列。 - public override func candidates(_ sender: Any!) -> [Any]! { - _ = sender // 防止格式整理工具毀掉與此對應的參數。 + override func candidates(_ sender: Any!) -> [Any]! { + _ = sender // 防止格式整理工具毀掉與此對應的參數。 var arrResult = [String]() // 注意:下文中的不可列印字元是用來方便在 IMEState 當中用來分割資料的。 @@ -29,18 +29,18 @@ extension SessionCtl { if arrResult.contains(result) { let reading: String = PrefMgr.shared.cassetteEnabled - ? theCandidate.0.joined(separator: separator) - : (PrefMgr.shared.showHanyuPinyinInCompositionBuffer - ? Tekkon.cnvPhonaToHanyuPinyin( - targetJoined: { - var arr = [String]() - theCandidate.0.forEach { key in - arr.append(Tekkon.restoreToneOneInPhona(target: key)) - } - return arr.joined(separator: "-") - }() - ) - : theCandidate.0.joined(separator: separator)) + ? theCandidate.0.joined(separator: separator) + : (PrefMgr.shared.showHanyuPinyinInCompositionBuffer + ? Tekkon.cnvPhonaToHanyuPinyin( + targetJoined: { + var arr = [String]() + theCandidate.0.forEach { key in + arr.append(Tekkon.restoreToneOneInPhona(target: key)) + } + return arr.joined(separator: "-") + }() + ) + : theCandidate.0.joined(separator: separator)) result = "\(result)\u{17}(\(reading))" } arrResult.append(prefix + result) @@ -55,7 +55,7 @@ extension SessionCtl { } else if state.type == .ofCandidates { guard !state.candidates.isEmpty else { return .init() } if state.candidates[0].0.contains("_punctuation") { - arrResult = state.candidates.map(\.1) // 標點符號選單處理。 + arrResult = state.candidates.map(\.1) // 標點符號選單處理。 } else { handleIMKCandidatesPrepared(state.candidates) } @@ -66,7 +66,7 @@ extension SessionCtl { /// IMK 選字窗限定函式,只要選字窗內的高亮內容選擇出現變化了、就會呼叫這個函式。 /// - Parameter currentSelection: 已經高亮選中的候選字詞內容。 - public override func candidateSelectionChanged(_ currentSelection: NSAttributedString!) { + override func candidateSelectionChanged(_ currentSelection: NSAttributedString!) { guard let currentCandidate = currentSelection?.string, !currentCandidate.isEmpty else { return } let annotation = reverseLookup(for: currentCandidate).joined(separator: "\n") guard !annotation.isEmpty else { return } @@ -79,7 +79,7 @@ extension SessionCtl { /// IMK 選字窗限定函式,只要選字窗確認了某個候選字詞的選擇、就會呼叫這個函式。 /// - Parameter candidateString: 已經確認的候選字詞內容。 - public override func candidateSelected(_ candidateString: NSAttributedString!) { + override func candidateSelected(_ candidateString: NSAttributedString!) { let candidateString: String = candidateString?.string ?? "" if state.type == .ofAssociates { if !PrefMgr.shared.alsoConfirmAssociatedCandidatesByEnter { @@ -98,18 +98,18 @@ extension SessionCtl { let netaShown = (neta.1 == theConverted) ? neta.1 : "\(theConverted)\u{1A}(\(neta.1))" let reading: String = PrefMgr.shared.cassetteEnabled - ? neta.0.joined(separator: separator) - : (PrefMgr.shared.showHanyuPinyinInCompositionBuffer - ? Tekkon.cnvPhonaToHanyuPinyin( - targetJoined: { - var arr = [String]() - neta.0.forEach { key in - arr.append(Tekkon.restoreToneOneInPhona(target: key)) - } - return arr.joined(separator: "-") - }() - ) - : neta.0.joined(separator: separator)) + ? neta.0.joined(separator: separator) + : (PrefMgr.shared.showHanyuPinyinInCompositionBuffer + ? Tekkon.cnvPhonaToHanyuPinyin( + targetJoined: { + var arr = [String]() + neta.0.forEach { key in + arr.append(Tekkon.restoreToneOneInPhona(target: key)) + } + return arr.joined(separator: "-") + }() + ) + : neta.0.joined(separator: separator)) let netaShownWithPronunciation = "\(netaShown)\u{17}(\(reading))" if candidateString == prefix + netaShownWithPronunciation { indexDeducted = i @@ -139,7 +139,7 @@ extension SessionCtl { } else if state.type == .ofCandidates { guard !state.candidates.isEmpty else { return } if state.candidates[0].0.contains("_punctuation") { - handleSymbolCandidatesSelected(state.candidates) // 標點符號選單處理。 + handleSymbolCandidatesSelected(state.candidates) // 標點符號選單處理。 } else { handleIMKCandidatesSelected(state.candidates) } diff --git a/Source/Modules/SessionCtl_Menu.swift b/Source/Modules/SessionCtl_Menu.swift index 54c706ce..c4aae6ce 100644 --- a/Source/Modules/SessionCtl_Menu.swift +++ b/Source/Modules/SessionCtl_Menu.swift @@ -11,8 +11,8 @@ import NotifierUI import SSPreferences import UpdateSputnik -extension Bool { - fileprivate var state: NSControl.StateValue { +private extension Bool { + var state: NSControl.StateValue { self ? .on : .off } } @@ -104,7 +104,7 @@ extension SessionCtl { toggleSymbolInputItem.state = PrefMgr.shared.symbolInputEnabled.state } - menu.addItem(NSMenuItem.separator()) // --------------------- + menu.addItem(NSMenuItem.separator()) // --------------------- menu.addItem( withTitle: "Open User Dictionary Folder".localized, @@ -151,7 +151,7 @@ extension SessionCtl { ) revLookupMenuItem.keyEquivalentModifierMask = [.command, .control] - menu.addItem(NSMenuItem.separator()) // --------------------- + menu.addItem(NSMenuItem.separator()) // --------------------- menu.addItem( withTitle: "Optimize Memorized Phrases".localized, @@ -162,7 +162,7 @@ extension SessionCtl { action: #selector(clearUOM(_:)), keyEquivalent: "" ) - menu.addItem(NSMenuItem.separator()) // --------------------- + menu.addItem(NSMenuItem.separator()) // --------------------- menu.addItem( withTitle: "vChewing Preferences…".localized, @@ -203,8 +203,8 @@ extension SessionCtl { // MARK: - IME Menu Items -extension SessionCtl { - @objc public override func showPreferences(_: Any? = nil) { +public extension SessionCtl { + @objc override func showPreferences(_: Any? = nil) { if #unavailable(macOS 10.15) { CtlPrefWindow.show() } else if NSEvent.modifierFlags.contains(.option) { @@ -217,19 +217,19 @@ extension SessionCtl { NSApp.activate(ignoringOtherApps: true) } - @objc public func showCheatSheet(_: Any? = nil) { + @objc func showCheatSheet(_: Any? = nil) { guard let url = Bundle.main.url(forResource: "shortcuts", withExtension: "html") else { return } DispatchQueue.main.async { NSWorkspace.shared.openFile(url.path, withApplication: "Safari") } } - @objc public func showClientListMgr(_: Any? = nil) { + @objc func showClientListMgr(_: Any? = nil) { CtlClientListMgr.show() NSApp.activate(ignoringOtherApps: true) } - @objc public func toggleCassetteMode(_: Any? = nil) { + @objc func toggleCassetteMode(_: Any? = nil) { resetInputHandler(forceComposerCleanup: true) if !PrefMgr.shared.cassetteEnabled, !LMMgr.checkCassettePathValidity(PrefMgr.shared.cassettePath) { DispatchQueue.main.async { @@ -257,7 +257,7 @@ extension SessionCtl { } } - @objc public func toggleSCPCTypingMode(_: Any? = nil) { + @objc func toggleSCPCTypingMode(_: Any? = nil) { resetInputHandler(forceComposerCleanup: true) Notifier.notify( message: "Per-Char Select Mode".localized + "\n" @@ -267,7 +267,7 @@ extension SessionCtl { ) } - @objc public func toggleChineseConverter(_: Any? = nil) { + @objc func toggleChineseConverter(_: Any? = nil) { resetInputHandler(forceComposerCleanup: true) Notifier.notify( message: "Force KangXi Writing".localized + "\n" @@ -277,7 +277,7 @@ extension SessionCtl { ) } - @objc public func toggleShiftJISShinjitaiOutput(_: Any? = nil) { + @objc func toggleShiftJISShinjitaiOutput(_: Any? = nil) { resetInputHandler(forceComposerCleanup: true) Notifier.notify( message: "JIS Shinjitai Output".localized + "\n" @@ -287,7 +287,7 @@ extension SessionCtl { ) } - @objc public func toggleCurrencyNumerals(_: Any? = nil) { + @objc func toggleCurrencyNumerals(_: Any? = nil) { resetInputHandler(forceComposerCleanup: true) Notifier.notify( message: "Currency Numeral Output".localized + "\n" @@ -297,7 +297,7 @@ extension SessionCtl { ) } - @objc public func toggleHalfWidthPunctuation(_: Any? = nil) { + @objc func toggleHalfWidthPunctuation(_: Any? = nil) { resetInputHandler(forceComposerCleanup: true) Notifier.notify( message: "Half-Width Punctuation Mode".localized + "\n" @@ -307,7 +307,7 @@ extension SessionCtl { ) } - @objc public func toggleCNS11643Enabled(_: Any? = nil) { + @objc func toggleCNS11643Enabled(_: Any? = nil) { resetInputHandler(forceComposerCleanup: true) Notifier.notify( message: "CNS11643 Mode".localized + "\n" @@ -317,7 +317,7 @@ extension SessionCtl { ) } - @objc public func toggleSymbolEnabled(_: Any? = nil) { + @objc func toggleSymbolEnabled(_: Any? = nil) { resetInputHandler(forceComposerCleanup: true) Notifier.notify( message: "Symbol & Emoji Input".localized + "\n" @@ -327,7 +327,7 @@ extension SessionCtl { ) } - @objc public func toggleAssociatedPhrasesEnabled(_: Any? = nil) { + @objc func toggleAssociatedPhrasesEnabled(_: Any? = nil) { resetInputHandler(forceComposerCleanup: true) Notifier.notify( message: "Per-Char Associated Phrases".localized + "\n" @@ -337,7 +337,7 @@ extension SessionCtl { ) } - @objc public func togglePhraseReplacement(_: Any? = nil) { + @objc func togglePhraseReplacement(_: Any? = nil) { resetInputHandler(forceComposerCleanup: true) Notifier.notify( message: "Use Phrase Replacement".localized + "\n" @@ -347,20 +347,20 @@ extension SessionCtl { ) } - @objc public func selfUninstall(_: Any? = nil) { + @objc func selfUninstall(_: Any? = nil) { (NSApp.delegate as? AppDelegate)?.selfUninstall() } - @objc public func selfTerminate(_: Any? = nil) { + @objc func selfTerminate(_: Any? = nil) { NSApp.activate(ignoringOtherApps: true) NSApp.terminate(nil) } - @objc public func checkForUpdate(_: Any? = nil) { + @objc func checkForUpdate(_: Any? = nil) { UpdateSputnik.shared.checkForUpdate(forced: true, url: kUpdateInfoSourceURL) } - @objc public func openUserDataFolder(_: Any? = nil) { + @objc func openUserDataFolder(_: Any? = nil) { if !LMMgr.userDataFolderExists { return } @@ -369,49 +369,49 @@ extension SessionCtl { ) } - @objc public func openUserPhrases(_: Any? = nil) { + @objc func openUserPhrases(_: Any? = nil) { LMMgr.openUserDictFile(type: .thePhrases, dual: optionKeyPressed, alt: optionKeyPressed) } - @objc public func openExcludedPhrases(_: Any? = nil) { + @objc func openExcludedPhrases(_: Any? = nil) { LMMgr.openUserDictFile(type: .theFilter, dual: optionKeyPressed, alt: optionKeyPressed) } - @objc public func openUserSymbols(_: Any? = nil) { + @objc func openUserSymbols(_: Any? = nil) { LMMgr.openUserDictFile(type: .theSymbols, dual: optionKeyPressed, alt: optionKeyPressed) } - @objc public func openPhraseReplacement(_: Any? = nil) { + @objc func openPhraseReplacement(_: Any? = nil) { LMMgr.openUserDictFile(type: .theReplacements, dual: optionKeyPressed, alt: optionKeyPressed) } - @objc public func openAssociatedPhrases(_: Any? = nil) { + @objc func openAssociatedPhrases(_: Any? = nil) { LMMgr.openUserDictFile(type: .theAssociates, dual: optionKeyPressed, alt: optionKeyPressed) } - @objc public func reloadUserPhrasesData(_: Any? = nil) { + @objc func reloadUserPhrasesData(_: Any? = nil) { LMMgr.initUserLangModels() } - @objc public func callReverseLookupWindow(_: Any? = nil) { + @objc func callReverseLookupWindow(_: Any? = nil) { CtlRevLookupWindow.show() } - @objc public func removeUnigramsFromUOM(_: Any? = nil) { + @objc func removeUnigramsFromUOM(_: Any? = nil) { LMMgr.removeUnigramsFromUserOverrideModel(IMEApp.currentInputMode) if NSEvent.modifierFlags.contains(.option) { LMMgr.removeUnigramsFromUserOverrideModel(IMEApp.currentInputMode.reversed) } } - @objc public func clearUOM(_: Any? = nil) { + @objc func clearUOM(_: Any? = nil) { LMMgr.clearUserOverrideModelData(IMEApp.currentInputMode) if NSEvent.modifierFlags.contains(.option) { LMMgr.clearUserOverrideModelData(IMEApp.currentInputMode.reversed) } } - @objc public func showAbout(_: Any? = nil) { + @objc func showAbout(_: Any? = nil) { CtlAboutWindow.show() NSApp.activate(ignoringOtherApps: true) } diff --git a/Source/Modules/SymbolMenuDefaultData.swift b/Source/Modules/SymbolMenuDefaultData.swift index 3af5b4c5..b3a9068e 100644 --- a/Source/Modules/SymbolMenuDefaultData.swift +++ b/Source/Modules/SymbolMenuDefaultData.swift @@ -20,10 +20,10 @@ extension CandidateNode { for strLine in arrLines.lazy.filter({ !$0.isEmpty }) { fieldSlice = strLine.split(separator: "=") switch fieldSlice.count { - case 1: arrMembers.append(.init(name: String(fieldSlice[0]))) - case 2: - arrMembers.append(.init(name: String(fieldSlice[0]), symbols: .init(fieldSlice[1].map { String($0) }))) - default: break + case 1: arrMembers.append(.init(name: String(fieldSlice[0]))) + case 2: + arrMembers.append(.init(name: String(fieldSlice[0]), symbols: .init(fieldSlice[1].map { String($0) }))) + default: break } } Self.root = arrMembers.isEmpty ? defaultSymbolRoot : .init(name: "/", members: arrMembers) diff --git a/Source/Modules/UIModules/CandidateUI/IMKCandidatesImpl.swift b/Source/Modules/UIModules/CandidateUI/IMKCandidatesImpl.swift index 85bcb9c6..26462f1a 100644 --- a/Source/Modules/UIModules/CandidateUI/IMKCandidatesImpl.swift +++ b/Source/Modules/UIModules/CandidateUI/IMKCandidatesImpl.swift @@ -61,17 +61,17 @@ public class CtlCandidateIMK: IMKCandidates, CtlCandidateProtocol { public func specifyLayout(_ layout: NSUserInterfaceLayoutOrientation = .horizontal) { currentLayout = layout switch currentLayout { - case .horizontal: - if #available(macOS 10.14, *) { - setPanelType(kIMKScrollingGridCandidatePanel) - } else { - // macOS 10.13 High Sierra 的矩陣選字窗不支援選字鍵,所以只能弄成橫版單行。 - setPanelType(kIMKSingleRowSteppingCandidatePanel) - } - case .vertical: - setPanelType(kIMKSingleColumnScrollingCandidatePanel) - @unknown default: + case .horizontal: + if #available(macOS 10.14, *) { + setPanelType(kIMKScrollingGridCandidatePanel) + } else { + // macOS 10.13 High Sierra 的矩陣選字窗不支援選字鍵,所以只能弄成橫版單行。 setPanelType(kIMKSingleRowSteppingCandidatePanel) + } + case .vertical: + setPanelType(kIMKSingleColumnScrollingCandidatePanel) + @unknown default: + setPanelType(kIMKSingleRowSteppingCandidatePanel) } } @@ -171,8 +171,8 @@ var currentTISInputSource: TISInputSource? { // MARK: - Translating NumPad KeyCodes to Default IMK Candidate Selection KeyCodes. -extension CtlCandidateIMK { - public static func replaceNumPadKeyCodes(target event: NSEvent) -> NSEvent? { +public extension CtlCandidateIMK { + static func replaceNumPadKeyCodes(target event: NSEvent) -> NSEvent? { let mapNumPadKeyCodeTranslation: [UInt16: UInt16] = [ 83: 18, 84: 19, 85: 20, 86: 21, 87: 23, 88: 22, 89: 26, 91: 28, 92: 25, ] diff --git a/Source/Modules/UIModules/PrefUI/CtlPrefUI.swift b/Source/Modules/UIModules/PrefUI/CtlPrefUI.swift index 28595a33..39927e67 100644 --- a/Source/Modules/UIModules/PrefUI/CtlPrefUI.swift +++ b/Source/Modules/UIModules/PrefUI/CtlPrefUI.swift @@ -152,15 +152,15 @@ extension CtlPrefWindow { /// 由於用於頁籤標題的某些用語放在 localizable 資源內管理的話容易混亂,所以這裡單獨處理。 static var locPhrasesTabTitle: String { switch PrefMgr.shared.appleLanguages[0] { - case "ja": - return "辞書編集" - default: - if PrefMgr.shared.appleLanguages[0].contains("zh-Hans") { - return "语汇编辑" - } else if PrefMgr.shared.appleLanguages[0].contains("zh-Hant") { - return "語彙編輯" - } - return "Phrases" + case "ja": + return "辞書編集" + default: + if PrefMgr.shared.appleLanguages[0].contains("zh-Hans") { + return "语汇编辑" + } else if PrefMgr.shared.appleLanguages[0].contains("zh-Hant") { + return "語彙編輯" + } + return "Phrases" } } } diff --git a/Source/Modules/UIModules/PrefUI/VwrPrefPaneCassette.swift b/Source/Modules/UIModules/PrefUI/VwrPrefPaneCassette.swift index 4026ac2e..d2e904e9 100644 --- a/Source/Modules/UIModules/PrefUI/VwrPrefPaneCassette.swift +++ b/Source/Modules/UIModules/PrefUI/VwrPrefPaneCassette.swift @@ -7,8 +7,8 @@ // requirements defined in MIT License. import BookmarkManager -import SSPreferences import Shared +import SSPreferences import SwiftExtension import SwiftUI @@ -17,7 +17,7 @@ struct VwrPrefPaneCassette: View { private var fdrCassetteDataDefault: String { "" } @State private var tbxCassettePath: String = UserDefaults.standard.string(forKey: UserDef.kCassettePath.rawValue) - ?? "" + ?? "" @State private var selCassetteEnabled: Bool = UserDefaults.standard.bool( forKey: UserDef.kCassetteEnabled.rawValue) @State private var selForceCassetteChineseConversion: Int = UserDefaults.standard.integer( @@ -32,14 +32,14 @@ struct VwrPrefPaneCassette: View { private let contentMaxHeight: Double = 490 private let contentWidth: Double = { switch PrefMgr.shared.appleLanguages[0] { - case "ja": - return 520 - default: - if PrefMgr.shared.appleLanguages[0].contains("zh-Han") { - return 480 - } else { - return 580 - } + case "ja": + return 520 + default: + if PrefMgr.shared.appleLanguages[0].contains("zh-Han") { + return 480 + } else { + return 580 + } } }() diff --git a/Source/Modules/UIModules/PrefUI/VwrPrefPaneDevZone.swift b/Source/Modules/UIModules/PrefUI/VwrPrefPaneDevZone.swift index ac9d818e..6c17ba69 100644 --- a/Source/Modules/UIModules/PrefUI/VwrPrefPaneDevZone.swift +++ b/Source/Modules/UIModules/PrefUI/VwrPrefPaneDevZone.swift @@ -6,8 +6,8 @@ // marks, or product names of Contributor, except as required to fulfill notice // requirements defined in MIT License. -import SSPreferences import Shared +import SSPreferences import SwiftExtension import SwiftUI @@ -24,14 +24,14 @@ struct VwrPrefPaneDevZone: View { private let contentMaxHeight: Double = 490 private let contentWidth: Double = { switch PrefMgr.shared.appleLanguages[0] { - case "ja": - return 520 - default: - if PrefMgr.shared.appleLanguages[0].contains("zh-Han") { - return 480 - } else { - return 580 - } + case "ja": + return 520 + default: + if PrefMgr.shared.appleLanguages[0].contains("zh-Han") { + return 480 + } else { + return 580 + } } }() diff --git a/Source/Modules/UIModules/PrefUI/VwrPrefPaneDictionary.swift b/Source/Modules/UIModules/PrefUI/VwrPrefPaneDictionary.swift index d48f2fc8..05497e6d 100644 --- a/Source/Modules/UIModules/PrefUI/VwrPrefPaneDictionary.swift +++ b/Source/Modules/UIModules/PrefUI/VwrPrefPaneDictionary.swift @@ -7,8 +7,8 @@ // requirements defined in MIT License. import BookmarkManager -import SSPreferences import Shared +import SSPreferences import SwiftExtension import SwiftUI @@ -17,7 +17,7 @@ struct VwrPrefPaneDictionary: View { private var fdrUserDataDefault: String { LMMgr.dataFolderPath(isDefaultFolder: true) } @State private var tbxUserDataPathSpecified: String = UserDefaults.standard.string(forKey: UserDef.kUserDataFolderSpecified.rawValue) - ?? LMMgr.dataFolderPath(isDefaultFolder: true) + ?? LMMgr.dataFolderPath(isDefaultFolder: true) @State private var selAutoReloadUserData: Bool = UserDefaults.standard.bool( forKey: UserDef.kShouldAutoReloadUserDataFiles.rawValue) @State private var selUseExternalFactoryDict: Bool = UserDefaults.standard.bool( @@ -44,14 +44,14 @@ struct VwrPrefPaneDictionary: View { private let contentMaxHeight: Double = 490 private let contentWidth: Double = { switch PrefMgr.shared.appleLanguages[0] { - case "ja": - return 520 - default: - if PrefMgr.shared.appleLanguages[0].contains("zh-Han") { - return 480 - } else { - return 580 - } + case "ja": + return 520 + default: + if PrefMgr.shared.appleLanguages[0].contains("zh-Han") { + return 480 + } else { + return 580 + } } }() diff --git a/Source/Modules/UIModules/PrefUI/VwrPrefPaneExperience.swift b/Source/Modules/UIModules/PrefUI/VwrPrefPaneExperience.swift index dc6381c9..351490a4 100644 --- a/Source/Modules/UIModules/PrefUI/VwrPrefPaneExperience.swift +++ b/Source/Modules/UIModules/PrefUI/VwrPrefPaneExperience.swift @@ -6,8 +6,8 @@ // marks, or product names of Contributor, except as required to fulfill notice // requirements defined in MIT License. -import SSPreferences import Shared +import SSPreferences import SwiftExtension import SwiftUI @@ -56,14 +56,14 @@ struct VwrPrefPaneExperience: View { private let contentMaxHeight: Double = 490 private let contentWidth: Double = { switch PrefMgr.shared.appleLanguages[0] { - case "ja": - return 520 - default: - if PrefMgr.shared.appleLanguages[0].contains("zh-Han") { - return 480 - } else { - return 580 - } + case "ja": + return 520 + default: + if PrefMgr.shared.appleLanguages[0].contains("zh-Han") { + return 480 + } else { + return 580 + } } }() diff --git a/Source/Modules/UIModules/PrefUI/VwrPrefPaneGeneral.swift b/Source/Modules/UIModules/PrefUI/VwrPrefPaneGeneral.swift index ecf629e4..fd878578 100644 --- a/Source/Modules/UIModules/PrefUI/VwrPrefPaneGeneral.swift +++ b/Source/Modules/UIModules/PrefUI/VwrPrefPaneGeneral.swift @@ -6,8 +6,8 @@ // marks, or product names of Contributor, except as required to fulfill notice // requirements defined in MIT License. -import SSPreferences import Shared +import SSPreferences import SwiftExtension import SwiftUI @@ -43,14 +43,14 @@ struct VwrPrefPaneGeneral: View { private let contentMaxHeight: Double = 490 private let contentWidth: Double = { switch PrefMgr.shared.appleLanguages[0] { - case "ja": - return 520 - default: - if PrefMgr.shared.appleLanguages[0].contains("zh-Han") { - return 480 - } else { - return 580 - } + case "ja": + return 520 + default: + if PrefMgr.shared.appleLanguages[0].contains("zh-Han") { + return 480 + } else { + return 580 + } } }() @@ -190,11 +190,11 @@ struct VwrPrefPaneGeneral: View { PrefMgr.shared.shouldNotFartInLieuOfBeep = true alert.beginSheetModal(for: window) { result in switch result { - case .alertFirstButtonReturn: - PrefMgr.shared.shouldNotFartInLieuOfBeep = false - case .alertSecondButtonReturn: - PrefMgr.shared.shouldNotFartInLieuOfBeep = true - default: break + case .alertFirstButtonReturn: + PrefMgr.shared.shouldNotFartInLieuOfBeep = false + case .alertSecondButtonReturn: + PrefMgr.shared.shouldNotFartInLieuOfBeep = true + default: break } selEnableFartSuppressor = PrefMgr.shared.shouldNotFartInLieuOfBeep IMEApp.buzz() diff --git a/Source/Modules/UIModules/PrefUI/VwrPrefPaneKeyboard.swift b/Source/Modules/UIModules/PrefUI/VwrPrefPaneKeyboard.swift index b224d1e5..886db1fb 100644 --- a/Source/Modules/UIModules/PrefUI/VwrPrefPaneKeyboard.swift +++ b/Source/Modules/UIModules/PrefUI/VwrPrefPaneKeyboard.swift @@ -7,8 +7,8 @@ // requirements defined in MIT License. import IMKUtils -import SSPreferences import Shared +import SSPreferences import SwiftExtension import SwiftUI @@ -19,19 +19,19 @@ struct VwrPrefPaneKeyboard: View { UserDefaults.standard.string(forKey: UserDef.kBasicKeyboardLayout.rawValue) ?? PrefMgr.shared.basicKeyboardLayout @State private var selAlphanumericalKeyboardLayout: String = UserDefaults.standard.string(forKey: UserDef.kAlphanumericalKeyboardLayout.rawValue) - ?? PrefMgr.shared.alphanumericalKeyboardLayout + ?? PrefMgr.shared.alphanumericalKeyboardLayout private let contentMaxHeight: Double = 490 private let contentWidth: Double = { switch PrefMgr.shared.appleLanguages[0] { - case "ja": - return 520 - default: - if PrefMgr.shared.appleLanguages[0].contains("zh-Han") { - return 480 - } else { - return 580 - } + case "ja": + return 520 + default: + if PrefMgr.shared.appleLanguages[0].contains("zh-Han") { + return 480 + } else { + return 580 + } } }() @@ -100,7 +100,7 @@ struct VwrPrefPaneKeyboard: View { PrefMgr.shared.basicKeyboardLayout = value } ) { - ForEach(0...(IMKHelper.allowedBasicLayoutsAsTISInputSources.count - 1), id: \.self) { id in + ForEach(0 ... (IMKHelper.allowedBasicLayoutsAsTISInputSources.count - 1), id: \.self) { id in let theEntry = IMKHelper.allowedBasicLayoutsAsTISInputSources[id] if let theEntry = theEntry { Text(theEntry.vChewingLocalizedName).tag(theEntry.identifier) @@ -128,7 +128,7 @@ struct VwrPrefPaneKeyboard: View { PrefMgr.shared.alphanumericalKeyboardLayout = selAlphanumericalKeyboardLayout } ) { - ForEach(0...(IMKHelper.allowedAlphanumericalTISInputSources.count - 1), id: \.self) { id in + ForEach(0 ... (IMKHelper.allowedAlphanumericalTISInputSources.count - 1), id: \.self) { id in if let theEntry = IMKHelper.allowedAlphanumericalTISInputSources[id] { Text(theEntry.vChewingLocalizedName).tag(theEntry.identifier) } @@ -350,8 +350,8 @@ public struct ComboBox: NSViewRepresentable { public func comboBoxSelectionDidChange(_ notification: Notification) { if !ignoreSelectionChanges, - let box: NSComboBox = notification.object as? NSComboBox, - let newStringValue: String = box.objectValueOfSelectedItem as? String + let box: NSComboBox = notification.object as? NSComboBox, + let newStringValue: String = box.objectValueOfSelectedItem as? String { parent.text = newStringValue } diff --git a/Source/Modules/UIModules/PrefUI/VwrPrefPanePhrases.swift b/Source/Modules/UIModules/PrefUI/VwrPrefPanePhrases.swift index 2eea5872..d83eb70a 100644 --- a/Source/Modules/UIModules/PrefUI/VwrPrefPanePhrases.swift +++ b/Source/Modules/UIModules/PrefUI/VwrPrefPanePhrases.swift @@ -7,8 +7,8 @@ // requirements defined in MIT License. import PhraseEditorUI -import SSPreferences import Shared +import SSPreferences import SwiftExtension import SwiftUI @@ -17,14 +17,14 @@ struct VwrPrefPanePhrases: View { private let contentMaxHeight: Double = 490 private let contentWidth: Double = { switch PrefMgr.shared.appleLanguages[0] { - case "ja": - return 520 - default: - if PrefMgr.shared.appleLanguages[0].contains("zh-Han") { - return 480 - } else { - return 580 - } + case "ja": + return 520 + default: + if PrefMgr.shared.appleLanguages[0].contains("zh-Han") { + return 480 + } else { + return 580 + } } }() diff --git a/Source/Modules/WindowControllers/CtlAboutWindow.swift b/Source/Modules/WindowControllers/CtlAboutWindow.swift index 2744e8fe..db883af2 100644 --- a/Source/Modules/WindowControllers/CtlAboutWindow.swift +++ b/Source/Modules/WindowControllers/CtlAboutWindow.swift @@ -19,7 +19,7 @@ class CtlAboutWindow: NSWindowController { if shared == nil { shared = CtlAboutWindow(windowNibName: "frmAboutWindow") } guard let shared = shared, let sharedWindow = shared.window else { return } sharedWindow.setPosition(vertical: .top, horizontal: .left, padding: 20) - sharedWindow.orderFrontRegardless() // 逼著視窗往最前方顯示 + sharedWindow.orderFrontRegardless() // 逼著視窗往最前方顯示 sharedWindow.level = .statusBar sharedWindow.titlebarAppearsTransparent = true shared.showWindow(shared) @@ -35,7 +35,7 @@ class CtlAboutWindow: NSWindowController { window?.standardWindowButton(.zoomButton)?.isHidden = true guard let installingVersion = Bundle.main.infoDictionary?[kCFBundleVersionKey as String] - as? String, + as? String, let versionString = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else { return @@ -46,7 +46,7 @@ class CtlAboutWindow: NSWindowController { appCopyrightLabel.stringValue = copyrightLabel } if let eulaContent = Bundle.main.localizedInfoDictionary?["CFEULAContent"] as? String, - let eulaContentUpstream = Bundle.main.infoDictionary?["CFUpstreamEULAContent"] as? String + let eulaContentUpstream = Bundle.main.infoDictionary?["CFUpstreamEULAContent"] as? String { appEULAContent.string = eulaContent + "\n" + eulaContentUpstream } diff --git a/Source/Modules/WindowControllers/CtlClientListMgr.swift b/Source/Modules/WindowControllers/CtlClientListMgr.swift index b58baad0..5a6e9750 100644 --- a/Source/Modules/WindowControllers/CtlClientListMgr.swift +++ b/Source/Modules/WindowControllers/CtlClientListMgr.swift @@ -20,7 +20,7 @@ class CtlClientListMgr: NSWindowController, NSTableViewDelegate, NSTableViewData if shared == nil { shared = CtlClientListMgr(windowNibName: "frmClientListMgr") } guard let shared = shared, let sharedWindow = shared.window else { return } sharedWindow.setPosition(vertical: .center, horizontal: .right, padding: 20) - sharedWindow.orderFrontRegardless() // 逼著視窗往最前方顯示 + sharedWindow.orderFrontRegardless() // 逼著視窗往最前方顯示 sharedWindow.level = .statusBar sharedWindow.titlebarAppearsTransparent = true shared.showWindow(shared) @@ -85,52 +85,52 @@ extension CtlClientListMgr { alert.accessoryView = scrollview alert.beginSheetModal(for: window) { result in switch result { - case .alertFirstButtonReturn, .alertSecondButtonReturn: - theTextView.textContainer?.textView?.string.components(separatedBy: "\n").filter { !$0.isEmpty }.forEach { - self.applyNewValue($0) - } - if result == .alertFirstButtonReturn { break } - let dlgOpenPath = NSOpenPanel() - dlgOpenPath.title = NSLocalizedString( - "Choose the target application bundle.", comment: "" - ) - dlgOpenPath.showsResizeIndicator = true - dlgOpenPath.allowsMultipleSelection = true - dlgOpenPath.allowedFileTypes = ["app"] - dlgOpenPath.allowsOtherFileTypes = false - dlgOpenPath.showsHiddenFiles = true - dlgOpenPath.canChooseFiles = true - dlgOpenPath.canChooseDirectories = false - dlgOpenPath.beginSheetModal(for: window) { result in - switch result { - case .OK: - for url in dlgOpenPath.urls { - var title = NSLocalizedString( - "The selected item is not a valid macOS application bundle, nor not having a valid app bundle identifier.", - comment: "" - ) - let text = url.path + "\n\n" + NSLocalizedString("Please try again.", comment: "") - guard let bundle = Bundle(url: url) else { - self.window?.callAlert(title: title, text: text) - return - } - guard let identifier = bundle.bundleIdentifier else { - self.window?.callAlert(title: title, text: text) - return - } - if PrefMgr.shared.clientsIMKTextInputIncapable.contains(identifier) { - title = NSLocalizedString( - "The selected item's identifier is already in the list.", comment: "" - ) - self.window?.callAlert(title: title, text: identifier + "\n\n" + url.path) - return - } - self.applyNewValue(identifier) - } - default: return + case .alertFirstButtonReturn, .alertSecondButtonReturn: + theTextView.textContainer?.textView?.string.components(separatedBy: "\n").filter { !$0.isEmpty }.forEach { + self.applyNewValue($0) + } + if result == .alertFirstButtonReturn { break } + let dlgOpenPath = NSOpenPanel() + dlgOpenPath.title = NSLocalizedString( + "Choose the target application bundle.", comment: "" + ) + dlgOpenPath.showsResizeIndicator = true + dlgOpenPath.allowsMultipleSelection = true + dlgOpenPath.allowedFileTypes = ["app"] + dlgOpenPath.allowsOtherFileTypes = false + dlgOpenPath.showsHiddenFiles = true + dlgOpenPath.canChooseFiles = true + dlgOpenPath.canChooseDirectories = false + dlgOpenPath.beginSheetModal(for: window) { result in + switch result { + case .OK: + for url in dlgOpenPath.urls { + var title = NSLocalizedString( + "The selected item is not a valid macOS application bundle, nor not having a valid app bundle identifier.", + comment: "" + ) + let text = url.path + "\n\n" + NSLocalizedString("Please try again.", comment: "") + guard let bundle = Bundle(url: url) else { + self.window?.callAlert(title: title, text: text) + return + } + guard let identifier = bundle.bundleIdentifier else { + self.window?.callAlert(title: title, text: text) + return + } + if PrefMgr.shared.clientsIMKTextInputIncapable.contains(identifier) { + title = NSLocalizedString( + "The selected item's identifier is already in the list.", comment: "" + ) + self.window?.callAlert(title: title, text: identifier + "\n\n" + url.path) + return + } + self.applyNewValue(identifier) } + default: return } - default: return + } + default: return } } } @@ -141,7 +141,7 @@ extension CtlClientListMgr { arrResult.append(newValue) PrefMgr.shared.clientsIMKTextInputIncapable = arrResult.sorted() tblClients.reloadData() - btnRemoveClient.isEnabled = (0.. Any? { defer { - self.btnRemoveClient.isEnabled = (0..