Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
368 changes: 368 additions & 0 deletions SKILL.md

Large diffs are not rendered by default.

6 changes: 1 addition & 5 deletions sources/BridgingFunctions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,18 @@ extension RimeModule: DataSizeable {}
extension DataSizeable {
static func rimeStructInit() -> Self {
let valuePointer = UnsafeMutablePointer<Self>.allocate(capacity: 1)
// Initialize the memory to zero
memset(valuePointer, 0, MemoryLayout<Self>.size)
// Convert the pointer to a managed Swift variable
var value = valuePointer.move()
valuePointer.deallocate()
// Initialize data_size property
let offset = MemoryLayout.size(ofValue: \Self.data_size)
value.data_size = Int32(MemoryLayout<Self>.size - offset)
return value
}

mutating func setCString(_ swiftString: String, to keypath: WritableKeyPath<Self, UnsafePointer<CChar>?>) {
swiftString.withCString { cStr in
// Duplicate the string to create a persisting C string
// Rime traits keep C string pointers after this closure returns.
let mutableCStr = strdup(cStr)
// Free the existing string if there is one
if let existing = self[keyPath: keypath] {
free(UnsafeMutableRawPointer(mutating: existing))
}
Expand Down
4 changes: 1 addition & 3 deletions sources/InputSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ final class SquirrelInstaller {
for inputSource in sourceList {
let sourceIDRef = TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID)
guard let sourceID = unsafeBitCast(sourceIDRef, to: CFString?.self) as String? else { continue }
// print("[DEBUG] Examining input source: \(sourceID)")
inputSources[sourceID] = inputSource
}
return inputSources
Expand All @@ -44,7 +43,6 @@ final class SquirrelInstaller {
let enabledInputModes = enabledModes()
if !enabledInputModes.isEmpty {
print("User already registered Squirrel method(s): \(enabledInputModes.map { $0.rawValue })")
// Already registered.
return
}
TISRegisterInputSource(SquirrelApp.appDir as CFURL)
Expand All @@ -55,7 +53,7 @@ final class SquirrelInstaller {
let enabledInputModes = enabledModes()
if !enabledInputModes.isEmpty && modes.isEmpty {
print("User already enabled Squirrel method(s): \(enabledInputModes.map { $0.rawValue })")
// keep user's manually enabled input modes.
// Preserve manually enabled input modes.
return
}
let modesToEnable = modes.isEmpty ? [.primary] : modes
Expand Down
2 changes: 1 addition & 1 deletion sources/MacOSKeyCodes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ struct SquirrelKeycode {
}

if let keychar = keychar, keychar.isASCII, let codeValue = keychar.unicodeScalars.first?.value {
// NOTE: IBus/Rime use different keycodes for uppercase/lowercase letters.
// IBus/Rime use different keycodes for uppercase and lowercase letters.
if keychar.isLowercase && (shift != caps) {
// lowercase -> Uppercase
return keychar.uppercased().unicodeScalars.first!.value
Expand Down
8 changes: 1 addition & 7 deletions sources/Main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,7 @@ struct SquirrelApp {
}
return true
case "--build":
// Notification
SquirrelApplicationDelegate.showMessage(msgText: NSLocalizedString("deploy_update", comment: ""))
// Build all schemas in current directory
var builderTraits = RimeTraits.rimeStructInit()
builderTraits.setCString("rime.squirrel-builder", to: \.app_name)
rimeAPI.setup(&builderTraits)
Expand Down Expand Up @@ -125,18 +123,15 @@ struct SquirrelApp {
}

autoreleasepool {
// find the bundle identifier and then initialize the input method server
let main = Bundle.main
let connectionName = main.object(forInfoDictionaryKey: "InputMethodConnectionName") as! String
_ = IMKServer(name: connectionName, bundleIdentifier: main.bundleIdentifier!)
// load the bundle explicitly because in this case the input method is a
// background only application
let app = NSApplication.shared
let delegate = SquirrelApplicationDelegate()
app.delegate = delegate
app.setActivationPolicy(.accessory)

// opencc will be configured with relative dictionary paths
// OpenCC uses relative dictionary paths from SharedSupport.
FileManager.default.changeCurrentDirectoryPath(main.sharedSupportPath!)

if NSApp.squirrelAppDelegate.problematicLaunchDetected() {
Expand All @@ -155,7 +150,6 @@ struct SquirrelApp {
print("Squirrel reporting!")
}

// finally run everything
app.run()
print("Squirrel is quitting...")
rimeAPI.finalize()
Expand Down
18 changes: 3 additions & 15 deletions sources/ReservedProperty.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,25 @@
// ReservedProperty.swift
// Squirrel
//
// Reserved librime properties for plugin -> frontend coordination.
// See rime/squirrel#1124.
//
// Values use URL-style query strings. Bare values are stored under
// "value" for compatibility with historical comma-list payloads.

import Foundation

/// Reserved property keys recognised by Squirrel.
// Reserved librime properties for plugin-to-frontend coordination. Values use
// URL-style query strings; bare values are stored under "value" for compatibility
// with historical comma-list payloads. See rime/squirrel#1124.
enum ReservedPropertyKey: String {
/// Candidate comment indices using `accent_text_color`.
case commentHighlight = "_comment_highlight"

/// Candidate comment indices using `warning_text_color`.
case commentWarning = "_comment_warning"

/// Requests a candidate panel refresh.
case refreshUI = "_refresh_ui"
}

/// Parsed reserved-property fields.
struct ReservedPropertyValue {
let fields: [String: String]

/// Field used for bare values and index lists.
static let defaultField = "value"

static let empty = ReservedPropertyValue(fields: [:])

Check warning on line 22 in sources/ReservedProperty.swift

View workflow job for this annotation

GitHub Actions / build

unused

Property 'empty' is unused

/// Parses query strings or bare values written by plugins.
static func parse(_ raw: String) throws(ReservedPropertyError) -> ReservedPropertyValue {
guard !raw.isEmpty else { throw .emptyInput }
if !raw.contains("=") {
Expand All @@ -46,7 +35,6 @@
throw .unknownInput(raw)
}

/// Extracts non-negative indices from the `value` field.
func indices() throws(ReservedPropertyError) -> Set<Int> {
guard let raw = fields[Self.defaultField] else { throw .missingDefaultFields }
var out = Set<Int>()
Expand Down
13 changes: 2 additions & 11 deletions sources/SquirrelApplicationDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ final class SquirrelApplicationDelegate: NSObject, NSApplicationDelegate, SPUSta
func setupRime() {
createDirIfNotExist(path: SquirrelApp.userDir)
createDirIfNotExist(path: SquirrelApp.logDir)
// expose log file location to librime for librime plugins to write into
// Expose the log directory to librime plugins.
setenv("RIME_LOG_DIR", SquirrelApp.logDir.path(), 1)
// swiftlint:disable identifier_name
let notification_handler: @convention(c) (UnsafeMutableRawPointer?, RimeSessionId, UnsafePointer<CChar>?, UnsafePointer<CChar>?) -> Void = notificationHandler
Expand All @@ -161,13 +161,8 @@ final class SquirrelApplicationDelegate: NSObject, NSApplicationDelegate, SPUSta
func startRime(fullCheck: Bool) {
print("Initializing la rime...")
rimeAPI.initialize(nil)
// check for configuration updates
if rimeAPI.start_maintenance(fullCheck) {
// update squirrel config
// print("[DEBUG] maintenance suceeds")
_ = rimeAPI.deploy_config_file("squirrel.yaml", "config_version")
} else {
// print("[DEBUG] maintenance fails")
}
}

Expand Down Expand Up @@ -203,11 +198,10 @@ final class SquirrelApplicationDelegate: NSObject, NSApplicationDelegate, SPUSta
schema.close()
}

// prevent freezing the system
// Detect repeated launches that may indicate a bad configuration loop.
func problematicLaunchDetected() -> Bool {
var detected = false
let logFile = FileManager.default.temporaryDirectory.appendingPathComponent("squirrel_launch.json", conformingTo: .json)
// print("[DEBUG] archive: \(logFile)")
do {
let archive = try Data(contentsOf: logFile, options: [.uncached])
let decoder = JSONDecoder()
Expand All @@ -233,9 +227,6 @@ final class SquirrelApplicationDelegate: NSObject, NSApplicationDelegate, SPUSta
return detected
}

// add an awakeFromNib item so that we can set the action method. Note that
// any menuItems without an action will be disabled when displayed in the Text
// Input Menu.
func addObservers() {
let center = NSWorkspace.shared.notificationCenter
center.addObserver(forName: NSWorkspace.willPowerOffNotification, object: nil, queue: nil, using: workspaceWillPowerOff)
Expand Down
1 change: 0 additions & 1 deletion sources/SquirrelConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ final class SquirrelConfig {
var iterator = RimeConfigIterator()
_ = rimeAPI.config_begin_map(&iterator, &config, rootKey)
while rimeAPI.config_next(&iterator) {
// print("[DEBUG] option[\(iterator.index)]: \(String(cString: iterator.key)), path: (\(String(cString: iterator.path))")
if let key = iterator.key, let path = iterator.path, let value = getBool(String(cString: path)) {
appOptions[String(cString: key)] = value
}
Expand Down
Loading
Loading