diff --git a/.gitignore b/.gitignore
index 637b6d18..97372299 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,3 +30,6 @@ DerivedData/
# Testing
code_coverage.json
+
+# Python virtual environment
+venv/
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 6f75e2c1..26bf5b4b 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,4 +1,4 @@
-# Setting up a Python environment and pre-commit.
+# Set up a Python environment and prek for pre-commit hooks.
# Unix or MacOS:
# >>> python3 -m venv venv
@@ -9,9 +9,9 @@
# >>> venv\Scripts\activate.bat
# >>> pip install --upgrade pip
-# >>> pip install pre-commit
-# >>> pre-commit install
-# >>> pre-commit run --all-files
+# >>> pip install prek
+# >>> prek install
+# >>> prek run --all-files
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
diff --git a/Keyboards/DataManager/LanguageDBManager.swift b/Keyboards/DataManager/LanguageDBManager.swift
index d1e8461c..c8925d31 100644
--- a/Keyboards/DataManager/LanguageDBManager.swift
+++ b/Keyboards/DataManager/LanguageDBManager.swift
@@ -22,23 +22,42 @@ class LanguageDBManager {
/// Makes a connection to the language database given the value for controllerLanguage.
private func openDBQueue(_ dbName: String) -> DatabaseQueue {
- let dbResourcePath = Bundle.main.path(forResource: dbName, ofType: "sqlite")!
+ let mainBundlePath = Bundle.main.path(forResource: dbName, ofType: "sqlite")
+ let classBundlePath = Bundle(for: LanguageDBManager.self).path(forResource: dbName, ofType: "sqlite")
+
+ guard let resourcePath = mainBundlePath ?? classBundlePath else {
+ print("Database \(dbName).sqlite not found in main or class bundle. Using in-memory DB.")
+ return try! DatabaseQueue()
+ }
+
let fileManager = FileManager.default
do {
- let dbPath = try fileManager
- .url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
- .appendingPathComponent("\(dbName).sqlite")
- .path
+ let appSupportURL = try fileManager.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
+ let dbURL = appSupportURL.appendingPathComponent("\(dbName).sqlite")
+ let dbPath = dbURL.path
+
+ var shouldCopy = true
if fileManager.fileExists(atPath: dbPath) {
+ // Only copy if the resource is newer or if we want to ensure a fresh copy.
+ // For now, keeping the "fresh copy" behavior but more safely.
try fileManager.removeItem(atPath: dbPath)
}
- try fileManager.copyItem(atPath: dbResourcePath, toPath: dbPath)
- let dbQueue = try DatabaseQueue(path: dbPath)
- return dbQueue
+
+ if shouldCopy {
+ try fileManager.copyItem(atPath: resourcePath, toPath: dbPath)
+ }
+
+ return try DatabaseQueue(path: dbPath)
} catch {
- print("An error occurred: UILexicon not available")
- let dbQueue = try! DatabaseQueue(path: dbResourcePath)
- return dbQueue
+ print("An error occurred during DB setup for \(dbName): \(error). Attempting read-only access.")
+ var config = Configuration()
+ config.readonly = true
+ if let dbQueue = try? DatabaseQueue(path: resourcePath, configuration: config) {
+ return dbQueue
+ }
+ // Last resort: try to return an empty DB instead of crashing the keyword.
+ print("Failed to open database \(dbName) even in read-only mode. Returning empty DB.")
+ return try! DatabaseQueue()
}
}
@@ -273,6 +292,49 @@ extension LanguageDBManager {
return queryDBRow(query: query, outputCols: outputCols, args: StatementArguments(args))
}
+ /// Query emojis of word in `emoji_keywords` using pattern matching.
+ func queryEmojisPatternMatching(of word: String) -> [String] {
+ var outputValues = [String]()
+ let query = """
+ SELECT
+ emoji_keyword_0, emoji_keyword_1, emoji_keyword_2
+
+ FROM
+ emoji_keywords
+
+ WHERE
+ word LIKE ?
+
+ ORDER BY
+ LENGTH(word) ASC
+
+ LIMIT
+ 3
+ """
+ let args = StatementArguments(["\(word.lowercased())%"])
+ do {
+ try database?.read { db in
+ let rows = try Row.fetchAll(db, sql: query, arguments: args)
+ for row in rows {
+ for col in ["emoji_keyword_0", "emoji_keyword_1", "emoji_keyword_2"] {
+ if let val = row[col] as? String, !val.isEmpty {
+ if !outputValues.contains(val) {
+ outputValues.append(val)
+ }
+ if outputValues.count == 9 { return }
+ }
+ }
+ }
+ }
+ } catch {}
+
+ while outputValues.count < 9 {
+ outputValues.append("")
+ }
+
+ return Array(outputValues.prefix(9))
+ }
+
/// Query the noun form of word in `nonuns`.
func queryNounForm(of word: String) -> [String] {
let language = getControllerLanguageAbbr()
diff --git a/Keyboards/KeyboardsBase/InterfaceVariables.swift b/Keyboards/KeyboardsBase/InterfaceVariables.swift
index a5a509f3..5f8fecb7 100644
--- a/Keyboards/KeyboardsBase/InterfaceVariables.swift
+++ b/Keyboards/KeyboardsBase/InterfaceVariables.swift
@@ -94,6 +94,7 @@ enum CommandState {
case invalid
case displayInformation
case dynamicConjugation
+ case colonToEmoji
}
/// States of the keyboard corresponding to which auto actions should be presented.
diff --git a/Keyboards/KeyboardsBase/Keyboard.xib b/Keyboards/KeyboardsBase/Keyboard.xib
index 29c5703d..bdb8a87f 100644
--- a/Keyboards/KeyboardsBase/Keyboard.xib
+++ b/Keyboards/KeyboardsBase/Keyboard.xib
@@ -50,12 +50,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -375,7 +387,7 @@
-