Loads the user phrases just after the files are changes.
The revision uses a FSEventStream to monitor the change of the folder that stores user phrases.
This commit is contained in:
parent
3099798195
commit
1c339c622e
|
@ -51,6 +51,7 @@
|
|||
D461B792279DAC010070E734 /* InputState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D461B791279DAC010070E734 /* InputState.swift */; };
|
||||
D47B92C027972AD100458394 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47B92BF27972AC800458394 /* main.swift */; };
|
||||
D47D73A427A5D43900255A50 /* KeyHandlerBopomofoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47D73A327A5D43900255A50 /* KeyHandlerBopomofoTests.swift */; };
|
||||
D47D73C327A7200500255A50 /* FSEventStreamHelper in Frameworks */ = {isa = PBXBuildFile; productRef = D47D73C227A7200500255A50 /* FSEventStreamHelper */; };
|
||||
D47F7DCE278BFB57002F9DD7 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */; };
|
||||
D47F7DD0278C0897002F9DD7 /* NonModalAlertWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */; };
|
||||
D47F7DD3278C1263002F9DD7 /* UserOverrideModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DD2278C1263002F9DD7 /* UserOverrideModel.cpp */; };
|
||||
|
@ -203,6 +204,7 @@
|
|||
D461B791279DAC010070E734 /* InputState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputState.swift; sourceTree = "<group>"; };
|
||||
D47B92BF27972AC800458394 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
||||
D47D73A327A5D43900255A50 /* KeyHandlerBopomofoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandlerBopomofoTests.swift; sourceTree = "<group>"; };
|
||||
D47D73C027A71FFA00255A50 /* FSEventStreamHelper */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FSEventStreamHelper; path = Packages/FSEventStreamHelper; sourceTree = "<group>"; };
|
||||
D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
|
||||
D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonModalAlertWindowController.swift; sourceTree = "<group>"; };
|
||||
D47F7DD1278C1263002F9DD7 /* UserOverrideModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserOverrideModel.h; sourceTree = "<group>"; };
|
||||
|
@ -232,6 +234,7 @@
|
|||
D427F7C127908EFC004A2160 /* OpenCCBridge in Frameworks */,
|
||||
D44FB74A2791B829003C80A6 /* VXHanConvert in Frameworks */,
|
||||
D427F7A927905E90004A2160 /* TooltipUI in Frameworks */,
|
||||
D47D73C327A7200500255A50 /* FSEventStreamHelper in Frameworks */,
|
||||
D427F76A278C9E29004A2160 /* CandidateUI in Frameworks */,
|
||||
D427F7AE27907B8A004A2160 /* NotifierUI in Frameworks */,
|
||||
);
|
||||
|
@ -262,6 +265,7 @@
|
|||
6A0D4EC215FC0D3C00ABF4B3 /* Source */,
|
||||
D485D3B72796A8A000657FF3 /* McBopomofoTests */,
|
||||
6A0D4EA315FC0D2D00ABF4B3 /* Products */,
|
||||
D47D73C127A7200500255A50 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
|
@ -467,10 +471,18 @@
|
|||
D427F7B2279086B5004A2160 /* InputSourceHelper */,
|
||||
D427F7BF27908EAC004A2160 /* OpenCCBridge */,
|
||||
D44FB7482791B346003C80A6 /* VXHanConvert */,
|
||||
D47D73C027A71FFA00255A50 /* FSEventStreamHelper */,
|
||||
);
|
||||
name = Packages;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D47D73C127A7200500255A50 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D485D3B72796A8A000657FF3 /* McBopomofoTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -522,6 +534,7 @@
|
|||
D427F7B3279086DC004A2160 /* InputSourceHelper */,
|
||||
D427F7C027908EFC004A2160 /* OpenCCBridge */,
|
||||
D44FB7492791B829003C80A6 /* VXHanConvert */,
|
||||
D47D73C227A7200500255A50 /* FSEventStreamHelper */,
|
||||
);
|
||||
productName = McBopomofo;
|
||||
productReference = 6A0D4EA215FC0D2D00ABF4B3 /* McBopomofo.app */;
|
||||
|
@ -1387,6 +1400,10 @@
|
|||
isa = XCSwiftPackageProductDependency;
|
||||
productName = VXHanConvert;
|
||||
};
|
||||
D47D73C227A7200500255A50 /* FSEventStreamHelper */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = FSEventStreamHelper;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = 6A0D4E9415FC0CFA00ABF4B3 /* Project object */;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
.DS_Store
|
||||
/.build
|
||||
/Packages
|
||||
/*.xcodeproj
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
|
@ -0,0 +1,28 @@
|
|||
// swift-tools-version:5.3
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "FSEventStreamHelper",
|
||||
products: [
|
||||
// Products define the executables and libraries a package produces, and make them visible to other packages.
|
||||
.library(
|
||||
name: "FSEventStreamHelper",
|
||||
targets: ["FSEventStreamHelper"]),
|
||||
],
|
||||
dependencies: [
|
||||
// Dependencies declare other packages that this package depends on.
|
||||
// .package(url: /* package url */, from: "1.0.0"),
|
||||
],
|
||||
targets: [
|
||||
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||
// Targets can depend on other targets in this package, and on products in packages this package depends on.
|
||||
.target(
|
||||
name: "FSEventStreamHelper",
|
||||
dependencies: []),
|
||||
.testTarget(
|
||||
name: "FSEventStreamHelperTests",
|
||||
dependencies: ["FSEventStreamHelper"]),
|
||||
]
|
||||
)
|
|
@ -0,0 +1,3 @@
|
|||
# FSEventStreamHelper
|
||||
|
||||
A description of this package.
|
|
@ -0,0 +1,73 @@
|
|||
import Cocoa
|
||||
|
||||
public protocol FSEventStreamHelperDelegate: AnyObject {
|
||||
func helper(_ helper: FSEventStreamHelper, didReceive events: [FSEventStreamHelper.Event])
|
||||
}
|
||||
|
||||
public class FSEventStreamHelper : NSObject {
|
||||
|
||||
public struct Event {
|
||||
var path: String
|
||||
var flags: FSEventStreamEventFlags
|
||||
var id: FSEventStreamEventId
|
||||
}
|
||||
|
||||
public let path: String
|
||||
public let dispatchQueue: DispatchQueue
|
||||
public weak var delegate: FSEventStreamHelperDelegate?
|
||||
|
||||
@objc public init(path: String, queue: DispatchQueue) {
|
||||
self.path = path
|
||||
self.dispatchQueue = queue
|
||||
}
|
||||
|
||||
private var stream: FSEventStreamRef? = nil
|
||||
|
||||
public func start() -> Bool {
|
||||
if stream != nil {
|
||||
return false
|
||||
}
|
||||
var context = FSEventStreamContext()
|
||||
context.info = Unmanaged.passUnretained(self).toOpaque()
|
||||
guard let stream = FSEventStreamCreate(nil, {
|
||||
(stream, clientCallBackInfo, eventCount, eventPaths, eventFlags, eventIds) in
|
||||
let helper = Unmanaged<FSEventStreamHelper>.fromOpaque(clientCallBackInfo!).takeUnretainedValue()
|
||||
let pathsBase = eventPaths.assumingMemoryBound(to: UnsafePointer<CChar>.self)
|
||||
let pathsPtr = UnsafeBufferPointer(start: pathsBase, count: eventCount)
|
||||
let flagsPtr = UnsafeBufferPointer(start: eventFlags, count: eventCount)
|
||||
let eventIDsPtr = UnsafeBufferPointer(start: eventIds, count: eventCount)
|
||||
let events = (0..<eventCount).map {
|
||||
FSEventStreamHelper.Event(path: String(cString: pathsPtr[$0]),
|
||||
flags: flagsPtr[$0],
|
||||
id: eventIDsPtr[$0] )
|
||||
}
|
||||
helper.delegate?.helper(helper, didReceive: events)
|
||||
},
|
||||
&context,
|
||||
[path] as CFArray,
|
||||
UInt64(kFSEventStreamEventIdSinceNow),
|
||||
1.0,
|
||||
FSEventStreamCreateFlags(kFSEventStreamCreateFlagNone)
|
||||
) else {
|
||||
return false
|
||||
}
|
||||
|
||||
FSEventStreamSetDispatchQueue(stream, dispatchQueue)
|
||||
if !FSEventStreamStart(stream) {
|
||||
FSEventStreamInvalidate(stream)
|
||||
return false
|
||||
}
|
||||
self.stream = stream
|
||||
return true
|
||||
}
|
||||
|
||||
func stop() {
|
||||
guard let stream = stream else {
|
||||
return
|
||||
}
|
||||
FSEventStreamStop(stream)
|
||||
FSEventStreamInvalidate(stream)
|
||||
self.stream = nil
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import XCTest
|
||||
@testable import FSEventStreamHelper
|
||||
|
||||
final class FSEventStreamHelperTests: XCTestCase {
|
||||
}
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
import Cocoa
|
||||
import InputMethodKit
|
||||
import FSEventStreamHelper
|
||||
|
||||
private let kCheckUpdateAutomatically = "CheckUpdateAutomatically"
|
||||
private let kNextUpdateCheckDateKey = "NextUpdateCheckDate"
|
||||
|
@ -150,11 +151,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, NonModalAlertWindowControlle
|
|||
private var preferencesWindowController: PreferencesWindowController?
|
||||
private var checkTask: URLSessionTask?
|
||||
private var updateNextStepURL: URL?
|
||||
private var fsStreamHelper = FSEventStreamHelper(path: LanguageModelManager.dataFolderPath, queue: DispatchQueue(label: "User Phrases"))
|
||||
|
||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||
LanguageModelManager.setupDataModelValueConverter()
|
||||
LanguageModelManager.loadUserPhrases()
|
||||
LanguageModelManager.loadUserPhraseReplacement()
|
||||
fsStreamHelper.delegate = self
|
||||
_ = fsStreamHelper.start()
|
||||
|
||||
if UserDefaults.standard.object(forKey: kCheckUpdateAutomatically) == nil {
|
||||
UserDefaults.standard.set(true, forKey: kCheckUpdateAutomatically)
|
||||
|
@ -243,3 +247,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NonModalAlertWindowControlle
|
|||
updateNextStepURL = nil
|
||||
}
|
||||
}
|
||||
|
||||
extension AppDelegate : FSEventStreamHelperDelegate {
|
||||
func helper(_ helper: FSEventStreamHelper, didReceive events: [FSEventStreamHelper.Event]) {
|
||||
DispatchQueue.main.async {
|
||||
LanguageModelManager.loadUserPhrases()
|
||||
LanguageModelManager.loadUserPhraseReplacement()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -393,7 +393,7 @@ extension McBopomofoInputMethodController {
|
|||
if state.tooltip.isEmpty {
|
||||
hideTooltip()
|
||||
} else {
|
||||
show(tooltip: state.tooltip, composingBuffer: state.composingBuffer, cursorIndex: state.cursorIndex, client: client)
|
||||
show(tooltip: state.tooltip, composingBuffer: state.composingBuffer, cursorIndex: state.markerIndex, client: client)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -222,7 +222,9 @@ static void LTLoadLanguageModelFile(NSString *filenameWithoutExtension, McBopomo
|
|||
[writeFile writeData:data];
|
||||
[writeFile closeFile];
|
||||
|
||||
[self loadUserPhrases];
|
||||
// We use FSEventStream to monitor the change of the user phrase folder,
|
||||
// so we don't have to load data here.
|
||||
// [self loadUserPhrases];
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue