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 */; };
|
D461B792279DAC010070E734 /* InputState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D461B791279DAC010070E734 /* InputState.swift */; };
|
||||||
D47B92C027972AD100458394 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47B92BF27972AC800458394 /* main.swift */; };
|
D47B92C027972AD100458394 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47B92BF27972AC800458394 /* main.swift */; };
|
||||||
D47D73A427A5D43900255A50 /* KeyHandlerBopomofoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47D73A327A5D43900255A50 /* KeyHandlerBopomofoTests.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 */; };
|
D47F7DCE278BFB57002F9DD7 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */; };
|
||||||
D47F7DD0278C0897002F9DD7 /* NonModalAlertWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */; };
|
D47F7DD0278C0897002F9DD7 /* NonModalAlertWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */; };
|
||||||
D47F7DD3278C1263002F9DD7 /* UserOverrideModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DD2278C1263002F9DD7 /* UserOverrideModel.cpp */; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
D47F7DD1278C1263002F9DD7 /* UserOverrideModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserOverrideModel.h; sourceTree = "<group>"; };
|
||||||
|
@ -232,6 +234,7 @@
|
||||||
D427F7C127908EFC004A2160 /* OpenCCBridge in Frameworks */,
|
D427F7C127908EFC004A2160 /* OpenCCBridge in Frameworks */,
|
||||||
D44FB74A2791B829003C80A6 /* VXHanConvert in Frameworks */,
|
D44FB74A2791B829003C80A6 /* VXHanConvert in Frameworks */,
|
||||||
D427F7A927905E90004A2160 /* TooltipUI in Frameworks */,
|
D427F7A927905E90004A2160 /* TooltipUI in Frameworks */,
|
||||||
|
D47D73C327A7200500255A50 /* FSEventStreamHelper in Frameworks */,
|
||||||
D427F76A278C9E29004A2160 /* CandidateUI in Frameworks */,
|
D427F76A278C9E29004A2160 /* CandidateUI in Frameworks */,
|
||||||
D427F7AE27907B8A004A2160 /* NotifierUI in Frameworks */,
|
D427F7AE27907B8A004A2160 /* NotifierUI in Frameworks */,
|
||||||
);
|
);
|
||||||
|
@ -262,6 +265,7 @@
|
||||||
6A0D4EC215FC0D3C00ABF4B3 /* Source */,
|
6A0D4EC215FC0D3C00ABF4B3 /* Source */,
|
||||||
D485D3B72796A8A000657FF3 /* McBopomofoTests */,
|
D485D3B72796A8A000657FF3 /* McBopomofoTests */,
|
||||||
6A0D4EA315FC0D2D00ABF4B3 /* Products */,
|
6A0D4EA315FC0D2D00ABF4B3 /* Products */,
|
||||||
|
D47D73C127A7200500255A50 /* Frameworks */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
@ -467,10 +471,18 @@
|
||||||
D427F7B2279086B5004A2160 /* InputSourceHelper */,
|
D427F7B2279086B5004A2160 /* InputSourceHelper */,
|
||||||
D427F7BF27908EAC004A2160 /* OpenCCBridge */,
|
D427F7BF27908EAC004A2160 /* OpenCCBridge */,
|
||||||
D44FB7482791B346003C80A6 /* VXHanConvert */,
|
D44FB7482791B346003C80A6 /* VXHanConvert */,
|
||||||
|
D47D73C027A71FFA00255A50 /* FSEventStreamHelper */,
|
||||||
);
|
);
|
||||||
name = Packages;
|
name = Packages;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
D47D73C127A7200500255A50 /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
D485D3B72796A8A000657FF3 /* McBopomofoTests */ = {
|
D485D3B72796A8A000657FF3 /* McBopomofoTests */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -522,6 +534,7 @@
|
||||||
D427F7B3279086DC004A2160 /* InputSourceHelper */,
|
D427F7B3279086DC004A2160 /* InputSourceHelper */,
|
||||||
D427F7C027908EFC004A2160 /* OpenCCBridge */,
|
D427F7C027908EFC004A2160 /* OpenCCBridge */,
|
||||||
D44FB7492791B829003C80A6 /* VXHanConvert */,
|
D44FB7492791B829003C80A6 /* VXHanConvert */,
|
||||||
|
D47D73C227A7200500255A50 /* FSEventStreamHelper */,
|
||||||
);
|
);
|
||||||
productName = McBopomofo;
|
productName = McBopomofo;
|
||||||
productReference = 6A0D4EA215FC0D2D00ABF4B3 /* McBopomofo.app */;
|
productReference = 6A0D4EA215FC0D2D00ABF4B3 /* McBopomofo.app */;
|
||||||
|
@ -1387,6 +1400,10 @@
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = VXHanConvert;
|
productName = VXHanConvert;
|
||||||
};
|
};
|
||||||
|
D47D73C227A7200500255A50 /* FSEventStreamHelper */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = FSEventStreamHelper;
|
||||||
|
};
|
||||||
/* End XCSwiftPackageProductDependency section */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
};
|
};
|
||||||
rootObject = 6A0D4E9415FC0CFA00ABF4B3 /* Project object */;
|
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 Cocoa
|
||||||
import InputMethodKit
|
import InputMethodKit
|
||||||
|
import FSEventStreamHelper
|
||||||
|
|
||||||
private let kCheckUpdateAutomatically = "CheckUpdateAutomatically"
|
private let kCheckUpdateAutomatically = "CheckUpdateAutomatically"
|
||||||
private let kNextUpdateCheckDateKey = "NextUpdateCheckDate"
|
private let kNextUpdateCheckDateKey = "NextUpdateCheckDate"
|
||||||
|
@ -150,11 +151,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, NonModalAlertWindowControlle
|
||||||
private var preferencesWindowController: PreferencesWindowController?
|
private var preferencesWindowController: PreferencesWindowController?
|
||||||
private var checkTask: URLSessionTask?
|
private var checkTask: URLSessionTask?
|
||||||
private var updateNextStepURL: URL?
|
private var updateNextStepURL: URL?
|
||||||
|
private var fsStreamHelper = FSEventStreamHelper(path: LanguageModelManager.dataFolderPath, queue: DispatchQueue(label: "User Phrases"))
|
||||||
|
|
||||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||||
LanguageModelManager.setupDataModelValueConverter()
|
LanguageModelManager.setupDataModelValueConverter()
|
||||||
LanguageModelManager.loadUserPhrases()
|
LanguageModelManager.loadUserPhrases()
|
||||||
LanguageModelManager.loadUserPhraseReplacement()
|
LanguageModelManager.loadUserPhraseReplacement()
|
||||||
|
fsStreamHelper.delegate = self
|
||||||
|
_ = fsStreamHelper.start()
|
||||||
|
|
||||||
if UserDefaults.standard.object(forKey: kCheckUpdateAutomatically) == nil {
|
if UserDefaults.standard.object(forKey: kCheckUpdateAutomatically) == nil {
|
||||||
UserDefaults.standard.set(true, forKey: kCheckUpdateAutomatically)
|
UserDefaults.standard.set(true, forKey: kCheckUpdateAutomatically)
|
||||||
|
@ -243,3 +247,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NonModalAlertWindowControlle
|
||||||
updateNextStepURL = nil
|
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 {
|
if state.tooltip.isEmpty {
|
||||||
hideTooltip()
|
hideTooltip()
|
||||||
} else {
|
} 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 writeData:data];
|
||||||
[writeFile closeFile];
|
[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;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue