Clang-format // Use Google Swift Format Style.

This commit is contained in:
ShikiSuen 2023-02-07 17:40:28 +08:00
parent e1edd189ad
commit 856c5d02d7
169 changed files with 2987 additions and 2935 deletions

View File

@ -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
}

104
.swiftformat.json Normal file
View File

@ -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

View File

@ -10,8 +10,8 @@
import Cocoa import Cocoa
extension String { fileprivate extension String {
fileprivate mutating func regReplace(pattern: String, replaceWith: String = "") { mutating func regReplace(pattern: String, replaceWith: String = "") {
// Ref: https://stackoverflow.com/a/40993403/4162914 && https://stackoverflow.com/a/71291137/4162914 // Ref: https://stackoverflow.com/a/40993403/4162914 && https://stackoverflow.com/a/71291137/4162914
do { do {
let regex = try NSRegularExpression( let regex = try NSRegularExpression(

View File

@ -12,8 +12,8 @@ import Foundation
// MARK: - // MARK: -
extension String { fileprivate extension String {
fileprivate mutating func regReplace(pattern: String, replaceWith: String = "") { mutating func regReplace(pattern: String, replaceWith: String = "") {
// Ref: https://stackoverflow.com/a/40993403/4162914 && https://stackoverflow.com/a/71291137/4162914 // Ref: https://stackoverflow.com/a/40993403/4162914 && https://stackoverflow.com/a/71291137/4162914
do { do {
let regex = try NSRegularExpression( let regex = try NSRegularExpression(
@ -29,21 +29,21 @@ extension String {
// MARK: - String charComponents Extension // MARK: - String charComponents Extension
extension String { public extension String {
public var charComponents: [String] { map { String($0) } } var charComponents: [String] { map { String($0) } }
} }
extension Array where Element == String.Element { public extension Array where Element == String.Element {
public var charComponents: [String] { map { String($0) } } var charComponents: [String] { map { String($0) } }
} }
// MARK: - StringView Ranges Extension (by Isaac Xen) // MARK: - StringView Ranges Extension (by Isaac Xen)
extension String { fileprivate extension String {
fileprivate func ranges(splitBy separator: Element) -> [Range<String.Index>] { func ranges(splitBy separator: Element) -> [Range<String.Index>] {
var startIndex = startIndex var startIndex = startIndex
return split(separator: separator).reduce(into: []) { ranges, substring in return split(separator: separator).reduce(into: []) { ranges, substring in
_ = range(of: substring, range: startIndex..<endIndex).map { range in _ = range(of: substring, range: startIndex ..< endIndex).map { range in
ranges.append(range) ranges.append(range)
startIndex = range.upperBound startIndex = range.upperBound
} }
@ -54,8 +54,8 @@ extension String {
// MARK: - // MARK: -
// Ref: https://stackoverflow.com/a/32581409/4162914 // Ref: https://stackoverflow.com/a/32581409/4162914
extension Double { fileprivate extension Double {
fileprivate func rounded(toPlaces places: Int) -> Double { func rounded(toPlaces places: Int) -> Double {
let divisor = pow(10.0, Double(places)) let divisor = pow(10.0, Double(places))
return (self * divisor).rounded() / divisor return (self * divisor).rounded() / divisor
} }
@ -220,7 +220,7 @@ func rawDictForPhrases(isCHS: Bool) -> [Unigram] {
Unigram( Unigram(
key: phone, value: phrase, score: 0.0, key: phone, value: phrase, score: 0.0,
count: occurrence count: occurrence
) ),
] ]
} }
} }
@ -303,7 +303,7 @@ func rawDictForKanjis(isCHS: Bool) -> [Unigram] {
Unigram( Unigram(
key: phone, value: phrase, score: 0.0, key: phone, value: phrase, score: 0.0,
count: occurrence count: occurrence
) ),
] ]
} }
} }
@ -389,7 +389,7 @@ func rawDictForNonKanjis(isCHS: Bool) -> [Unigram] {
Unigram( Unigram(
key: phone, value: phrase, score: 0.0, key: phone, value: phrase, score: 0.0,
count: occurrence count: occurrence
) ),
] ]
} }
} }
@ -432,7 +432,7 @@ func weightAndSort(_ arrStructUncalculated: [Unigram], isCHS: Bool) -> [Unigram]
Unigram( Unigram(
key: unigram.key, value: unigram.value, score: weightRounded, key: unigram.key, value: unigram.value, score: weightRounded,
count: unigram.count count: unigram.count
) ),
] ]
} }
NSLog(" - \(i18n): 成功計算權重。") NSLog(" - \(i18n): 成功計算權重。")
@ -763,7 +763,7 @@ func healthCheck(_ data: [Unigram]) -> String {
let separator: String = { let separator: String = {
var result = "" var result = ""
for _ in 0..<72 { result += "-" } for _ in 0 ..< 72 { result += "-" }
return result return result
}() }()
@ -784,7 +784,7 @@ func healthCheck(_ data: [Unigram]) -> String {
printl("\n其中有:") printl("\n其中有:")
var insufficientsMap = [Int: [(String, String, Double, [Unigram], Double)]]() 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 } insufficientsMap[x] = insufficients.filter { $0.0.split(separator: "-").count == x }
} }

View File

@ -20,23 +20,14 @@ debug:
DSTROOT = /Library/Input Methods DSTROOT = /Library/Input Methods
VC_APP_ROOT = $(DSTROOT)/vChewing.app 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 format:
@swiftformat --swiftversion 5.5 --indent 2 ./
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
lint: 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 @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 .PHONY: permission-check install-debug install-release
permission-check: permission-check:

View File

@ -4,19 +4,19 @@ import PackageDescription
let package = Package( let package = Package(
name: "FolderMonitor", name: "FolderMonitor",
platforms: [ platforms: [
.macOS(.v10_11) .macOS(.v10_11),
], ],
products: [ products: [
.library( .library(
name: "FolderMonitor", name: "FolderMonitor",
targets: ["FolderMonitor"] targets: ["FolderMonitor"]
) ),
], ],
dependencies: [], dependencies: [],
targets: [ targets: [
.target( .target(
name: "FolderMonitor", name: "FolderMonitor",
dependencies: [] dependencies: []
) ),
] ]
) )

View File

@ -4,19 +4,19 @@ import PackageDescription
let package = Package( let package = Package(
name: "NSAttributedTextView", name: "NSAttributedTextView",
platforms: [ platforms: [
.macOS(.v10_11) .macOS(.v10_11),
], ],
products: [ products: [
.library( .library(
name: "NSAttributedTextView", name: "NSAttributedTextView",
targets: ["NSAttributedTextView"] targets: ["NSAttributedTextView"]
) ),
], ],
dependencies: [], dependencies: [],
targets: [ targets: [
.target( .target(
name: "NSAttributedTextView", name: "NSAttributedTextView",
dependencies: [] dependencies: []
) ),
] ]
) )

View File

@ -137,7 +137,7 @@ public class NSAttributedTextView: NSView {
}() }()
let frameAttrs: CFDictionary = let frameAttrs: CFDictionary =
[ [
kCTFrameProgressionAttributeName: theCTFrameProgression.rawValue kCTFrameProgressionAttributeName: theCTFrameProgression.rawValue,
] as CFDictionary ] as CFDictionary
let newFrame = CTFramesetterCreateFrame(setter, CFRangeMake(0, 0), path, frameAttrs) let newFrame = CTFramesetterCreateFrame(setter, CFRangeMake(0, 0), path, frameAttrs)
ctFrame = newFrame ctFrame = newFrame

View File

@ -4,19 +4,19 @@ import PackageDescription
let package = Package( let package = Package(
name: "BookmarkManager", name: "BookmarkManager",
platforms: [ platforms: [
.macOS(.v10_11) .macOS(.v10_11),
], ],
products: [ products: [
.library( .library(
name: "BookmarkManager", name: "BookmarkManager",
targets: ["BookmarkManager"] targets: ["BookmarkManager"]
) ),
], ],
dependencies: [], dependencies: [],
targets: [ targets: [
.target( .target(
name: "BookmarkManager", name: "BookmarkManager",
dependencies: [] dependencies: []
) ),
] ]
) )

View File

@ -4,19 +4,19 @@ import PackageDescription
let package = Package( let package = Package(
name: "ShiftKeyUpChecker", name: "ShiftKeyUpChecker",
platforms: [ platforms: [
.macOS(.v10_11) .macOS(.v10_11),
], ],
products: [ products: [
.library( .library(
name: "ShiftKeyUpChecker", name: "ShiftKeyUpChecker",
targets: ["ShiftKeyUpChecker"] targets: ["ShiftKeyUpChecker"]
) ),
], ],
dependencies: [], dependencies: [],
targets: [ targets: [
.target( .target(
name: "ShiftKeyUpChecker", name: "ShiftKeyUpChecker",
dependencies: [] dependencies: []
) ),
] ]
) )

View File

@ -5,8 +5,8 @@
import Carbon import Carbon
import Cocoa import Cocoa
extension Date { private extension Date {
fileprivate static func - (lhs: Date, rhs: Date) -> TimeInterval { static func - (lhs: Date, rhs: Date) -> TimeInterval {
lhs.timeIntervalSinceReferenceDate - rhs.timeIntervalSinceReferenceDate lhs.timeIntervalSinceReferenceDate - rhs.timeIntervalSinceReferenceDate
} }
} }

View File

@ -4,23 +4,23 @@ import PackageDescription
let package = Package( let package = Package(
name: "LineReader", name: "LineReader",
platforms: [ platforms: [
.macOS(.v10_11) .macOS(.v10_11),
], ],
products: [ products: [
.library( .library(
name: "LineReader", name: "LineReader",
targets: ["LineReader"] targets: ["LineReader"]
) ),
], ],
dependencies: [ dependencies: [
.package(path: "../vChewing_SwiftExtension") .package(path: "../vChewing_SwiftExtension"),
], ],
targets: [ targets: [
.target( .target(
name: "LineReader", name: "LineReader",
dependencies: [ dependencies: [
.product(name: "SwiftExtension", package: "vChewing_SwiftExtension") .product(name: "SwiftExtension", package: "vChewing_SwiftExtension"),
] ]
) ),
] ]
) )

View File

@ -31,9 +31,9 @@ public class LineReader {
// get a data from the buffer up to the next delimiter // get a data from the buffer up to the next delimiter
if let range = buffer.range(of: delimData) { if let range = buffer.range(of: delimData) {
// convert data to a string // convert data to a string
let line = String(data: buffer.subdata(in: 0..<range.lowerBound), encoding: encoding)! let line = String(data: buffer.subdata(in: 0 ..< range.lowerBound), encoding: encoding)!
// remove that data from the buffer // remove that data from the buffer
buffer.removeSubrange(0..<range.upperBound) buffer.removeSubrange(0 ..< range.upperBound)
return line.trimmingCharacters(in: .newlines) return line.trimmingCharacters(in: .newlines)
} }

View File

@ -13,10 +13,10 @@ let package = Package(
.library( .library(
name: "SwiftUIBackports", name: "SwiftUIBackports",
targets: ["SwiftUIBackports"] targets: ["SwiftUIBackports"]
) ),
], ],
targets: [ targets: [
.target(name: "SwiftUIBackports") .target(name: "SwiftUIBackports"),
], ],
swiftLanguageVersions: [.v5] swiftLanguageVersions: [.v5]
) )

View File

@ -42,21 +42,21 @@ public struct Backport<Wrapped> {
} }
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension View { public extension View {
/// Wraps a SwiftUI `View` that can be extended to provide backport functionality. /// Wraps a SwiftUI `View` that can be extended to provide backport functionality.
public var backport: Backport<Self> { .init(self) } var backport: Backport<Self> { .init(self) }
} }
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension NSObjectProtocol { public extension NSObjectProtocol {
/// Wraps an `NSObject` that can be extended to provide backport functionality. /// Wraps an `NSObject` that can be extended to provide backport functionality.
public var backport: Backport<Self> { .init(self) } var backport: Backport<Self> { .init(self) }
} }
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension AnyTransition { public extension AnyTransition {
/// Wraps an `AnyTransition` that can be extended to provide backport functionality. /// Wraps an `AnyTransition` that can be extended to provide backport functionality.
public static var backport: Backport<AnyTransition> { static var backport: Backport<AnyTransition> {
Backport(.identity) Backport(.identity)
} }
} }

View File

@ -5,12 +5,12 @@
import SwiftUI import SwiftUI
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension EnvironmentValues { private extension EnvironmentValues {
fileprivate func containsValue(forKey key: String) -> Bool { func containsValue(forKey key: String) -> Bool {
value(forKey: key) != nil value(forKey: key) != nil
} }
fileprivate func value<T>(forKey key: String, from mirror: Mirror, as _: T.Type) -> T? { func value<T>(forKey key: String, from mirror: Mirror, as _: T.Type) -> T? {
// Found a match // Found a match
if let value = mirror.descendant("value", "some") { if let value = mirror.descendant("value", "some") {
if let typedValue = value as? T { 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. /// 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"). /// Can be used to grab private environment values such as foregroundColor ("ForegroundColorKey").
fileprivate func value<T>(forKey key: String, as _: T.Type) -> T? { func value<T>(forKey key: String, as _: T.Type) -> T? {
if let mirror = value(forKey: key) as? Mirror { if let mirror = value(forKey: key) as? Mirror {
return value(forKey: key, from: mirror, as: T.self) return value(forKey: key, from: mirror, as: T.self)
} else if let value = value(forKey: key) as? T { } 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? { func keyFromTypeName(typeName: String) -> String? {
let expectedPrefix = "TypedElement<EnvironmentPropertyKey<" let expectedPrefix = "TypedElement<EnvironmentPropertyKey<"
guard typeName.hasPrefix(expectedPrefix) else { guard typeName.hasPrefix(expectedPrefix) else {

View File

@ -123,7 +123,7 @@ import SwiftUI
#if os(iOS) #if os(iOS)
extension InspectionView { extension InspectionView {
fileprivate struct Representable: UIViewRepresentable { struct Representable: UIViewRepresentable {
let parent: InspectionView let parent: InspectionView
func makeUIView(context _: Context) -> UIView { .init() } func makeUIView(context _: Context) -> UIView { .init() }
@ -149,7 +149,7 @@ import SwiftUI
#elseif os(macOS) #elseif os(macOS)
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension InspectionView { extension InspectionView {
fileprivate struct Representable: NSViewRepresentable { struct Representable: NSViewRepresentable {
let parent: InspectionView let parent: InspectionView
func makeNSView(context _: Context) -> NSView { func makeNSView(context _: Context) -> NSView {

View File

@ -5,8 +5,8 @@
#if os(iOS) #if os(iOS)
import UIKit import UIKit
extension UIView { public extension UIView {
public var parentController: UIViewController? { var parentController: UIViewController? {
var responder: UIResponder? = self var responder: UIResponder? = self
while !(responder is UIViewController), superview != nil { while !(responder is UIViewController), superview != nil {
@ -24,8 +24,8 @@
import AppKit import AppKit
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension NSView { public extension NSView {
public var parentController: NSViewController? { var parentController: NSViewController? {
var responder: NSResponder? = self var responder: NSResponder? = self
while !(responder is NSViewController), superview != nil { while !(responder is NSViewController), superview != nil {

View File

@ -29,12 +29,12 @@ import SwiftUI
guard let viewClassNameUtf8 = (viewSubclassName as NSString).utf8String else { return } guard let viewClassNameUtf8 = (viewSubclassName as NSString).utf8String else { return }
guard let viewSubclass = objc_allocateClassPair(viewClass, viewClassNameUtf8, 0) 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 let safeAreaInsets: @convention(block) (AnyObject) -> UIEdgeInsets = { _ in
.zero .zero
} }
class_addMethod( class_addMethod(
viewSubclass, #selector(getter:UIView.safeAreaInsets), imp_implementationWithBlock(safeAreaInsets), viewSubclass, #selector(getter: UIView.safeAreaInsets), imp_implementationWithBlock(safeAreaInsets),
method_getTypeEncoding(method) method_getTypeEncoding(method)
) )
} }

View File

@ -43,7 +43,7 @@ extension Backport where Wrapped == Any {
} }
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension Backport.AppStorage { public extension Backport.AppStorage {
/// Creates a property that can read and write to a boolean user default. /// Creates a property that can read and write to a boolean user default.
/// ///
/// - Parameters: /// - Parameters:
@ -53,7 +53,7 @@ extension Backport.AppStorage {
/// store. /// store.
/// - store: The store to read and write to. A value /// - store: The store to read and write to. A value
/// of `nil` will use the user default store from the environment. /// 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 let value = store.value(forKey: key) as? Value ?? wrappedValue
self.init( self.init(
value: value, store: store, key: key, value: value, store: store, key: key,
@ -71,7 +71,7 @@ extension Backport.AppStorage {
/// store. /// store.
/// - store: The store to read and write to. A value /// - store: The store to read and write to. A value
/// of `nil` will use the user default store from the environment. /// 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 let value = store.value(forKey: key) as? Value ?? wrappedValue
self.init( self.init(
value: value, store: store, key: key, value: value, store: store, key: key,
@ -89,7 +89,7 @@ extension Backport.AppStorage {
/// store. /// store.
/// - store: The store to read and write to. A value /// - store: The store to read and write to. A value
/// of `nil` will use the user default store from the environment. /// 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 let value = store.value(forKey: key) as? Value ?? wrappedValue
self.init( self.init(
value: value, store: store, key: key, value: value, store: store, key: key,
@ -107,7 +107,7 @@ extension Backport.AppStorage {
/// store. /// store.
/// - store: The store to read and write to. A value /// - store: The store to read and write to. A value
/// of `nil` will use the user default store from the environment. /// 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 let value = store.value(forKey: key) as? Value ?? wrappedValue
self.init( self.init(
value: value, store: store, key: key, value: value, store: store, key: key,
@ -125,7 +125,7 @@ extension Backport.AppStorage {
/// store. /// store.
/// - store: The store to read and write to. A value /// - store: The store to read and write to. A value
/// of `nil` will use the user default store from the environment. /// 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 let value = store.url(forKey: key) ?? wrappedValue
self.init( self.init(
value: value, store: store, key: key, value: value, store: store, key: key,
@ -146,7 +146,7 @@ extension Backport.AppStorage {
/// store. /// store.
/// - store: The store to read and write to. A value /// - store: The store to read and write to. A value
/// of `nil` will use the user default store from the environment. /// 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 let value = store.value(forKey: key) as? Data ?? wrappedValue
self.init( self.init(
value: value, store: store, key: key, value: value, store: store, key: key,
@ -157,7 +157,7 @@ extension Backport.AppStorage {
} }
@available(macOS 10.15, *) @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 /// Creates a property that can read and write an Optional boolean user
/// default. /// default.
/// ///
@ -168,7 +168,7 @@ extension Backport.AppStorage where Wrapped == Any, Value: ExpressibleByNilLiter
/// store. /// store.
/// - store: The store to read and write to. A value /// - store: The store to read and write to. A value
/// of `nil` will use the user default store from the environment. /// 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 let value = store.value(forKey: key) as? Value ?? .none
self.init( self.init(
value: value, store: store, key: key, value: value, store: store, key: key,
@ -187,7 +187,7 @@ extension Backport.AppStorage where Wrapped == Any, Value: ExpressibleByNilLiter
/// store. /// store.
/// - store: The store to read and write to. A value /// - store: The store to read and write to. A value
/// of `nil` will use the user default store from the environment. /// 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 let value = store.value(forKey: key) as? Value ?? .none
self.init( self.init(
value: value, store: store, key: key, value: value, store: store, key: key,
@ -206,7 +206,7 @@ extension Backport.AppStorage where Wrapped == Any, Value: ExpressibleByNilLiter
/// store. /// store.
/// - store: The store to read and write to. A value /// - store: The store to read and write to. A value
/// of `nil` will use the user default store from the environment. /// 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 let value = store.value(forKey: key) as? Value ?? .none
self.init( self.init(
value: value, store: store, key: key, value: value, store: store, key: key,
@ -225,7 +225,7 @@ extension Backport.AppStorage where Wrapped == Any, Value: ExpressibleByNilLiter
/// store. /// store.
/// - store: The store to read and write to. A value /// - store: The store to read and write to. A value
/// of `nil` will use the user default store from the environment. /// 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 let value = store.value(forKey: key) as? Value ?? .none
self.init( self.init(
value: value, store: store, key: key, value: value, store: store, key: key,
@ -244,7 +244,7 @@ extension Backport.AppStorage where Wrapped == Any, Value: ExpressibleByNilLiter
/// store. /// store.
/// - store: The store to read and write to. A value /// - store: The store to read and write to. A value
/// of `nil` will use the user default store from the environment. /// 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 let value = store.url(forKey: key) ?? .none
self.init( self.init(
value: value, store: store, key: key, value: value, store: store, key: key,
@ -263,7 +263,7 @@ extension Backport.AppStorage where Wrapped == Any, Value: ExpressibleByNilLiter
/// store. /// store.
/// - store: The store to read and write to. A value /// - store: The store to read and write to. A value
/// of `nil` will use the user default store from the environment. /// 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 let value = store.value(forKey: key) as? Value ?? .none
self.init( self.init(
value: value, store: store, key: key, value: value, store: store, key: key,
@ -274,7 +274,7 @@ extension Backport.AppStorage where Wrapped == Any, Value: ExpressibleByNilLiter
} }
@available(macOS 10.15, *) @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, /// Creates a property that can read and write to a string user default,
/// transforming that to `RawRepresentable` data type. /// transforming that to `RawRepresentable` data type.
/// ///
@ -295,7 +295,7 @@ extension Backport.AppStorage where Wrapped == Any, Value: RawRepresentable {
/// store. /// store.
/// - store: The store to read and write to. A value /// - store: The store to read and write to. A value
/// of `nil` will use the user default store from the environment. /// 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 rawValue = store.value(forKey: key) as? Value.RawValue
let value = rawValue.flatMap(Value.init) ?? wrappedValue let value = rawValue.flatMap(Value.init) ?? wrappedValue
self.init( self.init(
@ -325,7 +325,7 @@ extension Backport.AppStorage where Wrapped == Any, Value: RawRepresentable {
/// store. /// store.
/// - store: The store to read and write to. A value /// - store: The store to read and write to. A value
/// of `nil` will use the user default store from the environment. /// 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 rawValue = store.value(forKey: key) as? Value.RawValue
let value = rawValue.flatMap(Value.init) ?? wrappedValue let value = rawValue.flatMap(Value.init) ?? wrappedValue
self.init( self.init(

View File

@ -5,7 +5,7 @@
import SwiftUI import SwiftUI
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension Backport where Wrapped: View { public extension Backport where Wrapped: View {
/// Layers the views that you specify behind this view. /// Layers the views that you specify behind this view.
/// ///
/// Use this modifier to place one or more views behind another 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. /// The last view that you list appears at the front of the stack.
/// ///
/// - Returns: A view that uses the specified content as a background. /// - Returns: A view that uses the specified content as a background.
public func background<Content: View>(alignment: Alignment = .center, @ViewBuilder _ content: () -> Content) func background<Content: View>(alignment: Alignment = .center, @ViewBuilder _ content: () -> Content)
-> some View -> some View
{ {
self.content.background(content(), alignment: alignment) self.content.background(content(), alignment: alignment)

View File

@ -5,7 +5,7 @@
import SwiftUI import SwiftUI
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension EnvironmentValues { public extension EnvironmentValues {
/// An action that dismisses the current presentation. /// An action that dismisses the current presentation.
/// ///
/// Use this environment value to get the ``Backport.DismissAction`` instance /// 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 /// The dismiss action has no effect on a view that isn't currently
/// presented. If you need to query whether SwiftUI is currently presenting /// presented. If you need to query whether SwiftUI is currently presenting
/// a view, read the ``EnvironmentValues/backportIsPresented`` environment value. /// a view, read the ``EnvironmentValues/backportIsPresented`` environment value.
public var backportDismiss: Backport<Any>.DismissAction { var backportDismiss: Backport<Any>.DismissAction {
.init(presentation: presentationMode) .init(presentation: presentationMode)
} }
@ -104,7 +104,7 @@ extension EnvironmentValues {
/// ///
/// To dismiss the currently presented view, use /// To dismiss the currently presented view, use
/// ``EnvironmentValues/backportDismiss``. /// ``EnvironmentValues/backportDismiss``.
public var backportIsPresented: Bool { var backportIsPresented: Bool {
presentationMode.wrappedValue.isPresented presentationMode.wrappedValue.isPresented
} }
} }

View File

@ -5,7 +5,7 @@
import SwiftUI import SwiftUI
@available(macOS 10.15, *) @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. /// 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 /// 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 /// - Returns: A view that sets the Dynamic Type size to the specified
/// `size`. /// `size`.
@ViewBuilder @ViewBuilder
public func dynamicTypeSize(_ size: Backport<Any>.DynamicTypeSize) -> some View { func dynamicTypeSize(_ size: Backport<Any>.DynamicTypeSize) -> some View {
content.environment(\.backportDynamicTypeSize, size) 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 /// - Returns: A view that constrains the Dynamic Type size of this view
/// within the specified `range`. /// within the specified `range`.
@ViewBuilder @ViewBuilder
public func dynamicTypeSize<T>(_ range: T) -> some View func dynamicTypeSize<T>(_ range: T) -> some View
where T: RangeExpression, T.Bound == Backport<Any>.DynamicTypeSize { where T: RangeExpression, T.Bound == Backport<Any>.DynamicTypeSize
{
if let range = range as? Range<T.Bound> { if let range = range as? Range<T.Bound> {
content content
.modifier(DynamicTypeRangeModifier()) .modifier(DynamicTypeRangeModifier())

View File

@ -111,14 +111,14 @@ extension Backport where Wrapped == Any {
} }
@available(macOS 10.15, *) @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 /// Creates a label with an icon image and a title generated from a
/// localized string. /// localized string.
/// ///
/// - Parameters: /// - Parameters:
/// - titleKey: A title generated from a localized string. /// - titleKey: A title generated from a localized string.
/// - image: The name of the image resource to lookup. /// - 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) }) self.init(title: { Text(titleKey) }, icon: { Image(name) })
} }
@ -127,7 +127,7 @@ extension Backport.Label where Wrapped == Any, Title == Text, Icon == Image {
/// - Parameters: /// - Parameters:
/// - title: A string used as the label's title. /// - title: A string used as the label's title.
/// - image: The name of the image resource to lookup. /// - image: The name of the image resource to lookup.
public init<S>(_ title: S, image name: String) where S: StringProtocol { init<S>(_ title: S, image name: String) where S: StringProtocol {
self.init(title: { Text(title) }, icon: { Image(name) }) 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, *) @available(macOS 10.15, *)
extension Backport.Label public extension Backport.Label
where Wrapped == Any, Title == Backport.LabelStyleConfiguration.Title, Icon == Backport.LabelStyleConfiguration.Icon { where Wrapped == Any, Title == Backport.LabelStyleConfiguration.Title, Icon == Backport.LabelStyleConfiguration.Icon
{
/// Creates a label representing the configuration of a style. /// Creates a label representing the configuration of a style.
/// ///
/// You can use this initializer within the ``LabelStyle/makeBody(configuration:)`` /// 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. /// - Parameter configuration: The label style to use.
public init(_ configuration: Backport.LabelStyleConfiguration) { init(_ configuration: Backport.LabelStyleConfiguration) {
config = configuration config = configuration
} }
} }

View File

@ -137,8 +137,8 @@ extension Backport where Wrapped == Any {
} }
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension Backport.LabeledContent public extension Backport.LabeledContent
where where
Wrapped == Any, Label == Backport<Any>.LabeledContentStyleConfiguration.Label, Wrapped == Any, Label == Backport<Any>.LabeledContentStyleConfiguration.Label,
Content == Backport<Any>.LabeledContentStyleConfiguration.Content Content == Backport<Any>.LabeledContentStyleConfiguration.Content
{ {
@ -161,13 +161,13 @@ where
/// } /// }
/// ///
/// - Parameter configuration: The properties of the labeled content /// - Parameter configuration: The properties of the labeled content
public init(_ config: Backport.LabeledContentStyleConfiguration) { init(_ config: Backport.LabeledContentStyleConfiguration) {
self.config = config self.config = config
} }
} }
@available(macOS 10.15, *) @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 /// Creates a labeled view that generates its label from a localized string
/// key. /// 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 /// - titleKey: The key for the view's localized title, that describes
/// the purpose of the view. /// the purpose of the view.
/// - content: The value content being labeled. /// - content: The value content being labeled.
public init(_ titleKey: LocalizedStringKey, @ViewBuilder content: () -> Content) { init(_ titleKey: LocalizedStringKey, @ViewBuilder content: () -> Content) {
config = .init( config = .init(
label: Text(titleKey), label: Text(titleKey),
content: content() content: content()
@ -196,7 +196,7 @@ extension Backport.LabeledContent where Wrapped == Any, Label == Text, Content:
/// - Parameters: /// - Parameters:
/// - title: A string that describes the purpose of the view. /// - title: A string that describes the purpose of the view.
/// - content: The value content being labeled. /// - content: The value content being labeled.
public init<S>(_ title: S, @ViewBuilder content: () -> Content) where S: StringProtocol { init<S>(_ title: S, @ViewBuilder content: () -> Content) where S: StringProtocol {
config = .init( config = .init(
label: Text(title), label: Text(title),
content: content() content: content()
@ -226,7 +226,7 @@ extension Backport.LabeledContent: View where Wrapped == Any, Label: View, Conte
} }
@available(macOS 10.15, *) @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. /// Creates a labeled informational view.
/// ///
/// This initializer creates a ``Text`` label on your behalf, and treats the /// 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 /// - titleKey: The key for the view's localized title, that describes
/// the purpose of the view. /// the purpose of the view.
/// - value: The value being labeled. /// - value: The value being labeled.
public init<S: StringProtocol>(_ titleKey: LocalizedStringKey, value: S) { init<S: StringProtocol>(_ titleKey: LocalizedStringKey, value: S) {
config = .init( config = .init(
label: Text(titleKey), label: Text(titleKey),
content: Text(value) content: Text(value)
@ -266,7 +266,7 @@ extension Backport.LabeledContent where Wrapped == Any, Label == Text, Content =
/// - Parameters: /// - Parameters:
/// - title: A string that describes the purpose of the view. /// - title: A string that describes the purpose of the view.
/// - value: The value being labeled. /// - value: The value being labeled.
public init<S1: StringProtocol, S2: StringProtocol>(_ title: S1, value: S2) { init<S1: StringProtocol, S2: StringProtocol>(_ title: S1, value: S2) {
config = .init( config = .init(
label: Text(title), label: Text(title),
content: Text(value) content: Text(value)

View File

@ -5,8 +5,8 @@
import SwiftUI import SwiftUI
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension Backport where Wrapped == Any { public extension Backport where Wrapped == Any {
public struct AutomaticLabeledContentStyle: BackportLabeledContentStyle { struct AutomaticLabeledContentStyle: BackportLabeledContentStyle {
public func makeBody(configuration: Configuration) -> some View { public func makeBody(configuration: Configuration) -> some View {
HStack(alignment: .firstTextBaseline) { HStack(alignment: .firstTextBaseline) {
configuration.label configuration.label

View File

@ -47,7 +47,7 @@ extension Backport where Wrapped: View {
.environment( .environment(
\.navigationDestinations, \.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, *) @available(macOS 10.15, *)
extension EnvironmentValues { private extension EnvironmentValues {
fileprivate var navigationDestinations: [AnyMetaType: DestinationView] { var navigationDestinations: [AnyMetaType: DestinationView] {
get { self[NavigationDestinationsEnvironmentKey.self] } get { self[NavigationDestinationsEnvironmentKey.self] }
set { set {
var current = self[NavigationDestinationsEnvironmentKey.self] var current = self[NavigationDestinationsEnvironmentKey.self]
@ -127,8 +127,8 @@ extension AnyMetaType: Hashable {
} }
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension Dictionary { private extension Dictionary {
fileprivate subscript(_ key: Any.Type) -> Value? where Key == AnyMetaType { subscript(_ key: Any.Type) -> Value? where Key == AnyMetaType {
get { self[.init(type: key)] } get { self[.init(type: key)] }
_modify { yield &self[.init(type: key)] } _modify { yield &self[.init(type: key)] }
} }

View File

@ -169,8 +169,8 @@ private struct BackportOpenURLKey: EnvironmentKey {
} }
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension EnvironmentValues { public extension EnvironmentValues {
public var backportOpenURL: Backport<Any>.OpenURLAction { var backportOpenURL: Backport<Any>.OpenURLAction {
get { self[BackportOpenURLKey.self] } get { self[BackportOpenURLKey.self] }
set { self[BackportOpenURLKey.self] = newValue } set { self[BackportOpenURLKey.self] = newValue }
} }

View File

@ -5,7 +5,7 @@
import SwiftUI import SwiftUI
@available(macOS 10.15, *) @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. /// 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. /// 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. /// The last view that you list appears at the front of the stack.
/// ///
/// - Returns: A view that uses the specified content as a foreground. /// - Returns: A view that uses the specified content as a foreground.
public func overlay<Content: View>(alignment: Alignment = .center, @ViewBuilder _ content: () -> Content) -> some View func overlay<Content: View>(alignment: Alignment = .center, @ViewBuilder _ content: () -> Content) -> some View {
{
self.content.overlay(content(), alignment: alignment) self.content.overlay(content(), alignment: alignment)
} }
} }

View File

@ -104,7 +104,8 @@ extension Backport.ProgressView where Wrapped == Any {
/// task, meaning the task is complete if `value` equals `total`. The /// task, meaning the task is complete if `value` equals `total`. The
/// default value is `1.0`. /// default value is `1.0`.
public init<V>(value: V?, total: V = 1.0) public init<V>(value: V?, total: V = 1.0)
where Label == EmptyView, CurrentValueLabel == EmptyView, V: BinaryFloatingPoint { where Label == EmptyView, CurrentValueLabel == EmptyView, V: BinaryFloatingPoint
{
if let value = value { if let value = value {
config = .init(fractionCompleted: Double(value) / Double(total), preferredKind: .linear, max: Double(total)) config = .init(fractionCompleted: Double(value) / Double(total), preferredKind: .linear, max: Double(total))
} else { } else {
@ -130,7 +131,8 @@ extension Backport.ProgressView where Wrapped == Any {
/// - label: A view builder that creates a view that describes the task /// - label: A view builder that creates a view that describes the task
/// in progress. /// in progress.
public init<V>(value: V?, total: V = 1.0, @ViewBuilder label: () -> Label) public init<V>(value: V?, total: V = 1.0, @ViewBuilder label: () -> Label)
where CurrentValueLabel == EmptyView, V: BinaryFloatingPoint { where CurrentValueLabel == EmptyView, V: BinaryFloatingPoint
{
if let value = value { if let value = value {
config = .init( config = .init(
fractionCompleted: Double(value) / Double(total), label: .init(content: label()), preferredKind: .linear 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 /// task, meaning the task is complete if `value` equals `total`. The
/// default value is `1.0`. /// default value is `1.0`.
public init<V>(_ titleKey: LocalizedStringKey, value: V?, total: V = 1.0) public init<V>(_ 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 { if let value = value {
config = .init( config = .init(
fractionCompleted: Double(value) / Double(total), label: .init(content: Text(titleKey)), preferredKind: .linear, 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 /// task, meaning the task is complete if `value` equals `total`. The
/// default value is `1.0`. /// default value is `1.0`.
public init<S, V>(_ title: S, value: V?, total: V = 1.0) public init<S, V>(_ 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 { if let value = value {
config = .init( config = .init(
fractionCompleted: Double(value) / Double(total), label: .init(content: Text(title)), preferredKind: .linear, fractionCompleted: Double(value) / Double(total), label: .init(content: Text(title)), preferredKind: .linear,

View File

@ -42,8 +42,8 @@ extension Backport where Wrapped == Any {
} }
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension BackportProgressViewStyle where Self == Backport<Any>.CircularProgressViewStyle { public extension BackportProgressViewStyle where Self == Backport<Any>.CircularProgressViewStyle {
public static var circular: Self { .init() } static var circular: Self { .init() }
} }
#if os(macOS) #if os(macOS)

View File

@ -49,6 +49,6 @@ extension Backport where Wrapped == Any {
} }
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension BackportProgressViewStyle where Self == Backport<Any>.DefaultProgressViewStyle { public extension BackportProgressViewStyle where Self == Backport<Any>.DefaultProgressViewStyle {
public static var automatic: Self { .init() } static var automatic: Self { .init() }
} }

View File

@ -63,8 +63,8 @@ extension Backport where Wrapped == Any {
} }
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension BackportProgressViewStyle where Self == Backport<Any>.LinearProgressViewStyle { public extension BackportProgressViewStyle where Self == Backport<Any>.LinearProgressViewStyle {
public static var linear: Self { .init() } static var linear: Self { .init() }
} }
#if os(macOS) #if os(macOS)

View File

@ -9,7 +9,8 @@ import SwiftUI
final class PreviewController<Items>: UIViewController, UIAdaptivePresentationControllerDelegate, final class PreviewController<Items>: UIViewController, UIAdaptivePresentationControllerDelegate,
QLPreviewControllerDelegate, QLPreviewControllerDataSource QLPreviewControllerDelegate, QLPreviewControllerDataSource
where Items: RandomAccessCollection, Items.Element == URL { where Items: RandomAccessCollection, Items.Element == URL
{
var items: Items var items: Items
var selection: Binding<Items.Element?> { var selection: Binding<Items.Element?> {

View File

@ -10,7 +10,8 @@ import SwiftUI
@available(macOS 10.15, *) @available(macOS 10.15, *)
final class PreviewController<Items>: NSViewController, QLPreviewPanelDataSource, QLPreviewPanelDelegate final class PreviewController<Items>: NSViewController, QLPreviewPanelDataSource, QLPreviewPanelDelegate
where Items: RandomAccessCollection, Items.Element == URL { where Items: RandomAccessCollection, Items.Element == URL
{
private let panel = QLPreviewPanel.shared()! private let panel = QLPreviewPanel.shared()!
private weak var windowResponder: NSResponder? private weak var windowResponder: NSResponder?

View File

@ -29,7 +29,8 @@ extension Backport where Wrapped: View {
/// ///
/// - Returns: A view that presents the preview of the contents of the URL. /// - Returns: A view that presents the preview of the contents of the URL.
public func quickLookPreview<Items>(_ selection: Binding<Items.Element?>, in items: Items) -> some View public func quickLookPreview<Items>(_ selection: Binding<Items.Element?>, in items: Items) -> some View
where Items: RandomAccessCollection, Items.Element == URL { where Items: RandomAccessCollection, Items.Element == URL
{
#if os(iOS) || os(macOS) #if os(iOS) || os(macOS)
content.background(QuicklookSheet(selection: selection, items: items)) content.background(QuicklookSheet(selection: selection, items: items))
#else #else
@ -64,7 +65,8 @@ extension Backport where Wrapped: View {
@available(macOS 10.15, *) @available(macOS 10.15, *)
private struct QuicklookSheet<Items>: NSViewControllerRepresentable private struct QuicklookSheet<Items>: NSViewControllerRepresentable
where Items: RandomAccessCollection, Items.Element == URL { where Items: RandomAccessCollection, Items.Element == URL
{
let selection: Binding<Items.Element?> let selection: Binding<Items.Element?>
let items: Items let items: Items
@ -81,7 +83,8 @@ extension Backport where Wrapped: View {
#elseif os(iOS) #elseif os(iOS)
private struct QuicklookSheet<Items>: UIViewControllerRepresentable private struct QuicklookSheet<Items>: UIViewControllerRepresentable
where Items: RandomAccessCollection, Items.Element == URL { where Items: RandomAccessCollection, Items.Element == URL
{
let selection: Binding<Items.Element?> let selection: Binding<Items.Element?>
let items: Items let items: Items

View File

@ -3,14 +3,14 @@ import SwiftUI
#if os(iOS) || os(macOS) #if os(iOS) || os(macOS)
@available(macOS 10.15, *) @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. /// 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. /// 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, dont call it in response to a user action, such as a button tap. /// 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, dont 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. /// > 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<Any>.RequestReviewAction { .init() } @MainActor var backportRequestReview: Backport<Any>.RequestReviewAction { .init() }
} }
/// An instance that tells StoreKit to request an App Store rating or review from the user, if appropriate. /// An instance that tells StoreKit to request an App Store rating or review from the user, if appropriate.

View File

@ -33,13 +33,13 @@ extension Backport where Wrapped == Any {
} }
@available(macOS 10.15, *) @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. /// Creates a section with the provided section content.
/// - Parameters: /// - Parameters:
/// - titleKey: The key for the section's localized title, which describes /// - titleKey: The key for the section's localized title, which describes
/// the contents of the section. /// the contents of the section.
/// - content: The section's content. /// - content: The section's content.
public init(_ titleKey: LocalizedStringKey, @ViewBuilder content: @escaping () -> Content) { init(_ titleKey: LocalizedStringKey, @ViewBuilder content: @escaping () -> Content) {
header = { Text(titleKey) } header = { Text(titleKey) }
self.content = content self.content = content
footer = { EmptyView() } footer = { EmptyView() }
@ -50,7 +50,7 @@ extension Backport.Section where Wrapped == Any, Parent == Text, Footer == Empty
/// - Parameters: /// - Parameters:
/// - title: A string that describes the contents of the section. /// - title: A string that describes the contents of the section.
/// - content: The section's content. /// - content: The section's content.
public init<S>(_ title: S, @ViewBuilder content: @escaping () -> Content) where S: StringProtocol { init<S>(_ title: S, @ViewBuilder content: @escaping () -> Content) where S: StringProtocol {
header = { Text(title) } header = { Text(title) }
self.content = content self.content = content
footer = { EmptyView() } footer = { EmptyView() }

View File

@ -5,7 +5,7 @@
import SwiftUI import SwiftUI
@available(macOS 10.15, *) @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 views 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. /// Creates a transition that when added to a view will animate the views 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. /// - Parameter edge: the edge from which the view will be animated in.
/// - Returns: A transition that animates a view by moving and fading it. /// - Returns: A transition that animates a view by moving and fading it.
@ -13,7 +13,7 @@ extension Backport where Wrapped == AnyTransition {
@available(watchOS, deprecated: 9.0) @available(watchOS, deprecated: 9.0)
@available(macOS, deprecated: 13.0) @available(macOS, deprecated: 13.0)
@available(tvOS, deprecated: 16.0) @available(tvOS, deprecated: 16.0)
public func push(from edge: Edge) -> AnyTransition { func push(from edge: Edge) -> AnyTransition {
var oppositeEdge: Edge var oppositeEdge: Edge
switch edge { switch edge {
case .top: case .top:

View File

@ -189,8 +189,8 @@ extension Backport where Wrapped == Any {
#if os(iOS) #if os(iOS)
@available(iOS 15, *) @available(iOS 15, *)
extension Backport where Wrapped == Any { fileprivate extension Backport where Wrapped == Any {
fileprivate struct Representable: UIViewControllerRepresentable { struct Representable: UIViewControllerRepresentable {
let detents: Set<Backport<Any>.PresentationDetent> let detents: Set<Backport<Any>.PresentationDetent>
let selection: Binding<Backport<Any>.PresentationDetent>? let selection: Binding<Backport<Any>.PresentationDetent>?
let largestUndimmed: Backport<Any>.PresentationDetent? let largestUndimmed: Backport<Any>.PresentationDetent?
@ -207,8 +207,8 @@ extension Backport where Wrapped == Any {
@available(macOS 10.15, *) @available(macOS 10.15, *)
@available(iOS 15, *) @available(iOS 15, *)
extension Backport.Representable { fileprivate extension Backport.Representable {
fileprivate final class Controller: UIViewController, UISheetPresentationControllerDelegate { final class Controller: UIViewController, UISheetPresentationControllerDelegate {
var detents: Set<Backport<Any>.PresentationDetent> var detents: Set<Backport<Any>.PresentationDetent>
var selection: Binding<Backport<Any>.PresentationDetent>? var selection: Binding<Backport<Any>.PresentationDetent>?
var largestUndimmed: Backport<Any>.PresentationDetent? var largestUndimmed: Backport<Any>.PresentationDetent?

View File

@ -48,8 +48,8 @@ extension Backport where Wrapped: View {
#if os(iOS) #if os(iOS)
@available(iOS 15, *) @available(iOS 15, *)
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension Backport where Wrapped == Any { fileprivate extension Backport where Wrapped == Any {
fileprivate struct Representable: UIViewControllerRepresentable { struct Representable: UIViewControllerRepresentable {
let visibility: Backport<Any>.Visibility let visibility: Backport<Any>.Visibility
func makeUIViewController(context _: Context) -> Backport.Representable.Controller { func makeUIViewController(context _: Context) -> Backport.Representable.Controller {
@ -64,8 +64,8 @@ extension Backport where Wrapped: View {
@available(macOS 10.15, *) @available(macOS 10.15, *)
@available(iOS 15, *) @available(iOS 15, *)
extension Backport.Representable { fileprivate extension Backport.Representable {
fileprivate final class Controller: UIViewController { final class Controller: UIViewController {
var visibility: Backport<Any>.Visibility var visibility: Backport<Any>.Visibility
@available(macOS 10.15, *) @available(macOS 10.15, *)

View File

@ -53,8 +53,8 @@ extension Backport where Wrapped: View {
#if os(iOS) #if os(iOS)
@available(iOS 15, *) @available(iOS 15, *)
extension Backport where Wrapped == Any { fileprivate extension Backport where Wrapped == Any {
fileprivate struct Representable: UIViewControllerRepresentable { struct Representable: UIViewControllerRepresentable {
let identifier: Backport<Any>.PresentationDetent.Identifier? let identifier: Backport<Any>.PresentationDetent.Identifier?
func makeUIViewController(context _: Context) -> Backport.Representable.Controller { func makeUIViewController(context _: Context) -> Backport.Representable.Controller {
@ -68,8 +68,8 @@ extension Backport where Wrapped: View {
} }
@available(iOS 15, *) @available(iOS 15, *)
extension Backport.Representable { fileprivate extension Backport.Representable {
fileprivate final class Controller: UIViewController { final class Controller: UIViewController {
var identifier: Backport<Any>.PresentationDetent.Identifier? var identifier: Backport<Any>.PresentationDetent.Identifier?
init(identifier: Backport<Any>.PresentationDetent.Identifier?) { init(identifier: Backport<Any>.PresentationDetent.Identifier?) {

View File

@ -5,7 +5,7 @@
import SwiftUI import SwiftUI
@available(macOS 10.15, *) @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. /// Conditionally prevents interactive dismissal of a popover or a sheet.
/// ///
/// Users can dismiss certain kinds of presentations using built-in /// Users can dismiss certain kinds of presentations using built-in
@ -76,7 +76,7 @@ extension Backport where Wrapped: View {
@available(tvOS, deprecated: 16) @available(tvOS, deprecated: 16)
@available(macOS, deprecated: 13) @available(macOS, deprecated: 13)
@available(watchOS, deprecated: 9) @available(watchOS, deprecated: 9)
public func interactiveDismissDisabled(_ isDisabled: Bool = true) -> some View { func interactiveDismissDisabled(_ isDisabled: Bool = true) -> some View {
#if os(iOS) #if os(iOS)
if #available(iOS 15, *) { if #available(iOS 15, *) {
content.background(Backport<Any>.Representable(isModal: isDisabled, onAttempt: nil)) content.background(Backport<Any>.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. /// - 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. /// You can use this as an opportunity to present an confirmation or prompt to the user.
@ViewBuilder @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 os(iOS)
if #available(iOS 15, *) { if #available(iOS 15, *) {
content.background(Backport<Any>.Representable(isModal: isDisabled, onAttempt: onAttempt)) content.background(Backport<Any>.Representable(isModal: isDisabled, onAttempt: onAttempt))
@ -172,8 +172,8 @@ extension Backport where Wrapped: View {
#if os(iOS) #if os(iOS)
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension Backport where Wrapped == Any { fileprivate extension Backport where Wrapped == Any {
fileprivate struct Representable: UIViewControllerRepresentable { struct Representable: UIViewControllerRepresentable {
let isModal: Bool let isModal: Bool
let onAttempt: (() -> Void)? let onAttempt: (() -> Void)?
@ -190,8 +190,8 @@ extension Backport where Wrapped: View {
} }
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension Backport.Representable { fileprivate extension Backport.Representable {
fileprivate final class Controller: UIViewController, UIAdaptivePresentationControllerDelegate { final class Controller: UIViewController, UIAdaptivePresentationControllerDelegate {
var isModal: Bool var isModal: Bool
var onAttempt: (() -> Void)? var onAttempt: (() -> Void)?
weak var _delegate: UIAdaptivePresentationControllerDelegate? weak var _delegate: UIAdaptivePresentationControllerDelegate?

View File

@ -24,7 +24,7 @@ extension Backport where Wrapped == Any {
public var wrappedValue: Value { public var wrappedValue: Value {
#if os(iOS) || os(tvOS) #if os(iOS) || os(tvOS)
let traits = UITraitCollection(traitsFrom: [ let traits = UITraitCollection(traitsFrom: [
UITraitCollection(preferredContentSizeCategory: UIContentSizeCategory(sizeCategory: sizeCategory)) UITraitCollection(preferredContentSizeCategory: UIContentSizeCategory(sizeCategory: sizeCategory)),
]) ])
return Value(metrics.scaledValue(for: CGFloat(baseValue), compatibleWith: traits)) return Value(metrics.scaledValue(for: CGFloat(baseValue), compatibleWith: traits))
@ -59,8 +59,8 @@ extension Backport where Wrapped == Any {
} }
#if os(iOS) || os(tvOS) #if os(iOS) || os(tvOS)
extension UIContentSizeCategory { fileprivate extension UIContentSizeCategory {
fileprivate init(sizeCategory: ContentSizeCategory?) { init(sizeCategory: ContentSizeCategory?) {
switch sizeCategory { switch sizeCategory {
case .accessibilityExtraExtraExtraLarge: self = .accessibilityExtraExtraExtraLarge case .accessibilityExtraExtraExtraLarge: self = .accessibilityExtraExtraExtraLarge
case .accessibilityExtraExtraLarge: self = .accessibilityExtraExtraLarge case .accessibilityExtraExtraLarge: self = .accessibilityExtraExtraLarge

View File

@ -36,7 +36,8 @@ import SwiftUI
} }
*/ */
public struct UIHostingConfiguration<Label, Background>: BackportUIContentConfiguration public struct UIHostingConfiguration<Label, Background>: BackportUIContentConfiguration
where Label: View, Background: View { where Label: View, Background: View
{
var content: Label var content: Label
var background: AnyView? var background: AnyView?
var insets: ProposedInsets var insets: ProposedInsets
@ -57,7 +58,8 @@ import SwiftUI
/// - Parameter background: The contents of the SwiftUI hierarchy to be /// - Parameter background: The contents of the SwiftUI hierarchy to be
/// shown inside the background of the cell. /// shown inside the background of the cell.
public func background<B>(@ViewBuilder background: () -> B) -> Backport.UIHostingConfiguration<Label, B> public func background<B>(@ViewBuilder background: () -> B) -> Backport.UIHostingConfiguration<Label, B>
where B: View { where B: View
{
.init(content: content, background: AnyView(background()), insets: insets, minSize: minSize) .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. /// Sets the margins around the content of the configuration.
/// ///
/// Use this modifier to replace the default margins applied to the root of /// 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 /// use the system default values. The default value is
/// ``Edge/Set/all``. /// ``Edge/Set/all``.
/// - length: The amount to apply. /// - 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 var view = self
if edges.contains(.leading) { view.insets.leading = length } if edges.contains(.leading) { view.insets.leading = length }
if edges.contains(.trailing) { view.insets.trailing = length } if edges.contains(.trailing) { view.insets.trailing = length }
@ -138,7 +140,7 @@ import SwiftUI
/// use the system default values. The default value is /// use the system default values. The default value is
/// ``Edge/Set/all``. /// ``Edge/Set/all``.
/// - insets: The insets to apply. /// - 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 var view = self
if edges.contains(.leading) { view.insets.leading = insets.leading } if edges.contains(.leading) { view.insets.leading = insets.leading }
if edges.contains(.trailing) { view.insets.trailing = insets.trailing } if edges.contains(.trailing) { view.insets.trailing = insets.trailing }

View File

@ -4,19 +4,19 @@ import PackageDescription
let package = Package( let package = Package(
name: "SSPreferences", name: "SSPreferences",
platforms: [ platforms: [
.macOS(.v10_11) .macOS(.v10_11),
], ],
products: [ products: [
.library( .library(
name: "SSPreferences", name: "SSPreferences",
targets: [ targets: [
"SSPreferences" "SSPreferences",
] ]
) ),
], ],
targets: [ targets: [
.target( .target(
name: "SSPreferences" name: "SSPreferences"
) ),
] ]
) )

View File

@ -5,12 +5,12 @@
import SwiftUI import SwiftUI
@available(macOS 10.15, *) @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`. Function builder for `Preferences` components used in order to restrict types of child views to be of type `Section`.
*/ */
@resultBuilder @resultBuilder
public enum SectionBuilder { enum SectionBuilder {
public static func buildBlock(_ sections: Section...) -> [Section] { public static func buildBlock(_ sections: Section...) -> [Section] {
sections 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. 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 sectionBuilder: () -> [Section]
private let contentWidth: Double private let contentWidth: Double
private let minimumLabelWidth: Double private let minimumLabelWidth: Double
@ -49,7 +49,7 @@ extension SSPreferences {
let sections = sectionBuilder() let sections = sectionBuilder()
return VStack(alignment: .preferenceSectionLabel) { return VStack(alignment: .preferenceSectionLabel) {
ForEach(0..<sections.count, id: \.self) { index in ForEach(0 ..< sections.count, id: \.self) { index in
viewForSection(sections, index: index) viewForSection(sections, index: index)
} }
} }

View File

@ -15,13 +15,13 @@ public protocol PreferencePaneConvertible {
} }
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension SSPreferences { public extension SSPreferences {
/** /**
Create a SwiftUI-based preference pane. Create a SwiftUI-based preference pane.
SwiftUI equivalent of the `PreferencePane` protocol. SwiftUI equivalent of the `PreferencePane` protocol.
*/ */
public struct Pane<Content: View>: View, PreferencePaneConvertible { struct Pane<Content: View>: View, PreferencePaneConvertible {
let identifier: PaneIdentifier let identifier: PaneIdentifier
let title: String let title: String
let toolbarIcon: NSImage let toolbarIcon: NSImage
@ -49,7 +49,7 @@ extension SSPreferences {
/** /**
Hosting controller enabling `Preferences.Pane` to be used alongside AppKit `NSViewController`'s. Hosting controller enabling `Preferences.Pane` to be used alongside AppKit `NSViewController`'s.
*/ */
public final class PaneHostingController<Content: View>: NSHostingController<Content>, PreferencePane { final class PaneHostingController<Content: View>: NSHostingController<Content>, PreferencePane {
public let preferencePaneIdentifier: PaneIdentifier public let preferencePaneIdentifier: PaneIdentifier
public let preferencePaneTitle: String public let preferencePaneTitle: String
public let toolbarItemIcon: NSImage public let toolbarItemIcon: NSImage
@ -84,11 +84,11 @@ extension SSPreferences {
} }
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension View { public extension View {
/** /**
Applies font and color for a label used for describing a preference. 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)) font(.system(size: 11.0))
// TODO: Use `.foregroundStyle` when targeting macOS 12. // TODO: Use `.foregroundStyle` when targeting macOS 12.
.foregroundColor(.secondary) .foregroundColor(.secondary)

View File

@ -4,8 +4,8 @@
import Cocoa import Cocoa
extension SSPreferences { public extension SSPreferences {
public struct PaneIdentifier: Hashable, RawRepresentable, Codable { struct PaneIdentifier: Hashable, RawRepresentable, Codable {
public let rawValue: String public let rawValue: String
public init(rawValue: String) { public init(rawValue: String) {
@ -20,24 +20,24 @@ public protocol PreferencePane: NSViewController {
var toolbarItemIcon: NSImage { get } var toolbarItemIcon: NSImage { get }
} }
extension PreferencePane { public extension PreferencePane {
public var toolbarItemIdentifier: NSToolbarItem.Identifier { var toolbarItemIdentifier: NSToolbarItem.Identifier {
preferencePaneIdentifier.toolbarItemIdentifier preferencePaneIdentifier.toolbarItemIdentifier
} }
public var toolbarItemIcon: NSImage { .empty } var toolbarItemIcon: NSImage { .empty }
} }
extension SSPreferences.PaneIdentifier { public extension SSPreferences.PaneIdentifier {
public init(_ rawValue: String) { init(_ rawValue: String) {
self.init(rawValue: rawValue) self.init(rawValue: rawValue)
} }
public init(fromToolbarItemIdentifier itemIdentifier: NSToolbarItem.Identifier) { init(fromToolbarItemIdentifier itemIdentifier: NSToolbarItem.Identifier) {
self.init(rawValue: itemIdentifier.rawValue) self.init(rawValue: itemIdentifier.rawValue)
} }
public var toolbarItemIdentifier: NSToolbarItem.Identifier { var toolbarItemIdentifier: NSToolbarItem.Identifier {
NSToolbarItem.Identifier(rawValue) NSToolbarItem.Identifier(rawValue)
} }
} }

View File

@ -4,8 +4,8 @@
import Cocoa import Cocoa
extension SSPreferences { public extension SSPreferences {
public enum Style { enum Style {
case toolbarItems case toolbarItems
case segmentedControl case segmentedControl
} }

View File

@ -171,8 +171,7 @@ final class PreferencesTabViewController: NSViewController, PreferencesStyleCont
.isEmpty == false .isEmpty == false
if isAnimated { if isAnimated {
NSAnimationContext.runAnimationGroup( NSAnimationContext.runAnimationGroup({ context in
{ context in
context.allowsImplicitAnimation = true context.allowsImplicitAnimation = true
context.duration = 0.25 context.duration = 0.25
context.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) context.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
@ -184,8 +183,7 @@ final class PreferencesTabViewController: NSViewController, PreferencesStyleCont
options: options, options: options,
completionHandler: completion completionHandler: completion
) )
}, completionHandler: nil }, completionHandler: nil)
)
} else { } else {
super.transition( super.transition(
from: fromViewController, from: fromViewController,

View File

@ -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. /// 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) { if let target = super.supplementalTarget(forAction: action, sender: sender) {
return target return target
} }
@ -150,11 +150,11 @@ extension PreferencesWindowController {
} }
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension PreferencesWindowController { public extension PreferencesWindowController {
/** /**
Create a preferences window from only SwiftUI-based preference panes. Create a preferences window from only SwiftUI-based preference panes.
*/ */
public convenience init( convenience init(
panes: [PreferencePaneConvertible], panes: [PreferencePaneConvertible],
style: SSPreferences.Style = .toolbarItems, style: SSPreferences.Style = .toolbarItems,
animated: Bool = true, animated: Bool = true,

View File

@ -5,12 +5,12 @@
import SwiftUI import SwiftUI
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension SSPreferences { public extension SSPreferences {
/** /**
Represents a section with right-aligned title and optional bottom divider. Represents a section with right-aligned title and optional bottom divider.
*/ */
@available(macOS 10.15, *) @available(macOS 10.15, *)
public struct Section: View { struct Section: View {
/** /**
Preference key holding max width of section labels. Preference key holding max width of section labels.
*/ */

View File

@ -61,7 +61,7 @@ final class SegmentedControlStyleViewController: NSViewController, PreferencesSt
let title = preferencePane.preferencePaneTitle let title = preferencePane.preferencePaneTitle
let titleSize = title.size( let titleSize = title.size(
withAttributes: [ withAttributes: [
.font: NSFont.systemFont(ofSize: NSFont.systemFontSize(for: .regular)) .font: NSFont.systemFont(ofSize: NSFont.systemFontSize(for: .regular)),
] ]
) )

View File

@ -4,13 +4,13 @@ import PackageDescription
let package = Package( let package = Package(
name: "CandidateWindow", name: "CandidateWindow",
platforms: [ platforms: [
.macOS(.v10_11) .macOS(.v10_11),
], ],
products: [ products: [
.library( .library(
name: "CandidateWindow", name: "CandidateWindow",
targets: ["CandidateWindow"] targets: ["CandidateWindow"]
) ),
], ],
dependencies: [ dependencies: [
.package(path: "../vChewing_Shared"), .package(path: "../vChewing_Shared"),

View File

@ -149,8 +149,8 @@ public class CandidateCellData: Hashable {
// MARK: - Contents specifically made for macOS 12 and newer. // MARK: - Contents specifically made for macOS 12 and newer.
@available(macOS 12, *) @available(macOS 12, *)
extension CandidateCellData { public extension CandidateCellData {
public var attributedStringForSwiftUI: some View { var attributedStringForSwiftUI: some View {
var result: some View { var result: some View {
ZStack(alignment: .leading) { ZStack(alignment: .leading) {
if isSelected { if isSelected {
@ -179,8 +179,8 @@ extension CandidateCellData {
// MARK: - Contents specifically made for macOS 10.15 and macOS 11. // MARK: - Contents specifically made for macOS 10.15 and macOS 11.
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension CandidateCellData { public extension CandidateCellData {
public var themeColorBackports: some View { var themeColorBackports: some View {
// //
let result: Color = { let result: Color = {
switch locale { switch locale {
@ -193,7 +193,7 @@ extension CandidateCellData {
return result.opacity(0.85) return result.opacity(0.85)
} }
public var attributedStringForSwiftUIBackports: some View { var attributedStringForSwiftUIBackports: some View {
var result: some View { var result: some View {
ZStack(alignment: .leading) { ZStack(alignment: .leading) {
if isSelected { if isSelected {

View File

@ -96,7 +96,7 @@ public struct CandidatePool {
switch currentLayout { switch currentLayout {
case .horizontal: return rangeForLastHorizontalPageBlanked case .horizontal: return rangeForLastHorizontalPageBlanked
case .vertical: return rangeForLastVerticalPageBlanked case .vertical: return rangeForLastVerticalPageBlanked
@unknown default: return 0..<0 @unknown default: return 0 ..< 0
} }
} }
@ -104,7 +104,7 @@ public struct CandidatePool {
switch currentLayout { switch currentLayout {
case .horizontal: return rangeForCurrentHorizontalPage case .horizontal: return rangeForCurrentHorizontalPage
case .vertical: return rangeForCurrentVerticalPage case .vertical: return rangeForCurrentVerticalPage
@unknown default: return 0..<0 @unknown default: return 0 ..< 0
} }
} }
@ -206,19 +206,19 @@ extension CandidatePool {
} }
private var rangeForLastHorizontalPageBlanked: Range<Int> { private var rangeForLastHorizontalPageBlanked: Range<Int> {
0..<(maxRowsPerPage - rangeForCurrentHorizontalPage.count) 0 ..< (maxRowsPerPage - rangeForCurrentHorizontalPage.count)
} }
private var rangeForLastVerticalPageBlanked: Range<Int> { private var rangeForLastVerticalPageBlanked: Range<Int> {
0..<(maxColumnsPerPage - rangeForCurrentVerticalPage.count) 0 ..< (maxColumnsPerPage - rangeForCurrentVerticalPage.count)
} }
private var rangeForCurrentHorizontalPage: Range<Int> { private var rangeForCurrentHorizontalPage: Range<Int> {
currentRowNumber..<min(candidateRows.count, currentRowNumber + maxRowsPerPage) currentRowNumber ..< min(candidateRows.count, currentRowNumber + maxRowsPerPage)
} }
private var rangeForCurrentVerticalPage: Range<Int> { private var rangeForCurrentVerticalPage: Range<Int> {
currentColumnNumber..<min(candidateColumns.count, currentColumnNumber + maxColumnsPerPage) currentColumnNumber ..< min(candidateColumns.count, currentColumnNumber + maxColumnsPerPage)
} }
private mutating func selectNewNeighborRow(direction: VerticalDirection) { private mutating func selectNewNeighborRow(direction: VerticalDirection) {
@ -291,7 +291,7 @@ extension CandidatePool {
private mutating func highlightHorizontal(at indexSpecified: Int) { private mutating func highlightHorizontal(at indexSpecified: Int) {
var indexSpecified = indexSpecified var indexSpecified = indexSpecified
highlightedIndex = indexSpecified highlightedIndex = indexSpecified
if !(0..<candidateDataAll.count).contains(highlightedIndex) { if !(0 ..< candidateDataAll.count).contains(highlightedIndex) {
switch highlightedIndex { switch highlightedIndex {
case candidateDataAll.count...: case candidateDataAll.count...:
currentRowNumber = candidateRows.count - 1 currentRowNumber = candidateRows.count - 1
@ -324,7 +324,7 @@ extension CandidatePool {
private mutating func highlightVertical(at indexSpecified: Int) { private mutating func highlightVertical(at indexSpecified: Int) {
var indexSpecified = indexSpecified var indexSpecified = indexSpecified
highlightedIndex = indexSpecified highlightedIndex = indexSpecified
if !(0..<candidateDataAll.count).contains(highlightedIndex) { if !(0 ..< candidateDataAll.count).contains(highlightedIndex) {
switch highlightedIndex { switch highlightedIndex {
case candidateDataAll.count...: case candidateDataAll.count...:
currentColumnNumber = candidateColumns.count - 1 currentColumnNumber = candidateColumns.count - 1

View File

@ -160,7 +160,7 @@ public class CtlCandidateTDK: CtlCandidate {
return highlightNextCandidate() return highlightNextCandidate()
} }
if count <= 0 { return false } if count <= 0 { return false }
for _ in 0..<min(thePool.maxLinesPerPage, count) { for _ in 0 ..< min(thePool.maxLinesPerPage, count) {
thePool.selectNewNeighborLine(isForward: true) thePool.selectNewNeighborLine(isForward: true)
} }
updateDisplay() updateDisplay()
@ -180,7 +180,7 @@ public class CtlCandidateTDK: CtlCandidate {
return highlightPreviousCandidate() return highlightPreviousCandidate()
} }
if count <= 0 { return false } if count <= 0 { return false }
for _ in 0..<min(thePool.maxLinesPerPage, count) { for _ in 0 ..< min(thePool.maxLinesPerPage, count) {
thePool.selectNewNeighborLine(isForward: false) thePool.selectNewNeighborLine(isForward: false)
} }
updateDisplay() updateDisplay()
@ -211,7 +211,7 @@ public class CtlCandidateTDK: CtlCandidate {
override public func candidateIndexAtKeyLabelIndex(_ id: Int) -> Int { override public func candidateIndexAtKeyLabelIndex(_ id: Int) -> Int {
let arrCurrentLine = thePool.candidateLines[thePool.currentLineNumber] let arrCurrentLine = thePool.candidateLines[thePool.currentLineNumber]
if !(0..<arrCurrentLine.count).contains(id) { return -114_514 } if !(0 ..< arrCurrentLine.count).contains(id) { return -114_514 }
let actualID = max(0, min(id, arrCurrentLine.count - 1)) let actualID = max(0, min(id, arrCurrentLine.count - 1))
return arrCurrentLine[actualID].index return arrCurrentLine[actualID].index
} }
@ -241,8 +241,8 @@ extension CtlCandidateTDK {
} }
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension CtlCandidateTDK { public extension CtlCandidateTDK {
public var highlightedColorUIBackports: some View { var highlightedColorUIBackports: some View {
// //
let result: Color = { let result: Color = {
switch locale { switch locale {

View File

@ -106,7 +106,7 @@ public struct VwrCandidateVertical: View {
if thePool.maxLinesPerPage - thePool.rangeForCurrentPage.count > 0 { if thePool.maxLinesPerPage - thePool.rangeForCurrentPage.count > 0 {
ForEach(Array(thePool.rangeForLastPageBlanked.enumerated()), id: \.offset) { loopIndex, _ in ForEach(Array(thePool.rangeForLastPageBlanked.enumerated()), id: \.offset) { loopIndex, _ in
VStack(alignment: .leading, spacing: 0) { VStack(alignment: .leading, spacing: 0) {
ForEach(0..<thePool.maxLineCapacity, id: \.self) { _ in ForEach(0 ..< thePool.maxLineCapacity, id: \.self) { _ in
thePool.blankCell.attributedStringForSwiftUI.fixedSize() thePool.blankCell.attributedStringForSwiftUI.fixedSize()
.frame(width: Double(CandidateCellData.unifiedSize * 5), alignment: .topLeading) .frame(width: Double(CandidateCellData.unifiedSize * 5), alignment: .topLeading)
.contentShape(Rectangle()) .contentShape(Rectangle())

View File

@ -107,7 +107,7 @@ public struct VwrCandidateVerticalBackports: View {
if thePool.maxLinesPerPage - thePool.rangeForCurrentPage.count > 0 { if thePool.maxLinesPerPage - thePool.rangeForCurrentPage.count > 0 {
ForEach(Array(thePool.rangeForLastPageBlanked.enumerated()), id: \.offset) { loopIndex, _ in ForEach(Array(thePool.rangeForLastPageBlanked.enumerated()), id: \.offset) { loopIndex, _ in
VStack(alignment: .leading, spacing: 0) { VStack(alignment: .leading, spacing: 0) {
ForEach(0..<thePool.maxLineCapacity, id: \.self) { _ in ForEach(0 ..< thePool.maxLineCapacity, id: \.self) { _ in
thePool.blankCell.attributedStringForSwiftUIBackports.fixedSize() thePool.blankCell.attributedStringForSwiftUIBackports.fixedSize()
.frame(width: Double(CandidateCellData.unifiedSize * 5), alignment: .topLeading) .frame(width: Double(CandidateCellData.unifiedSize * 5), alignment: .topLeading)
.contentShape(Rectangle()) .contentShape(Rectangle())

View File

@ -4,13 +4,13 @@ import PackageDescription
let package = Package( let package = Package(
name: "CocoaExtension", name: "CocoaExtension",
platforms: [ platforms: [
.macOS(.v10_11) .macOS(.v10_11),
], ],
products: [ products: [
.library( .library(
name: "CocoaExtension", name: "CocoaExtension",
targets: ["CocoaExtension"] targets: ["CocoaExtension"]
) ),
], ],
dependencies: [ dependencies: [
.package(path: "../vChewing_IMKUtils"), .package(path: "../vChewing_IMKUtils"),
@ -23,6 +23,6 @@ let package = Package(
.product(name: "IMKUtils", package: "vChewing_IMKUtils"), .product(name: "IMKUtils", package: "vChewing_IMKUtils"),
.product(name: "SwiftExtension", package: "vChewing_SwiftExtension"), .product(name: "SwiftExtension", package: "vChewing_SwiftExtension"),
] ]
) ),
] ]
) )

View File

@ -11,22 +11,22 @@ import SwiftExtension
// MARK: - NSMutableString extension // MARK: - NSMutableString extension
extension NSMutableString { public extension NSMutableString {
public var localized: String { NSLocalizedString(description, comment: "") } var localized: String { NSLocalizedString(description, comment: "") }
} }
// MARK: - NSRect Extension // MARK: - NSRect Extension
extension NSRect { public extension NSRect {
public static var seniorTheBeast: NSRect { static var seniorTheBeast: NSRect {
NSRect(x: 0.0, y: 0.0, width: 0.114, height: 0.514) NSRect(x: 0.0, y: 0.0, width: 0.114, height: 0.514)
} }
} }
// MARK: - Shell Extension // MARK: - Shell Extension
extension NSApplication { public extension NSApplication {
public static func shell(_ command: String) throws -> String { static func shell(_ command: String) throws -> String {
let task = Process() let task = Process()
let pipe = Pipe() let pipe = Pipe()
@ -59,10 +59,10 @@ extension NSApplication {
} }
} }
extension NSApplication { public extension NSApplication {
// MARK: - System Dark Mode Status Detector. // MARK: - System Dark Mode Status Detector.
public static var isDarkMode: Bool { static var isDarkMode: Bool {
if #unavailable(macOS 10.14) { return false } if #unavailable(macOS 10.14) { return false }
if #available(macOS 10.15, *) { if #available(macOS 10.15, *) {
let appearanceDescription = NSApp.effectiveAppearance.debugDescription let appearanceDescription = NSApp.effectiveAppearance.debugDescription
@ -76,23 +76,23 @@ extension NSApplication {
// MARK: - Tell whether this IME is running with Root privileges. // MARK: - Tell whether this IME is running with Root privileges.
public static var isSudoMode: Bool { static var isSudoMode: Bool {
NSUserName() == "root" NSUserName() == "root"
} }
} }
// MARK: - Real Home Dir for Sandboxed Apps // MARK: - Real Home Dir for Sandboxed Apps
extension FileManager { public extension FileManager {
public static let realHomeDir = URL( static let realHomeDir = URL(
fileURLWithFileSystemRepresentation: getpwuid(getuid()).pointee.pw_dir, isDirectory: true, relativeTo: nil fileURLWithFileSystemRepresentation: getpwuid(getuid()).pointee.pw_dir, isDirectory: true, relativeTo: nil
) )
} }
// MARK: - Trash a file if it exists. // MARK: - Trash a file if it exists.
extension FileManager { public extension FileManager {
@discardableResult public static func trashTargetIfExists(_ path: String) -> Bool { @discardableResult static func trashTargetIfExists(_ path: String) -> Bool {
do { do {
if FileManager.default.fileExists(atPath: path) { if FileManager.default.fileExists(atPath: path) {
// //
@ -113,9 +113,9 @@ extension FileManager {
// MARK: - Memory Footprint Calculator // MARK: - Memory Footprint Calculator
// Ref: https://developer.apple.com/forums/thread/105088?answerId=357415022#357415022 // 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. /// 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 // 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. // complex for the Swift C importer, so we have to define them ourselves.
let tskVMInfoCount = mach_msg_type_number_t( let tskVMInfoCount = mach_msg_type_number_t(
@ -136,11 +136,11 @@ extension NSApplication {
// MARK: - Check whether current date is the given date. // MARK: - Check whether current date is the given date.
extension Date { public extension Date {
/// Check whether current date is the given 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. /// - 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. /// - 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 let currentYear = Self.currentYear
var dateDigits = dateDigits var dateDigits = dateDigits
let strDateDigits = dateDigits.description let strDateDigits = dateDigits.description
@ -162,7 +162,7 @@ extension Date {
return false return false
} }
public static var currentYear: Int { static var currentYear: Int {
let formatter = DateFormatter() let formatter = DateFormatter()
formatter.dateFormat = "yyyy" formatter.dateFormat = "yyyy"
return (Int(formatter.string(from: Date())) ?? 1970) return (Int(formatter.string(from: Date())) ?? 1970)

View File

@ -11,8 +11,8 @@ import IMKUtils
// MARK: - NSEvent Extension - Reconstructors // MARK: - NSEvent Extension - Reconstructors
extension NSEvent { public extension NSEvent {
public func reinitiate( func reinitiate(
with type: NSEvent.EventType? = nil, with type: NSEvent.EventType? = nil,
location: NSPoint? = nil, location: NSPoint? = nil,
modifierFlags: NSEvent.ModifierFlags? = nil, modifierFlags: NSEvent.ModifierFlags? = nil,
@ -44,7 +44,7 @@ extension NSEvent {
/// Emacs NSEvent NSEvent NSEvent /// Emacs NSEvent NSEvent NSEvent
/// - Parameter isVerticalTyping: /// - Parameter isVerticalTyping:
/// - Returns: /// - Returns:
public func convertFromEmacsKeyEvent(isVerticalContext: Bool) -> NSEvent { func convertFromEmacsKeyEvent(isVerticalContext: Bool) -> NSEvent {
guard isEmacsKey else { return self } guard isEmacsKey else { return self }
let newKeyCode: UInt16 = { let newKeyCode: UInt16 = {
switch isVerticalContext { switch isVerticalContext {
@ -76,15 +76,15 @@ extension NSEvent {
// MARK: - NSEvent Extension - InputSignalProtocol // MARK: - NSEvent Extension - InputSignalProtocol
extension NSEvent { public extension NSEvent {
public var isTypingVertical: Bool { charactersIgnoringModifiers == "Vertical" } var isTypingVertical: Bool { charactersIgnoringModifiers == "Vertical" }
public var text: String { characters ?? "" } var text: String { characters ?? "" }
public var inputTextIgnoringModifiers: String? { var inputTextIgnoringModifiers: String? {
guard charactersIgnoringModifiers != nil else { return nil } guard charactersIgnoringModifiers != nil else { return nil }
return charactersIgnoringModifiers ?? characters ?? "" return charactersIgnoringModifiers ?? characters ?? ""
} }
public var charCode: UInt16 { var charCode: UInt16 {
guard type != .flagsChanged else { return 0 } guard type != .flagsChanged else { return 0 }
guard characters != nil else { return 0 } guard characters != nil else { return 0 }
// count > 0!isEmpty滿 // count > 0!isEmpty滿
@ -94,9 +94,9 @@ extension NSEvent {
return result <= UInt16.max ? UInt16(result) : UInt16.max 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 // isControlHold
[6, 2, 1, 5, 4, 22].contains(charCode) && modifierFlags == .control [6, 2, 1, 5, 4, 22].contains(charCode) && modifierFlags == .control
} }
@ -104,86 +104,86 @@ extension NSEvent {
// Alt+Shift+ macOS // Alt+Shift+ macOS
// KeyCode // KeyCode
// //
public var mainAreaNumKeyChar: String? { mapMainAreaNumKey[keyCode] } var mainAreaNumKeyChar: String? { mapMainAreaNumKey[keyCode] }
// ANSI charCode InputHandler // ANSI charCode InputHandler
public var isInvalid: Bool { var isInvalid: Bool {
(0x20...0xFF).contains(charCode) ? false : !(isReservedKey && !isKeyCodeBlacklisted) (0x20 ... 0xFF).contains(charCode) ? false : !(isReservedKey && !isKeyCodeBlacklisted)
} }
public var isKeyCodeBlacklisted: Bool { var isKeyCodeBlacklisted: Bool {
guard let code = KeyCodeBlackListed(rawValue: keyCode) else { return false } guard let code = KeyCodeBlackListed(rawValue: keyCode) else { return false }
return code.rawValue != KeyCode.kNone.rawValue return code.rawValue != KeyCode.kNone.rawValue
} }
public var isReservedKey: Bool { var isReservedKey: Bool {
guard let code = KeyCode(rawValue: keyCode) else { return false } guard let code = KeyCode(rawValue: keyCode) else { return false }
return code.rawValue != KeyCode.kNone.rawValue return code.rawValue != KeyCode.kNone.rawValue
} }
/// flags KeyCode /// flags KeyCode
public var isJISAlphanumericalKey: Bool { KeyCode(rawValue: keyCode) == KeyCode.kJISAlphanumericalKey } var isJISAlphanumericalKey: Bool { KeyCode(rawValue: keyCode) == KeyCode.kJISAlphanumericalKey }
public var isJISKanaSwappingKey: Bool { KeyCode(rawValue: keyCode) == KeyCode.kJISKanaSwappingKey } var isJISKanaSwappingKey: Bool { KeyCode(rawValue: keyCode) == KeyCode.kJISKanaSwappingKey }
public var isNumericPadKey: Bool { arrNumpadKeyCodes.contains(keyCode) } var isNumericPadKey: Bool { arrNumpadKeyCodes.contains(keyCode) }
public var isMainAreaNumKey: Bool { arrMainAreaNumKey.contains(keyCode) } var isMainAreaNumKey: Bool { arrMainAreaNumKey.contains(keyCode) }
public var isShiftHold: Bool { modifierFlags.contains([.shift]) } var isShiftHold: Bool { modifierFlags.contains([.shift]) }
public var isCommandHold: Bool { modifierFlags.contains([.command]) } var isCommandHold: Bool { modifierFlags.contains([.command]) }
public var isControlHold: Bool { modifierFlags.contains([.control]) } var isControlHold: Bool { modifierFlags.contains([.control]) }
public var isControlHotKey: Bool { modifierFlags.contains([.control]) && text.first?.isLetter ?? false } var isControlHotKey: Bool { modifierFlags.contains([.control]) && text.first?.isLetter ?? false }
public var isOptionHold: Bool { modifierFlags.contains([.option]) } var isOptionHold: Bool { modifierFlags.contains([.option]) }
public var isOptionHotKey: Bool { modifierFlags.contains([.option]) && text.first?.isLetter ?? false } var isOptionHotKey: Bool { modifierFlags.contains([.option]) && text.first?.isLetter ?? false }
public var isCapsLockOn: Bool { modifierFlags.contains([.capsLock]) } var isCapsLockOn: Bool { modifierFlags.contains([.capsLock]) }
public var isFunctionKeyHold: Bool { modifierFlags.contains([.function]) } var isFunctionKeyHold: Bool { modifierFlags.contains([.function]) }
public var isNonLaptopFunctionKey: Bool { modifierFlags.contains([.numericPad]) && !isNumericPadKey } var isNonLaptopFunctionKey: Bool { modifierFlags.contains([.numericPad]) && !isNumericPadKey }
public var isEnter: Bool { [KeyCode.kCarriageReturn, KeyCode.kLineFeed].contains(KeyCode(rawValue: keyCode)) } var isEnter: Bool { [KeyCode.kCarriageReturn, KeyCode.kLineFeed].contains(KeyCode(rawValue: keyCode)) }
public var isTab: Bool { KeyCode(rawValue: keyCode) == KeyCode.kTab } var isTab: Bool { KeyCode(rawValue: keyCode) == KeyCode.kTab }
public var isUp: Bool { KeyCode(rawValue: keyCode) == KeyCode.kUpArrow } var isUp: Bool { KeyCode(rawValue: keyCode) == KeyCode.kUpArrow }
public var isDown: Bool { KeyCode(rawValue: keyCode) == KeyCode.kDownArrow } var isDown: Bool { KeyCode(rawValue: keyCode) == KeyCode.kDownArrow }
public var isLeft: Bool { KeyCode(rawValue: keyCode) == KeyCode.kLeftArrow } var isLeft: Bool { KeyCode(rawValue: keyCode) == KeyCode.kLeftArrow }
public var isRight: Bool { KeyCode(rawValue: keyCode) == KeyCode.kRightArrow } var isRight: Bool { KeyCode(rawValue: keyCode) == KeyCode.kRightArrow }
public var isPageUp: Bool { KeyCode(rawValue: keyCode) == KeyCode.kPageUp } var isPageUp: Bool { KeyCode(rawValue: keyCode) == KeyCode.kPageUp }
public var isPageDown: Bool { KeyCode(rawValue: keyCode) == KeyCode.kPageDown } var isPageDown: Bool { KeyCode(rawValue: keyCode) == KeyCode.kPageDown }
public var isSpace: Bool { KeyCode(rawValue: keyCode) == KeyCode.kSpace } var isSpace: Bool { KeyCode(rawValue: keyCode) == KeyCode.kSpace }
public var isBackSpace: Bool { KeyCode(rawValue: keyCode) == KeyCode.kBackSpace } var isBackSpace: Bool { KeyCode(rawValue: keyCode) == KeyCode.kBackSpace }
public var isEsc: Bool { KeyCode(rawValue: keyCode) == KeyCode.kEscape } var isEsc: Bool { KeyCode(rawValue: keyCode) == KeyCode.kEscape }
public var isHome: Bool { KeyCode(rawValue: keyCode) == KeyCode.kHome } var isHome: Bool { KeyCode(rawValue: keyCode) == KeyCode.kHome }
public var isEnd: Bool { KeyCode(rawValue: keyCode) == KeyCode.kEnd } var isEnd: Bool { KeyCode(rawValue: keyCode) == KeyCode.kEnd }
public var isDelete: Bool { KeyCode(rawValue: keyCode) == KeyCode.kWindowsDelete } var isDelete: Bool { KeyCode(rawValue: keyCode) == KeyCode.kWindowsDelete }
public var isCursorBackward: Bool { var isCursorBackward: Bool {
isTypingVertical isTypingVertical
? KeyCode(rawValue: keyCode) == .kUpArrow ? KeyCode(rawValue: keyCode) == .kUpArrow
: KeyCode(rawValue: keyCode) == .kLeftArrow : KeyCode(rawValue: keyCode) == .kLeftArrow
} }
public var isCursorForward: Bool { var isCursorForward: Bool {
isTypingVertical isTypingVertical
? KeyCode(rawValue: keyCode) == .kDownArrow ? KeyCode(rawValue: keyCode) == .kDownArrow
: KeyCode(rawValue: keyCode) == .kRightArrow : KeyCode(rawValue: keyCode) == .kRightArrow
} }
public var isCursorClockRight: Bool { var isCursorClockRight: Bool {
isTypingVertical isTypingVertical
? KeyCode(rawValue: keyCode) == .kRightArrow ? KeyCode(rawValue: keyCode) == .kRightArrow
: KeyCode(rawValue: keyCode) == .kUpArrow : KeyCode(rawValue: keyCode) == .kUpArrow
} }
public var isCursorClockLeft: Bool { var isCursorClockLeft: Bool {
isTypingVertical isTypingVertical
? KeyCode(rawValue: keyCode) == .kLeftArrow ? KeyCode(rawValue: keyCode) == .kLeftArrow
: KeyCode(rawValue: keyCode) == .kDownArrow : KeyCode(rawValue: keyCode) == .kDownArrow
} }
public var isASCII: Bool { charCode < 0x80 } var isASCII: Bool { charCode < 0x80 }
// flags == .shift Shift // flags == .shift Shift
public var isUpperCaseASCIILetterKey: Bool { var isUpperCaseASCIILetterKey: Bool {
(65...90).contains(charCode) && modifierFlags == .shift (65 ... 90).contains(charCode) && modifierFlags == .shift
} }
// KeyCode macOS Apple // KeyCode macOS Apple
// ![input isShiftHold] 使 Shift // ![input isShiftHold] 使 Shift
public var isSymbolMenuPhysicalKey: Bool { var isSymbolMenuPhysicalKey: Bool {
[KeyCode.kSymbolMenuPhysicalKeyIntl, KeyCode.kSymbolMenuPhysicalKeyJIS].contains(KeyCode(rawValue: keyCode)) [KeyCode.kSymbolMenuPhysicalKeyIntl, KeyCode.kSymbolMenuPhysicalKeyJIS].contains(KeyCode(rawValue: keyCode))
} }
} }
@ -323,8 +323,8 @@ let arrAppleABCKeyboardMap: [UInt16: (String, String)] = [
45: ("n", "N"), 46: ("m", "M"), 43: (",", "<"), 47: (".", ">"), 44: ("/", "?"), 45: ("n", "N"), 46: ("m", "M"), 43: (",", "<"), 47: (".", ">"), 44: ("/", "?"),
] ]
extension NSEvent { public extension NSEvent {
public var inAppleABCStaticForm: NSEvent { var inAppleABCStaticForm: NSEvent {
if type == .flagsChanged { return self } if type == .flagsChanged { return self }
guard modifierFlags == .shift || modifierFlags == [] else { return self } guard modifierFlags == .shift || modifierFlags == [] else { return self }
if !arrAppleABCKeyboardMap.keys.contains(keyCode) { return self } if !arrAppleABCKeyboardMap.keys.contains(keyCode) { return self }

View File

@ -9,8 +9,8 @@
import AVFoundation import AVFoundation
import Cocoa import Cocoa
extension NSSound { public extension NSSound {
public static func buzz(fart: Bool = false) { static func buzz(fart: Bool = false) {
let filePath = Bundle.main.path(forResource: fart ? "Fart" : "Beep", ofType: "m4a")! let filePath = Bundle.main.path(forResource: fart ? "Fart" : "Beep", ofType: "m4a")!
let fileURL = URL(fileURLWithPath: filePath) let fileURL = URL(fileURLWithPath: filePath)
var soundID: SystemSoundID = 0 var soundID: SystemSoundID = 0
@ -18,12 +18,12 @@ extension NSSound {
AudioServicesPlaySystemSound(soundID) AudioServicesPlaySystemSound(soundID)
} }
public static func buzz(fart: Bool = false, count: Int) { static func buzz(fart: Bool = false, count: Int) {
if count <= 1 { if count <= 1 {
NSSound.buzz(fart: fart) NSSound.buzz(fart: fart)
return return
} }
for _ in 0...count { for _ in 0 ... count {
NSSound.buzz(fart: fart) NSSound.buzz(fart: fart)
usleep(500_000) usleep(500_000)
} }

View File

@ -9,8 +9,8 @@
import Cocoa import Cocoa
import InputMethodKit import InputMethodKit
extension NSWindowController { public extension NSWindowController {
public func orderFront() { func orderFront() {
window?.orderFront(self) window?.orderFront(self)
} }
@ -21,7 +21,7 @@ extension NSWindowController {
/// - Parameters: /// - Parameters:
/// - windowTopLeftPoint: /// - windowTopLeftPoint:
/// - heightDelta: /// - heightDelta:
public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight heightDelta: Double, useGCD: Bool) { func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight heightDelta: Double, useGCD: Bool) {
func doSet() { func doSet() {
guard let window = window, var screenFrame = NSScreen.main?.visibleFrame else { return } guard let window = window, var screenFrame = NSScreen.main?.visibleFrame else { return }
let windowSize = window.frame.size let windowSize = window.frame.size
@ -48,8 +48,8 @@ extension NSWindowController {
} }
} }
extension NSWindow { public extension NSWindow {
@discardableResult public func callAlert(title: String, text: String? = nil) -> NSApplication.ModalResponse { @discardableResult func callAlert(title: String, text: String? = nil) -> NSApplication.ModalResponse {
let alert = NSAlert() let alert = NSAlert()
alert.messageText = title alert.messageText = title
if let text = text { alert.informativeText = text } 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: /// - Parameters:
/// - windowTopLeftPoint: /// - windowTopLeftPoint:
/// - heightDelta: /// - heightDelta:
public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight heightDelta: Double, useGCD: Bool) { func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight heightDelta: Double, useGCD: Bool) {
func doSet() { func doSet() {
DispatchQueue.main.async { [self] in DispatchQueue.main.async { [self] in
guard var screenFrame = NSScreen.main?.visibleFrame else { return } guard var screenFrame = NSScreen.main?.visibleFrame else { return }

View File

@ -7,8 +7,8 @@ import SwiftUI
// MARK: Model // MARK: Model
extension NSWindow { public extension NSWindow {
public struct Position { struct Position {
public static let defaultPadding: CGFloat = 16 public static let defaultPadding: CGFloat = 16
public var vertical: Vertical public var vertical: Vertical
@ -17,28 +17,28 @@ extension NSWindow {
} }
} }
extension NSWindow.Position { public extension NSWindow.Position {
public enum Horizontal { enum Horizontal {
case left, center, right case left, center, right
} }
public enum Vertical { enum Vertical {
case top, center, bottom case top, center, bottom
} }
} }
// MARK: Logic // MARK: Logic
extension NSWindow.Position { public extension NSWindow.Position {
public func value(forWindow windowRect: CGRect, inScreen screenRect: CGRect) -> CGPoint { func value(forWindow windowRect: CGRect, inScreen screenRect: CGRect) -> CGPoint {
let xPosition = horizontal.valueFor( let xPosition = horizontal.valueFor(
screenRange: screenRect.minX..<screenRect.maxX, screenRange: screenRect.minX ..< screenRect.maxX,
width: windowRect.width, width: windowRect.width,
padding: padding padding: padding
) )
let yPosition = vertical.valueFor( let yPosition = vertical.valueFor(
screenRange: screenRect.minY..<screenRect.maxY, screenRange: screenRect.minY ..< screenRect.maxY,
height: windowRect.height, height: windowRect.height,
padding: padding padding: padding
) )
@ -47,8 +47,8 @@ extension NSWindow.Position {
} }
} }
extension NSWindow.Position.Horizontal { public extension NSWindow.Position.Horizontal {
public func valueFor( func valueFor(
screenRange: Range<CGFloat>, screenRange: Range<CGFloat>,
width: CGFloat, width: CGFloat,
padding: CGFloat padding: CGFloat
@ -63,8 +63,8 @@ extension NSWindow.Position.Horizontal {
} }
} }
extension NSWindow.Position.Vertical { public extension NSWindow.Position.Vertical {
public func valueFor( func valueFor(
screenRange: Range<CGFloat>, screenRange: Range<CGFloat>,
height: CGFloat, height: CGFloat,
padding: CGFloat padding: CGFloat
@ -81,14 +81,14 @@ extension NSWindow.Position.Vertical {
// MARK: - AppKit extension // MARK: - AppKit extension
extension NSWindow { public extension NSWindow {
public func setPosition(_ position: Position, in screen: NSScreen?) { func setPosition(_ position: Position, in screen: NSScreen?) {
guard let visibleFrame = (screen ?? self.screen)?.visibleFrame else { return } guard let visibleFrame = (screen ?? self.screen)?.visibleFrame else { return }
let origin = position.value(forWindow: frame, inScreen: visibleFrame) let origin = position.value(forWindow: frame, inScreen: visibleFrame)
setFrameOrigin(origin) setFrameOrigin(origin)
} }
public func setPosition( func setPosition(
vertical: Position.Vertical, vertical: Position.Vertical,
horizontal: Position.Horizontal, horizontal: Position.Horizontal,
padding: CGFloat = Position.defaultPadding, padding: CGFloat = Position.defaultPadding,
@ -136,8 +136,8 @@ extension NSWindow {
} }
@available(macOS 10.15, *) @available(macOS 10.15, *)
extension View { public extension View {
public func hostingWindowPosition( func hostingWindowPosition(
vertical: NSWindow.Position.Vertical, vertical: NSWindow.Position.Vertical,
horizontal: NSWindow.Position.Horizontal, horizontal: NSWindow.Position.Horizontal,
padding: CGFloat = NSWindow.Position.defaultPadding, padding: CGFloat = NSWindow.Position.defaultPadding,

View File

@ -4,13 +4,13 @@ import PackageDescription
let package = Package( let package = Package(
name: "Hotenka", name: "Hotenka",
platforms: [ platforms: [
.macOS(.v10_11) .macOS(.v10_11),
], ],
products: [ products: [
.library( .library(
name: "Hotenka", name: "Hotenka",
targets: ["Hotenka"] targets: ["Hotenka"]
) ),
], ],
dependencies: [], dependencies: [],
targets: [ targets: [

View File

@ -153,7 +153,7 @@ public class HotenkaChineseConverter {
innerloop: while j > 0 { innerloop: while j > 0 {
let start = target.index(target.startIndex, offsetBy: i) let start = target.index(target.startIndex, offsetBy: i)
let end = target.index(target.startIndex, offsetBy: i + j) let end = target.index(target.startIndex, offsetBy: i + j)
guard let useDictSubStr = useDict[String(target[start..<end])] else { guard let useDictSubStr = useDict[String(target[start ..< end])] else {
j -= 1 j -= 1
continue continue
} }
@ -164,7 +164,7 @@ public class HotenkaChineseConverter {
if j == 0 { if j == 0 {
let start = target.index(target.startIndex, offsetBy: i) let start = target.index(target.startIndex, offsetBy: i)
let end = target.index(target.startIndex, offsetBy: i + 1) let end = target.index(target.startIndex, offsetBy: i + 1)
result = result + String(target[start..<end]) result = result + String(target[start ..< end])
i += 1 i += 1
} else { } else {
i += j i += j
@ -177,18 +177,18 @@ public class HotenkaChineseConverter {
// MARK: - String extensions // MARK: - String extensions
extension String { private extension String {
fileprivate func range(of str: String) -> Range<Int> { func range(of str: String) -> Range<Int> {
var start = -1 var start = -1
withCString { bytes in withCString { bytes in
str.withCString { sbytes in str.withCString { sbytes in
start = strstr(bytes, sbytes) - UnsafeMutablePointer<Int8>(mutating: bytes) start = strstr(bytes, sbytes) - UnsafeMutablePointer<Int8>(mutating: bytes)
} }
} }
return start < 0 ? 0..<0 : start..<start + str.utf8.count return start < 0 ? 0 ..< 0 : start ..< start + str.utf8.count
} }
fileprivate func substring(to index: Int) -> String { func substring(to index: Int) -> String {
var out = self var out = self
withCString { bytes in withCString { bytes in
let bytes = UnsafeMutablePointer<Int8>(mutating: bytes) let bytes = UnsafeMutablePointer<Int8>(mutating: bytes)
@ -198,7 +198,7 @@ extension String {
return out return out
} }
fileprivate func substring(from index: Int) -> String { func substring(from index: Int) -> String {
var out = self var out = self
withCString { bytes in withCString { bytes in
out = String(cString: bytes + index) out = String(cString: bytes + index)

View File

@ -1,6 +1,7 @@
.PHONY: format .PHONY: lint format
format: format:
swiftformat ./ --swiftversion 5.5 @swiftformat --swiftversion 5.5 --indent 2 ./
@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 lint:
@git ls-files --exclude-standard | grep -E '\.swift$$' | swiftlint --fix --autocorrect

View File

@ -4,19 +4,19 @@ import PackageDescription
let package = Package( let package = Package(
name: "IMKUtils", name: "IMKUtils",
platforms: [ platforms: [
.macOS(.v10_11) .macOS(.v10_11),
], ],
products: [ products: [
.library( .library(
name: "IMKUtils", name: "IMKUtils",
targets: ["IMKUtils"] targets: ["IMKUtils"]
) ),
], ],
dependencies: [], dependencies: [],
targets: [ targets: [
.target( .target(
name: "IMKUtils", name: "IMKUtils",
dependencies: [] dependencies: []
) ),
] ]
) )

View File

@ -92,8 +92,8 @@ public enum IMKHelper {
// MARK: - // MARK: -
extension IMKHelper { public extension IMKHelper {
@discardableResult public static func registerInputMethod() -> Int32 { @discardableResult static func registerInputMethod() -> Int32 {
TISInputSource.registerInputMethod() ? 0 : -1 TISInputSource.registerInputMethod() ? 0 : -1
} }
} }

View File

@ -11,12 +11,12 @@ import InputMethodKit
// MARK: - TISInputSource Extension by The vChewing Project (MIT-NTL License). // MARK: - TISInputSource Extension by The vChewing Project (MIT-NTL License).
extension TISInputSource { public extension TISInputSource {
public static var allRegisteredInstancesOfThisInputMethod: [TISInputSource] { static var allRegisteredInstancesOfThisInputMethod: [TISInputSource] {
TISInputSource.modes.compactMap { TISInputSource.generate(from: $0) } 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], 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 { else {
@ -25,7 +25,7 @@ extension TISInputSource {
return tsInputModeListKey.keys.map { $0 } return tsInputModeListKey.keys.map { $0 }
} }
@discardableResult public static func registerInputMethod() -> Bool { @discardableResult static func registerInputMethod() -> Bool {
let instances = TISInputSource.allRegisteredInstancesOfThisInputMethod let instances = TISInputSource.allRegisteredInstancesOfThisInputMethod
if instances.isEmpty { if instances.isEmpty {
// //
@ -46,15 +46,15 @@ extension TISInputSource {
return succeeded return succeeded
} }
@discardableResult public static func registerInputSource() -> Bool { @discardableResult static func registerInputSource() -> Bool {
TISRegisterInputSource(Bundle.main.bundleURL as CFURL) == noErr TISRegisterInputSource(Bundle.main.bundleURL as CFURL) == noErr
} }
@discardableResult public func activate() -> Bool { @discardableResult func activate() -> Bool {
TISEnableInputSource(self) == noErr TISEnableInputSource(self) == noErr
} }
@discardableResult public func select() -> Bool { @discardableResult func select() -> Bool {
if !isSelectable { if !isSelectable {
NSLog("Non-selectable: \(identifier)") NSLog("Non-selectable: \(identifier)")
return false return false
@ -66,29 +66,29 @@ extension TISInputSource {
return true return true
} }
@discardableResult public func deactivate() -> Bool { @discardableResult func deactivate() -> Bool {
TISDisableInputSource(self) == noErr TISDisableInputSource(self) == noErr
} }
public var isActivated: Bool { var isActivated: Bool {
unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyInputSourceIsEnabled), to: CFBoolean.self) unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyInputSourceIsEnabled), to: CFBoolean.self)
== kCFBooleanTrue == kCFBooleanTrue
} }
public var isSelectable: Bool { var isSelectable: Bool {
unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyInputSourceIsSelectCapable), to: CFBoolean.self) unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyInputSourceIsSelectCapable), to: CFBoolean.self)
== kCFBooleanTrue == kCFBooleanTrue
} }
public static func generate(from identifier: String) -> TISInputSource? { static func generate(from identifier: String) -> TISInputSource? {
TISInputSource.rawTISInputSources(onlyASCII: false)[identifier] TISInputSource.rawTISInputSources(onlyASCII: false)[identifier]
} }
public var inputModeID: String { var inputModeID: String {
unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyInputModeID), to: NSString.self) as String? ?? "" unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyInputModeID), to: NSString.self) as String? ?? ""
} }
public var vChewingLocalizedName: String { var vChewingLocalizedName: String {
switch identifier { switch identifier {
case "com.apple.keylayout.ZhuyinBopomofo": case "com.apple.keylayout.ZhuyinBopomofo":
return NSLocalizedString("Apple Zhuyin Bopomofo (Dachen)", comment: "") return NSLocalizedString("Apple Zhuyin Bopomofo (Dachen)", comment: "")
@ -104,23 +104,23 @@ extension TISInputSource {
// Ref: Original source codes are written in Swift 4 from Mzp's InputMethodKit textbook. // Ref: Original source codes are written in Swift 4 from Mzp's InputMethodKit textbook.
// Note: Slightly modified by vChewing Project: Using Dictionaries when necessary. // Note: Slightly modified by vChewing Project: Using Dictionaries when necessary.
extension TISInputSource { public extension TISInputSource {
public var localizedName: String { var localizedName: String {
unsafeBitCast(TISGetInputSourceProperty(self, kTISPropertyLocalizedName), to: NSString.self) as 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? ?? "" 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. // 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 // Using Mzp's latest solution in his blog: https://mzp.hatenablog.com/entry/2018/07/16/212026
let r = TISGetInputSourceProperty(self, "TSMInputSourcePropertyScriptCode" as CFString) let r = TISGetInputSourceProperty(self, "TSMInputSourcePropertyScriptCode" as CFString)
return unsafeBitCast(r, to: NSString.self).integerValue as Int? ?? 0 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 // CFDictionary
// //
let conditions = CFDictionaryCreateMutable(nil, 2, nil, nil) let conditions = CFDictionaryCreateMutable(nil, 2, nil, nil)

View File

@ -4,13 +4,13 @@ import PackageDescription
let package = Package( let package = Package(
name: "LangModelAssembly", name: "LangModelAssembly",
platforms: [ platforms: [
.macOS(.v10_11) .macOS(.v10_11),
], ],
products: [ products: [
.library( .library(
name: "LangModelAssembly", name: "LangModelAssembly",
targets: ["LangModelAssembly"] targets: ["LangModelAssembly"]
) ),
], ],
dependencies: [ dependencies: [
.package(path: "../RMJay_LineReader"), .package(path: "../RMJay_LineReader"),

View File

@ -10,8 +10,8 @@ import Foundation
import LineReader import LineReader
import Shared import Shared
extension vChewingLM { public extension vChewingLM {
public enum LMConsolidator { enum LMConsolidator {
public static let kPragmaHeader = "# 𝙵𝙾𝚁𝙼𝙰𝚃 𝚘𝚛𝚐.𝚊𝚝𝚎𝚕𝚒𝚎𝚛𝙸𝚗𝚖𝚞.𝚟𝚌𝚑𝚎𝚠𝚒𝚗𝚐.𝚞𝚜𝚎𝚛𝙻𝚊𝚗𝚐𝚞𝚊𝚐𝚎𝙼𝚘𝚍𝚎𝚕𝙳𝚊𝚝𝚊.𝚏𝚘𝚛𝚖𝚊𝚝𝚝𝚎𝚍" public static let kPragmaHeader = "# 𝙵𝙾𝚁𝙼𝙰𝚃 𝚘𝚛𝚐.𝚊𝚝𝚎𝚕𝚒𝚎𝚛𝙸𝚗𝚖𝚞.𝚟𝚌𝚑𝚎𝚠𝚒𝚗𝚐.𝚞𝚜𝚎𝚛𝙻𝚊𝚗𝚐𝚞𝚊𝚐𝚎𝙼𝚘𝚍𝚎𝚕𝙳𝚊𝚝𝚊.𝚏𝚘𝚛𝚖𝚊𝚝𝚝𝚎𝚍"
/// ///
@ -85,7 +85,7 @@ extension vChewingLM {
var pragmaResult: Bool { var pragmaResult: Bool {
let realPragmaHeader = kPragmaHeader + "\n" let realPragmaHeader = kPragmaHeader + "\n"
if strProcessed.count <= kPragmaHeader.count { return false } if strProcessed.count <= kPragmaHeader.count { return false }
let range = 0..<(realPragmaHeader.count) let range = 0 ..< (realPragmaHeader.count)
let fetchedPragma = ContiguousArray(strProcessed.utf8CString[range]) let fetchedPragma = ContiguousArray(strProcessed.utf8CString[range])
return fetchedPragma == realPragmaHeader.utf8CString return fetchedPragma == realPragmaHeader.utf8CString
} }

View File

@ -10,7 +10,7 @@ import Foundation
import Megrez import Megrez
import Shared import Shared
extension vChewingLM { public extension vChewingLM {
/// LMInstantiatorLMI /// LMInstantiatorLMI
/// LangModelProtocol 使 /// LangModelProtocol 使
/// ///
@ -28,7 +28,7 @@ extension vChewingLM {
/// ///
/// LMI LMI /// LMI LMI
/// ///
public class LMInstantiator: LangModelProtocol { class LMInstantiator: LangModelProtocol {
// //
public var isCassetteEnabled = false public var isCassetteEnabled = false
public var isPhraseReplacementEnabled = false public var isPhraseReplacementEnabled = false
@ -348,7 +348,7 @@ extension vChewingLM {
// //
if isPhraseReplacementEnabled { if isPhraseReplacementEnabled {
for i in 0..<rawAllUnigrams.count { for i in 0 ..< rawAllUnigrams.count {
let newValue = lmReplacements.valuesFor(key: rawAllUnigrams[i].value) let newValue = lmReplacements.valuesFor(key: rawAllUnigrams[i].value)
guard !newValue.isEmpty else { continue } guard !newValue.isEmpty else { continue }
rawAllUnigrams[i].value = newValue rawAllUnigrams[i].value = newValue

View File

@ -10,37 +10,37 @@ import Foundation
import Megrez import Megrez
import Shared import Shared
extension vChewingLM.LMInstantiator { public extension vChewingLM.LMInstantiator {
/// ///
public var cassetteWildcardKey: String { Self.lmCassette.wildcardKey } var cassetteWildcardKey: String { Self.lmCassette.wildcardKey }
/// ///
public var maxCassetteKeyLength: Int { Self.lmCassette.maxKeyLength } var maxCassetteKeyLength: Int { Self.lmCassette.maxKeyLength }
/// ///
/// - Parameter char: /// - Parameter char:
/// - Returns: /// - Returns:
public func convertCassetteKeyToDisplay(char: String) -> String { func convertCassetteKeyToDisplay(char: String) -> String {
Self.lmCassette.convertKeyToDisplay(char: char) Self.lmCassette.convertKeyToDisplay(char: char)
} }
/// ///
/// - Parameter key: /// - Parameter key:
/// - Returns: /// - Returns:
public func isThisCassetteKeyAllowed(key: String) -> Bool { func isThisCassetteKeyAllowed(key: String) -> Bool {
Self.lmCassette.allowedKeys.contains(key) Self.lmCassette.allowedKeys.contains(key)
} }
/// ///
/// - Parameter key: /// - Parameter key:
/// - Returns: /// - Returns:
public func hasCassetteWildcardResultsFor(key: String) -> Bool { func hasCassetteWildcardResultsFor(key: String) -> Bool {
Self.lmCassette.hasUnigramsFor(key: key + Self.lmCassette.wildcard) Self.lmCassette.hasUnigramsFor(key: key + Self.lmCassette.wildcard)
} }
/// ///
/// - Parameter value: /// - Parameter value:
/// - Returns: /// - Returns:
public func cassetteReverseLookup(for value: String) -> [String] { func cassetteReverseLookup(for value: String) -> [String] {
var lookupResult = Self.lmCassette.reverseLookupMap[value] ?? [] var lookupResult = Self.lmCassette.reverseLookupMap[value] ?? []
guard !lookupResult.isEmpty else { return [] } guard !lookupResult.isEmpty else { return [] }
lookupResult = lookupResult.map { $0.trimmingCharacters(in: .newlines) } lookupResult = lookupResult.map { $0.trimmingCharacters(in: .newlines) }

View File

@ -83,10 +83,10 @@ private let tableMappingArabicNumeralsToChinese: [String: String] = [
"0": "", "1": "", "2": "", "3": "", "4": "", "5": "", "6": "", "7": "", "8": "", "9": "", "0": "", "1": "", "2": "", "3": "", "4": "", "5": "", "6": "", "7": "", "8": "", "9": "",
] ]
extension String { private extension String {
/// ///
/// - Parameter target: /// - Parameter target:
fileprivate var convertArabicNumeralsToChinese: String { var convertArabicNumeralsToChinese: String {
var target = self var target = self
for key in tableMappingArabicNumeralsToChinese.keys { for key in tableMappingArabicNumeralsToChinese.keys {
guard let result = tableMappingArabicNumeralsToChinese[key] else { continue } guard let result = tableMappingArabicNumeralsToChinese[key] else { continue }

View File

@ -24,7 +24,7 @@ extension String {
) { ) {
var startIndex = startIndex var startIndex = startIndex
split(separator: separator).forEach { substring in split(separator: separator).forEach { substring in
let theRange = range(of: substring, range: startIndex..<endIndex) let theRange = range(of: substring, range: startIndex ..< endIndex)
guard let theRange = theRange else { return } guard let theRange = theRange else { return }
task(theRange) task(theRange)
startIndex = theRange.upperBound startIndex = theRange.upperBound
@ -36,11 +36,11 @@ extension String {
// This is only for reference and is not used in this assembly. // This is only for reference and is not used in this assembly.
extension String { private extension String {
fileprivate func ranges(splitBy separator: Element) -> [Range<String.Index>] { func ranges(splitBy separator: Element) -> [Range<String.Index>] {
var startIndex = startIndex var startIndex = startIndex
return split(separator: separator).reduce(into: []) { ranges, substring in return split(separator: separator).reduce(into: []) { ranges, substring in
_ = range(of: substring, range: startIndex..<endIndex).map { range in _ = range(of: substring, range: startIndex ..< endIndex).map { range in
ranges.append(range) ranges.append(range)
startIndex = range.upperBound startIndex = range.upperBound
} }

View File

@ -10,8 +10,8 @@ import Megrez
import PinyinPhonaConverter import PinyinPhonaConverter
import Shared import Shared
extension vChewingLM { public extension vChewingLM {
@frozen public struct LMAssociates { @frozen struct LMAssociates {
public private(set) var filePath: String? public private(set) var filePath: String?
var rangeMap: [String: [(Range<String.Index>, Int)]] = [:] var rangeMap: [String: [(Range<String.Index>, Int)]] = [:]
var strData: String = "" var strData: String = ""

View File

@ -12,9 +12,9 @@ import LineReader
import Megrez import Megrez
import Shared import Shared
extension vChewingLM { public extension vChewingLM {
/// 便使 /// 便使
@frozen public struct LMCassette { @frozen struct LMCassette {
public private(set) var filePath: String? public private(set) var filePath: String?
public private(set) var nameShort: String = "" public private(set) var nameShort: String = ""
public private(set) var nameENG: String = "" public private(set) var nameENG: String = ""

View File

@ -10,12 +10,12 @@ import Megrez
import PinyinPhonaConverter import PinyinPhonaConverter
import Shared import Shared
extension vChewingLM { public extension vChewingLM {
/// LMCore LMCoreEX range /// LMCore LMCoreEX range
/// range strData /// range strData
/// C++ ParselessLM Swift /// C++ ParselessLM Swift
/// For /// For
@frozen public struct LMCoreEX { @frozen struct LMCoreEX {
public private(set) var filePath: String? public private(set) var filePath: String?
/// 便 strData /// 便 strData
var rangeMap: [String: [Range<String.Index>]] = [:] var rangeMap: [String: [Range<String.Index>]] = [:]

View File

@ -10,11 +10,11 @@ import Foundation
import Megrez import Megrez
import Shared import Shared
extension vChewingLM { public extension vChewingLM {
/// LMCore LMCoreNS plist /// LMCore LMCoreNS plist
/// mac /// mac
/// 使 plist /// 使 plist
@frozen public struct LMCoreNS { @frozen struct LMCoreNS {
public private(set) var filePath: String? public private(set) var filePath: String?
/// UTF8 /// UTF8
var dataMap: [String: [Data]] = [:] var dataMap: [String: [Data]] = [:]

View File

@ -9,8 +9,8 @@
import Foundation import Foundation
import Shared import Shared
extension vChewingLM { public extension vChewingLM {
@frozen public struct LMPlainBopomofo { @frozen struct LMPlainBopomofo {
public private(set) var filePath: String? public private(set) var filePath: String?
var dataMap: [String: String] = [:] var dataMap: [String: String] = [:]

View File

@ -8,8 +8,8 @@
import Shared import Shared
extension vChewingLM { public extension vChewingLM {
@frozen public struct LMReplacements { @frozen struct LMReplacements {
public private(set) var filePath: String? public private(set) var filePath: String?
var rangeMap: [String: Range<String.Index>] = [:] var rangeMap: [String: Range<String.Index>] = [:]
var strData: String = "" var strData: String = ""

View File

@ -9,8 +9,8 @@
import Foundation import Foundation
import Shared import Shared
extension vChewingLM { public extension vChewingLM {
@frozen public struct LMRevLookup { @frozen struct LMRevLookup {
public private(set) var dataMap: [String: [Data]] = [:] public private(set) var dataMap: [String: [Data]] = [:]
public private(set) var filePath: String = "" public private(set) var filePath: String = ""

View File

@ -11,8 +11,8 @@ import Foundation
import Megrez import Megrez
import Shared import Shared
extension vChewingLM { public extension vChewingLM {
public class LMUserOverride { class LMUserOverride {
// MARK: - Main // MARK: - Main
var mutCapacity: Int var mutCapacity: Int
@ -153,8 +153,8 @@ extension vChewingLM.LMUserOverride {
// MARK: - Hash and Dehash the entire UOM data, etc. // MARK: - Hash and Dehash the entire UOM data, etc.
extension vChewingLM.LMUserOverride { public extension vChewingLM.LMUserOverride {
public func bleachSpecifiedSuggestions(targets: [String], saveCallback: @escaping () -> Void) { func bleachSpecifiedSuggestions(targets: [String], saveCallback: @escaping () -> Void) {
if targets.isEmpty { return } if targets.isEmpty { return }
for neta in mutLRUMap { for neta in mutLRUMap {
for target in targets { for target in targets {
@ -168,7 +168,7 @@ extension vChewingLM.LMUserOverride {
} }
/// LRU /// LRU
public func bleachUnigrams(saveCallback: @escaping () -> Void) { func bleachUnigrams(saveCallback: @escaping () -> Void) {
for key in mutLRUMap.keys { for key in mutLRUMap.keys {
if !key.contains("(),()") { continue } if !key.contains("(),()") { continue }
mutLRUMap.removeValue(forKey: key) mutLRUMap.removeValue(forKey: key)
@ -184,7 +184,7 @@ extension vChewingLM.LMUserOverride {
} }
} }
public func clearData(withURL fileURL: URL) { func clearData(withURL fileURL: URL) {
mutLRUMap = .init() mutLRUMap = .init()
mutLRUList = .init() mutLRUList = .init()
do { do {
@ -196,7 +196,7 @@ extension vChewingLM.LMUserOverride {
} }
} }
public func saveData(toURL fileURL: URL? = nil) { func saveData(toURL fileURL: URL? = nil) {
let encoder = JSONEncoder() let encoder = JSONEncoder()
do { do {
guard let jsonData = try? encoder.encode(mutLRUMap) else { return } 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() let decoder = JSONDecoder()
do { do {
let data = try Data(contentsOf: fileURL, options: .mappedIfSafe) 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 candidates = [(String, Megrez.Unigram)]()
public var forceHighScoreOverride = false public var forceHighScoreOverride = false
public var isEmpty: Bool { candidates.isEmpty } public var isEmpty: Bool { candidates.isEmpty }
@ -282,7 +282,7 @@ extension vChewingLM.LMUserOverride {
eventCount: theObservation.count, totalCount: observation.count, eventCount: theObservation.count, totalCount: observation.count,
eventTimestamp: theObservation.timestamp, timestamp: timestamp, lambda: decayExp 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))) candidates.append((headReading, .init(value: i, score: overrideScore)))
forceHighScoreOverride = theObservation.forceHighScoreOverride forceHighScoreOverride = theObservation.forceHighScoreOverride

View File

@ -4,13 +4,13 @@ import PackageDescription
let package = Package( let package = Package(
name: "Megrez", name: "Megrez",
platforms: [ platforms: [
.macOS(.v10_11) .macOS(.v10_11),
], ],
products: [ products: [
.library( .library(
name: "Megrez", name: "Megrez",
targets: ["Megrez"] targets: ["Megrez"]
) ),
], ],
dependencies: [], dependencies: [],
targets: [ targets: [

View File

@ -5,7 +5,7 @@
import Foundation import Foundation
extension Megrez { public extension Megrez {
/// ///
/// ///
/// ///
@ -15,7 +15,7 @@ extension Megrez {
/// ///
/// ///
/// ///
public struct Compositor { struct Compositor {
/// ///
public enum TypingDirection { case front, rear } public enum TypingDirection { case front, rear }
/// ///
@ -137,16 +137,16 @@ extension Megrez {
guard let currentRegion = walkedNodes.cursorRegionMap[target] else { return false } guard let currentRegion = walkedNodes.cursorRegionMap[target] else { return false }
let aRegionForward = max(currentRegion - 1, 0) let aRegionForward = max(currentRegion - 1, 0)
let currentRegionBorderRear: Int = walkedNodes[0..<currentRegion].map(\.spanLength).reduce(0, +) let currentRegionBorderRear: Int = walkedNodes[0 ..< currentRegion].map(\.spanLength).reduce(0, +)
switch target { switch target {
case currentRegionBorderRear: case currentRegionBorderRear:
switch direction { switch direction {
case .front: case .front:
target = target =
(currentRegion > walkedNodes.count) (currentRegion > walkedNodes.count)
? keys.count : walkedNodes[0...currentRegion].map(\.spanLength).reduce(0, +) ? keys.count : walkedNodes[0 ... currentRegion].map(\.spanLength).reduce(0, +)
case .rear: case .rear:
target = walkedNodes[0..<aRegionForward].map(\.spanLength).reduce(0, +) target = walkedNodes[0 ..< aRegionForward].map(\.spanLength).reduce(0, +)
} }
default: default:
switch direction { switch direction {
@ -168,7 +168,7 @@ extension Megrez {
// C# StringBuilder Swift NSMutableString // C# StringBuilder Swift NSMutableString
let strOutput: NSMutableString = .init(string: "digraph {\ngraph [ rankdir=LR ];\nBOS;\n") let strOutput: NSMutableString = .init(string: "digraph {\ngraph [ rankdir=LR ];\nBOS;\n")
for (p, span) in spans.enumerated() { 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 } guard let np = span.nodeOf(length: ni) else { continue }
if p == 0 { if p == 0 {
strOutput.append("BOS -> \(np.value);\n") strOutput.append("BOS -> \(np.value);\n")
@ -176,7 +176,7 @@ extension Megrez {
strOutput.append("\(np.value);\n") strOutput.append("\(np.value);\n")
if (p + ni) < spans.count { if (p + ni) < spans.count {
let destinationSpan = spans[p + ni] 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 } guard let dn = destinationSpan.nodeOf(length: q) else { continue }
strOutput.append(np.value + " -> " + dn.value + ";\n") strOutput.append(np.value + " -> " + dn.value + ";\n")
} }
@ -248,7 +248,7 @@ extension Megrez.Compositor {
let affectedLength = Megrez.Compositor.maxSpanLength - 1 let affectedLength = Megrez.Compositor.maxSpanLength - 1
let begin = max(0, location - affectedLength) let begin = max(0, location - affectedLength)
guard location >= begin else { return } guard location >= begin else { return }
for i in begin..<location { for i in begin ..< location {
spans[i].dropNodesOfOrBeyond(length: location - i + 1) spans[i].dropNodesOfOrBeyond(length: location - i + 1)
} }
} }
@ -280,11 +280,11 @@ extension Megrez.Compositor {
/// - Returns: 0 /// - Returns: 0
@discardableResult public mutating func update(updateExisting: Bool = false) -> Int { @discardableResult public mutating func update(updateExisting: Bool = false) -> Int {
let maxSpanLength = Megrez.Compositor.maxSpanLength let maxSpanLength = Megrez.Compositor.maxSpanLength
let range = max(0, cursor - maxSpanLength)..<min(cursor + maxSpanLength, keys.count) let range = max(0, cursor - maxSpanLength) ..< min(cursor + maxSpanLength, keys.count)
var nodesChanged = 0 var nodesChanged = 0
for position in range { for position in range {
for theLength in 1...min(maxSpanLength, range.upperBound - position) { for theLength in 1 ... min(maxSpanLength, range.upperBound - position) {
let joinedKeyArray = getJoinedKeyArray(range: position..<(position + theLength)) let joinedKeyArray = getJoinedKeyArray(range: position ..< (position + theLength))
if let theNode = getNode(at: position, length: theLength, keyArray: joinedKeyArray) { if let theNode = getNode(at: position, length: theLength, keyArray: joinedKeyArray) {
if !updateExisting { continue } if !updateExisting { continue }
let unigrams = langModel.unigramsFor(keyArray: joinedKeyArray) let unigrams = langModel.unigramsFor(keyArray: joinedKeyArray)

View File

@ -3,7 +3,7 @@
// ==================== // ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT) // This code is released under the MIT license (SPDX-License-Identifier: MIT)
extension Megrez.Compositor { public extension Megrez.Compositor {
/// walkedNodes /// walkedNodes
/// ///
/// ///
@ -13,7 +13,7 @@ extension Megrez.Compositor {
/// `G = (V, E)` `O(|V|+|E|)` `G` /// `G = (V, E)` `O(|V|+|E|)` `G`
/// 使 /// 使
/// - Returns: /// - Returns:
@discardableResult public mutating func walk() -> (walkedNode: [Node], succeeded: Bool) { @discardableResult mutating func walk() -> (walkedNode: [Node], succeeded: Bool) {
var result = [Node]() var result = [Node]()
defer { walkedNodes = result } defer { walkedNodes = result }
guard !spans.isEmpty else { return (result, true) } guard !spans.isEmpty else { return (result, true) }
@ -24,7 +24,7 @@ extension Megrez.Compositor {
} }
for (i, span) in spans.enumerated() { 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) { if let theNode = span.nodeOf(length: j) {
vertexSpans[i].append(.init(node: theNode)) vertexSpans[i].append(.init(node: theNode))
} }

View File

@ -5,9 +5,9 @@
import Foundation 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] public var keyArray: [String]
/// ///
@ -77,14 +77,14 @@ extension Megrez.Compositor {
/// - all: 穿 /// - all: 穿
/// - beginAt: /// - beginAt:
/// - endAt /// - endAt
public enum CandidateFetchFilter { case all, beginAt, endAt } enum CandidateFetchFilter { case all, beginAt, endAt }
/// ///
/// ///
/// location - 1 /// location - 1
/// - Parameter location: /// - Parameter location:
/// - Returns: /// - Returns:
public func fetchCandidates(at location: Int, filter: CandidateFetchFilter = .all) -> [KeyValuePaired] { func fetchCandidates(at location: Int, filter: CandidateFetchFilter = .all) -> [KeyValuePaired] {
var result = [KeyValuePaired]() var result = [KeyValuePaired]()
guard !keys.isEmpty else { return result } guard !keys.isEmpty else { return result }
let location = max(min(location, keys.count - 1), 0) // let location = max(min(location, keys.count - 1), 0) //
@ -119,7 +119,7 @@ extension Megrez.Compositor {
/// - location: /// - location:
/// - overrideType: /// - overrideType:
/// - Returns: /// - Returns:
@discardableResult public func overrideCandidate( @discardableResult func overrideCandidate(
_ candidate: KeyValuePaired, at location: Int, overrideType: Node.OverrideType = .withHighScore _ candidate: KeyValuePaired, at location: Int, overrideType: Node.OverrideType = .withHighScore
) )
-> Bool -> Bool
@ -135,7 +135,7 @@ extension Megrez.Compositor {
/// - location: /// - location:
/// - overrideType: /// - overrideType:
/// - Returns: /// - Returns:
@discardableResult public func overrideCandidateLiteral( @discardableResult func overrideCandidateLiteral(
_ candidate: String, _ candidate: String,
at location: Int, overrideType: Node.OverrideType = .withHighScore at location: Int, overrideType: Node.OverrideType = .withHighScore
) -> Bool { ) -> Bool {
@ -166,7 +166,7 @@ extension Megrez.Compositor {
guard let overridden = overridden else { return false } // guard let overridden = overridden else { return false } //
for i in overridden.spanIndex..<min(spans.count, overridden.spanIndex + overridden.node.spanLength) { for i in overridden.spanIndex ..< min(spans.count, overridden.spanIndex + overridden.node.spanLength) {
/// A BC /// A BC
/// A BC 使 A /// A BC 使 A
/// DEF BC A /// DEF BC A
@ -190,12 +190,12 @@ extension Megrez.Compositor {
// Reference: https://stackoverflow.com/a/50545761/4162914 // Reference: https://stackoverflow.com/a/50545761/4162914
extension Sequence { private extension Sequence {
/// Return a stable-sorted collection. /// Return a stable-sorted collection.
/// ///
/// - Parameter areInIncreasingOrder: Return nil when two element are equal. /// - Parameter areInIncreasingOrder: Return nil when two element are equal.
/// - Returns: The sorted collection. /// - Returns: The sorted collection.
fileprivate func stableSorted( func stableSorted(
by areInIncreasingOrder: (Element, Element) throws -> Bool by areInIncreasingOrder: (Element, Element) throws -> Bool
) )
rethrows -> [Element] rethrows -> [Element]

View File

@ -15,7 +15,7 @@ extension Megrez.Compositor {
/// Megrez.Compositor.maxSpanLength /// Megrez.Compositor.maxSpanLength
private var maxSpanLength: Int { Megrez.Compositor.maxSpanLength } private var maxSpanLength: Int { Megrez.Compositor.maxSpanLength }
/// ///
private var allowedLengths: ClosedRange<Int> { 1...maxSpanLength } private var allowedLengths: ClosedRange<Int> { 1 ... maxSpanLength }
/// ///
public init() { public init() {
@ -25,7 +25,7 @@ extension Megrez.Compositor {
/// 0 /// 0
public func clear() { public func clear() {
nodes.removeAll() nodes.removeAll()
for _ in 0..<maxSpanLength { for _ in 0 ..< maxSpanLength {
nodes.append(nil) nodes.append(nil)
} }
maxLength = 0 maxLength = 0
@ -50,13 +50,13 @@ extension Megrez.Compositor {
guard allowedLengths.contains(length) else { guard allowedLengths.contains(length) else {
return false return false
} }
for i in length...maxSpanLength { for i in length ... maxSpanLength {
nodes[i - 1] = nil nodes[i - 1] = nil
} }
maxLength = 0 maxLength = 0
guard length > 1 else { return false } guard length > 1 else { return false }
let maxR = length - 2 let maxR = length - 2
for i in 0...maxR { for i in 0 ... maxR {
if nodes[maxR - i] == nil { continue } if nodes[maxR - i] == nil { continue }
maxLength = maxR - i + 1 maxLength = maxR - i + 1
break break
@ -83,17 +83,17 @@ extension Megrez.Compositor {
guard !spans.isEmpty, location < spans.count else { return results } 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 } guard let node = spans[location].nodeOf(length: theLocation) else { continue }
results.append(.init(node: node, spanIndex: location)) results.append(.init(node: node, spanIndex: location))
} }
// //
let begin: Int = location - min(location, Megrez.Compositor.maxSpanLength - 1) let begin: Int = location - min(location, Megrez.Compositor.maxSpanLength - 1)
for theLocation in begin..<location { for theLocation in begin ..< location {
let (A, B): (Int, Int) = (location - theLocation + 1, spans[theLocation].maxLength) let (A, B): (Int, Int) = (location - theLocation + 1, spans[theLocation].maxLength)
guard A <= B else { continue } guard A <= B else { continue }
for theLength in A...B { for theLength in A ... B {
guard let node = spans[theLocation].nodeOf(length: theLength) else { continue } guard let node = spans[theLocation].nodeOf(length: theLength) else { continue }
results.append(.init(node: node, spanIndex: theLocation)) results.append(.init(node: node, spanIndex: theLocation))
} }

View File

@ -5,7 +5,7 @@
import Foundation import Foundation
extension Megrez.Compositor { public extension Megrez.Compositor {
/// ///
/// ///
/// ///
@ -13,7 +13,7 @@ extension Megrez.Compositor {
/// ///
/// ///
/// 2 /// 2
public class Node: Equatable, Hashable { class Node: Equatable, Hashable {
/// ///
/// - withNoOverrides: /// - withNoOverrides:
/// - withTopUnigramScore: 使使 /// - withTopUnigramScore: 使使
@ -158,9 +158,9 @@ extension Megrez.Compositor {
} }
} }
extension Megrez.Compositor { public extension Megrez.Compositor {
/// Gramambular 2 NodeInSpan /// Gramambular 2 NodeInSpan
public struct NodeAnchor: Hashable { struct NodeAnchor: Hashable {
/// ///
let node: Megrez.Compositor.Node let node: Megrez.Compositor.Node
/// ///
@ -185,17 +185,17 @@ extension Megrez.Compositor {
// MARK: - Array Extensions. // MARK: - Array Extensions.
extension Array where Element == Megrez.Compositor.Node { public extension Array where Element == Megrez.Compositor.Node {
/// ///
public var values: [String] { map(\.value) } var values: [String] { map(\.value) }
/// ///
public func joinedKeys(by separator: String = Megrez.Compositor.theSeparator) -> [String] { func joinedKeys(by separator: String = Megrez.Compositor.theSeparator) -> [String] {
map { $0.keyArray.lazy.joined(separator: separator) } 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)
/// Result A Result B /// Result A Result B
@ -217,25 +217,25 @@ extension Array where Element == Megrez.Compositor.Node {
} }
/// 0 /// 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: /// - Parameter cursor:
public func contextRange(ofGivenCursor cursor: Int) -> Range<Int> { func contextRange(ofGivenCursor cursor: Int) -> Range<Int> {
guard !isEmpty else { return 0..<0 } guard !isEmpty else { return 0 ..< 0 }
let lastSpanningLength = reversed()[0].keyArray.count let lastSpanningLength = reversed()[0].keyArray.count
var nilReturn = (totalKeyCount - lastSpanningLength)..<totalKeyCount var nilReturn = (totalKeyCount - lastSpanningLength) ..< totalKeyCount
if cursor >= totalKeyCount { return nilReturn } // if cursor >= totalKeyCount { return nilReturn } //
let cursor = Swift.max(0, cursor) // let cursor = Swift.max(0, cursor) //
nilReturn = cursor..<cursor nilReturn = cursor ..< cursor
// nilReturn // nilReturn
guard let rearNodeID = nodeBorderPointDictPair.cursorRegionMap[cursor] else { return nilReturn } guard let rearNodeID = nodeBorderPointDictPair.cursorRegionMap[cursor] else { return nilReturn }
guard let rearIndex = nodeBorderPointDictPair.regionCursorMap[rearNodeID] else { return nilReturn } guard let rearIndex = nodeBorderPointDictPair.regionCursorMap[rearNodeID] else { return nilReturn }
guard let frontIndex = nodeBorderPointDictPair.regionCursorMap[rearNodeID + 1] else { return nilReturn } guard let frontIndex = nodeBorderPointDictPair.regionCursorMap[rearNodeID + 1] else { return nilReturn }
return rearIndex..<frontIndex return rearIndex ..< frontIndex
} }
/// ///
@ -243,7 +243,7 @@ extension Array where Element == Megrez.Compositor.Node {
/// - cursor: /// - cursor:
/// - outCursorPastNode: /// - outCursorPastNode:
/// - Returns: /// - Returns:
public func findNode(at cursor: Int, target outCursorPastNode: inout Int) -> Megrez.Compositor.Node? { func findNode(at cursor: Int, target outCursorPastNode: inout Int) -> Megrez.Compositor.Node? {
guard !isEmpty else { return nil } 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) let range = contextRange(ofGivenCursor: cursor)
@ -255,13 +255,13 @@ extension Array where Element == Megrez.Compositor.Node {
/// ///
/// - Parameter cursor: /// - Parameter cursor:
/// - Returns: /// - Returns:
public func findNode(at cursor: Int) -> Megrez.Compositor.Node? { func findNode(at cursor: Int) -> Megrez.Compositor.Node? {
var useless = 0 var useless = 0
return findNode(at: cursor, target: &useless) return findNode(at: cursor, target: &useless)
} }
/// 使 Megrez KeyValuePaired /// 使 Megrez KeyValuePaired
public var smashedPairs: [(key: String, value: String)] { var smashedPairs: [(key: String, value: String)] {
var arrData = [(key: String, value: String)]() var arrData = [(key: String, value: String)]()
let separator = Megrez.Compositor.theSeparator let separator = Megrez.Compositor.theSeparator
forEach { node in forEach { node in

View File

@ -11,9 +11,9 @@ public protocol LangModelProtocol {
func hasUnigramsFor(keyArray: [String]) -> Bool func hasUnigramsFor(keyArray: [String]) -> Bool
} }
extension Megrez.Compositor { public extension Megrez.Compositor {
/// ///
public class LangModelRanked: LangModelProtocol { class LangModelRanked: LangModelProtocol {
private let langModel: LangModelProtocol private let langModel: LangModelProtocol
/// ///
/// - Parameter withLM: /// - Parameter withLM:
@ -41,12 +41,12 @@ extension Megrez.Compositor {
// Reference: https://stackoverflow.com/a/50545761/4162914 // Reference: https://stackoverflow.com/a/50545761/4162914
extension Sequence { private extension Sequence {
/// Return a stable-sorted collection. /// Return a stable-sorted collection.
/// ///
/// - Parameter areInIncreasingOrder: Return nil when two element are equal. /// - Parameter areInIncreasingOrder: Return nil when two element are equal.
/// - Returns: The sorted collection. /// - Returns: The sorted collection.
fileprivate func stableSorted( func stableSorted(
by areInIncreasingOrder: (Element, Element) throws -> Bool by areInIncreasingOrder: (Element, Element) throws -> Bool
) )
rethrows -> [Element] rethrows -> [Element]

View File

@ -3,9 +3,9 @@
// ==================== // ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT) // 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 public var value: String
/// ///
@ -43,9 +43,9 @@ extension Megrez {
// MARK: - Array Extensions. // MARK: - Array Extensions.
extension Array where Element == Megrez.Unigram { public extension Array where Element == Megrez.Unigram {
/// ///
public mutating func consolidate(filter theFilter: Set<String> = .init()) { mutating func consolidate(filter theFilter: Set<String> = .init()) {
var inserted: [String: Double] = [:] var inserted: [String: Double] = [:]
var insertedArray: [Megrez.Unigram] = [] var insertedArray: [Megrez.Unigram] = []
for neta in filter({ !theFilter.contains($0.value) }) { for neta in filter({ !theFilter.contains($0.value) }) {

View File

@ -58,121 +58,121 @@ class MockLM: LangModelProtocol {
// MARK: - // MARK: -
public let strStressData = #""" public let strStressData = #"""
yi1 -2.08170692 yi1 -2.08170692
yi1-yi1 -4.38468400 yi1-yi1 -4.38468400
"""# """#
public let strEmojiSampleData = #""" public let strEmojiSampleData = #"""
gao1 -2.9396 gao1 -2.9396
re4 -3.6024 re4 -3.6024
gao1re4 -6.526 gao1re4 -6.526
huo3 -3.6966 huo3 -3.6966
huo3 🔥 -8 huo3 🔥 -8
yan4 -5.4466 yan4 -5.4466
huo3yan4 -5.6231 huo3yan4 -5.6231
huo3yan4 🔥 -8 huo3yan4 🔥 -8
wei2 -3.9832 wei2 -3.9832
xian3 -3.7810 xian3 -3.7810
wei2xian3 -4.2623 wei2xian3 -4.2623
mi4feng1 -3.6231 mi4feng1 -3.6231
mi4 -4.6231 mi4 -4.6231
feng1 -4.6231 feng1 -4.6231
feng1 🐝 -11 feng1 🐝 -11
mi4feng1 🐝 -11 mi4feng1 🐝 -11
"""# """#
public let strSampleData = #""" public let strSampleData = #"""
# #
# libTaBE (http://sourceforge.net/projects/libtabe/) # libTaBE (http://sourceforge.net/projects/libtabe/)
# (2002 ). 1999 Pai-Hsiang Hsiao BSD # (2002 ). 1999 Pai-Hsiang Hsiao BSD
# #
ni3 -6.000000 // Non-LibTaBE ni3 -6.000000 // Non-LibTaBE
zhe4 -6.000000 // Non-LibTaBE zhe4 -6.000000 // Non-LibTaBE
yang4 -6.000000 // Non-LibTaBE yang4 -6.000000 // Non-LibTaBE
si1 -9.495858 si1 -9.495858
si1 -9.006414 si1 -9.006414
si1 -99.000000 si1 -99.000000
si1 -8.091803 si1 -8.091803
si1 -99.000000 si1 -99.000000
si1 -13.513987 si1 -13.513987
si1 -12.259095 si1 -12.259095
gao1 -7.171551 gao1 -7.171551
ke1 -10.574273 ke1 -10.574273
ke1 -11.504072 ke1 -11.504072
ke1 -10.450457 ke1 -10.450457
ke1 -7.171052 ke1 -7.171052
ke1 -99.000000 ke1 -99.000000
gao1 -11.928720 gao1 -11.928720
gao1 -13.624335 gao1 -13.624335
gao1 -12.390804 gao1 -12.390804
de5 -3.516024 de5 -3.516024
di2 -3.516024 di2 -3.516024
di4 -3.516024 di4 -3.516024
zhong1 -5.809297 zhong1 -5.809297
de5 -7.427179 de5 -7.427179
gong1 -8.381971 gong1 -8.381971
gong1 -8.501463 gong1 -8.501463
ji4 -99.000000 ji4 -99.000000
jin1 -8.034095 jin1 -8.034095
gong1 -8.858181 gong1 -8.858181
ji4 -7.608341 ji4 -7.608341
ji4 -99.000000 ji4 -99.000000
jin1 -7.290109 jin1 -7.290109
ji4 -10.939895 ji4 -10.939895
zhong1 -99.000000 zhong1 -99.000000
ji4 -99.000000 ji4 -99.000000
ji4 -99.000000 ji4 -99.000000
jin1 -99.000000 jin1 -99.000000
ji4 -9.715317 ji4 -9.715317
ji4 -7.926683 ji4 -7.926683
ji4 -8.373022 ji4 -8.373022
zhong1 -9.877580 zhong1 -9.877580
jin1 -10.711079 jin1 -10.711079
gong1 -7.877973 gong1 -7.877973
gong1 -7.822167 gong1 -7.822167
gong1 -99.000000 gong1 -99.000000
gong1 -99.000000 gong1 -99.000000
gong1 -99.000000 gong1 -99.000000
zhong1 -9.685671 zhong1 -9.685671
ji4 -10.425662 ji4 -10.425662
gong1 -99.000000 gong1 -99.000000
gong1 -99.000000 gong1 -99.000000
ji4 -8.888722 ji4 -8.888722
ji4 -10.204425 ji4 -10.204425
jin1 -11.378321 jin1 -11.378321
zhong1 -99.000000 zhong1 -99.000000
ji4 -99.000000 ji4 -99.000000
ji4 -8.450826 ji4 -8.450826
jin1 -11.074890 jin1 -11.074890
gong1 -99.000000 gong1 -99.000000
ji4 -12.045357 ji4 -12.045357
zhong1 -99.000000 zhong1 -99.000000
ji4 -99.000000 ji4 -99.000000
ji4 -9.517568 ji4 -9.517568
ji4 -12.021587 ji4 -12.021587
jin1 -99.000000 jin1 -99.000000
jin1 -12.784206 jin1 -12.784206
nian2 -6.086515 nian2 -6.086515
jiang3 -9.164384 jiang3 -9.164384
jiang3 -8.690941 jiang3 -8.690941
jiang3 -10.127828 jiang3 -10.127828
nian2 -11.336864 nian2 -11.336864
nian2 -11.285740 nian2 -11.285740
jiang3 -12.492933 jiang3 -12.492933
gong1si1 -6.299461 gong1si1 -6.299461
ke1ji4 -6.736613 ke1ji4 -6.736613
ji4gong1 -13.336653 ji4gong1 -13.336653
jiang3jin1 -10.344678 jiang3jin1 -10.344678
nian2zhong1 -11.668947 nian2zhong1 -11.668947
nian2zhong1 -11.373044 nian2zhong1 -11.373044
gao1ke1ji4 -9.842421 gao1ke1ji4 -9.842421
zhe4yang4 -6.000000 // Non-LibTaBE zhe4yang4 -6.000000 // Non-LibTaBE
ni3zhe4 -9.000000 // Non-LibTaBE ni3zhe4 -9.000000 // Non-LibTaBE
jiao4 -3.676169 jiao4 -3.676169
jiao4 -3.24869962 jiao4 -3.24869962
jiao4yu4 -3.32220565 jiao4yu4 -3.32220565
yu4 -3.30192952 yu4 -3.30192952
"""# """#

View File

@ -335,7 +335,7 @@ final class MegrezTests: XCTestCase {
func test13_Compositor_StressBench() throws { func test13_Compositor_StressBench() throws {
NSLog("// Stress test preparation begins.") NSLog("// Stress test preparation begins.")
var compositor = Megrez.Compositor(with: SimpleLM(input: strStressData)) var compositor = Megrez.Compositor(with: SimpleLM(input: strStressData))
for _ in 0..<1919 { for _ in 0 ..< 1919 {
compositor.insertKey("yi") compositor.insertKey("yi")
} }
NSLog("// Stress test started.") NSLog("// Stress test started.")

View File

@ -1,6 +1,7 @@
.PHONY: format .PHONY: lint format
format: format:
swiftformat ./ --swiftversion 5.5 @swiftformat --swiftversion 5.5 --indent 2 ./
@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 lint:
@git ls-files --exclude-standard | grep -E '\.swift$$' | swiftlint --fix --autocorrect

View File

@ -4,19 +4,19 @@ import PackageDescription
let package = Package( let package = Package(
name: "NotifierUI", name: "NotifierUI",
platforms: [ platforms: [
.macOS(.v10_11) .macOS(.v10_11),
], ],
products: [ products: [
.library( .library(
name: "NotifierUI", name: "NotifierUI",
targets: ["NotifierUI"] targets: ["NotifierUI"]
) ),
], ],
dependencies: [], dependencies: [],
targets: [ targets: [
.target( .target(
name: "NotifierUI", name: "NotifierUI",
dependencies: [] dependencies: []
) ),
] ]
) )

View File

@ -178,15 +178,15 @@ extension Notifier {
} }
} }
extension NSMutableOrderedSet { private extension NSMutableOrderedSet {
fileprivate var arrayOfWindows: [NSWindow] { compactMap { ($0 as? Notifier)?.window } } 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 } } for neta in self { if let result = neta as? Notifier { return result } }
return nil return nil
} }
fileprivate var lastNotifier: Notifier? { var lastNotifier: Notifier? {
for neta in reversed { if let result = neta as? Notifier { return result } } for neta in reversed { if let result = neta as? Notifier { return result } }
return nil return nil
} }

View File

@ -4,13 +4,13 @@ import PackageDescription
let package = Package( let package = Package(
name: "PhraseEditorUI", name: "PhraseEditorUI",
platforms: [ platforms: [
.macOS(.v10_11) .macOS(.v10_11),
], ],
products: [ products: [
.library( .library(
name: "PhraseEditorUI", name: "PhraseEditorUI",
targets: ["PhraseEditorUI"] targets: ["PhraseEditorUI"]
) ),
], ],
dependencies: [ dependencies: [
.package(path: "../vChewing_LangModelAssembly"), .package(path: "../vChewing_LangModelAssembly"),
@ -25,6 +25,6 @@ let package = Package(
.product(name: "SwiftUIBackports", package: "ShapsBenkau_SwiftUIBackports"), .product(name: "SwiftUIBackports", package: "ShapsBenkau_SwiftUIBackports"),
.product(name: "Shared", package: "vChewing_Shared"), .product(name: "Shared", package: "vChewing_Shared"),
] ]
) ),
] ]
) )

Some files were not shown because too many files have changed in this diff Show More