Skip to content

Commit 503c90b

Browse files
committed
Plural command: update query for plural command and add plural annotation
1 parent 4c7eb1c commit 503c90b

7 files changed

Lines changed: 178 additions & 39 deletions

File tree

Keyboards/DataManager/LanguageDBManager.swift

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -97,16 +97,22 @@ class LanguageDBManager {
9797
/// - query: the query to run against the language database.
9898
/// - outputCols: the columns from which the values should come.
9999
/// - args: arguments to pass to `query`.
100-
private func queryDBRows(query: String, outputCols _: [String], args: StatementArguments) -> [String] {
100+
private func queryDBRows(query: String, outputCols: [String], args: StatementArguments) -> [String] {
101101
var outputValues = [String]()
102102
do {
103103
guard let languageDB = database else { return [] }
104104
let rows = try languageDB.read { db in
105105
try Row.fetchAll(db, sql: query, arguments: args)
106106
}
107107
for r in rows {
108-
outputValues.append(r["word"])
109-
}
108+
// Loop through all columns.
109+
for col in outputCols {
110+
if let value = r[col] as? String,
111+
!value.trimmingCharacters(in: .whitespaces).isEmpty {
112+
outputValues.append(value)
113+
}
114+
}
115+
}
110116
} catch let error as DatabaseError {
111117
let errorMessage = error.message
112118
let errorSQL = error.sql
@@ -301,20 +307,47 @@ extension LanguageDBManager {
301307

302308
/// Query the plural form of word in `nouns`.
303309
func queryNounPlural(of word: String) -> [String] {
304-
let query = """
305-
SELECT
306-
*
310+
let language = getControllerLanguageAbbr()
311+
let contract = ContractManager.shared.loadContract(language: language)
307312

308-
FROM
309-
nouns
313+
let queryInfos = PluralManager.shared.buildPluralQuery(
314+
word: word,
315+
contract: contract
316+
)
310317

311-
WHERE
312-
noun = ?
313-
"""
314-
let outputCols = ["plural"]
315-
let args = [word]
318+
// Try each query until we find a result.
319+
for queryInfo in queryInfos {
320+
let result = queryDBRow(
321+
query: queryInfo.query,
322+
outputCols: queryInfo.outputCols,
323+
args: StatementArguments(queryInfo.args)
324+
)
316325

317-
return queryDBRow(query: query, outputCols: outputCols, args: StatementArguments(args))
326+
// If we found a result, return it.
327+
if result.count >= 2 && !result[1].isEmpty {
328+
return [result[1]]
329+
}
330+
}
331+
332+
return []
333+
}
334+
335+
/// Query all plural forms for the current language.
336+
func queryAllPluralForms() -> [String]? {
337+
let language = getControllerLanguageAbbr()
338+
let contract = ContractManager.shared.loadContract(language: language)
339+
340+
guard let queryInfo = PluralManager.shared.buildAllPluralsQuery(contract: contract) else {
341+
return nil
342+
}
343+
344+
let result = queryDBRows(
345+
query: queryInfo.query,
346+
outputCols: queryInfo.outputCols,
347+
args: StatementArguments()
348+
)
349+
350+
return result == [""] ? nil : result
318351
}
319352

320353
/// Query preposition form of word in `prepositions`.
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
import Foundation
3+
4+
/// Manages plural query logic based on data contracts
5+
class PluralManager {
6+
static let shared = PluralManager()
7+
8+
private init() {}
9+
10+
struct PluralQueryInfo {
11+
let query: String
12+
let outputCols: [String]
13+
let args: [String]
14+
}
15+
16+
/**Builds a query to find the plural form of a word based on the contract structure
17+
- Parameters:
18+
- word: The singular word to find the plural for
19+
- contract: The data contract defining the database structure
20+
- Returns: Query info to execute, or nil if contract is invalid
21+
*/
22+
func buildPluralQuery(word: String, contract: DataContract?) -> [PluralQueryInfo] { // ← Return array!
23+
guard let contract = contract,
24+
let numbers = contract.numbers,
25+
!numbers.isEmpty else {
26+
NSLog("PluralManager: No valid plural columns in contract")
27+
return []
28+
}
29+
30+
var queries: [PluralQueryInfo] = []
31+
32+
// Build a query for EACH singular/plural pair
33+
for (singularCol, pluralCol) in numbers {
34+
let query = """
35+
SELECT `\(singularCol)`, `\(pluralCol)`
36+
FROM nouns
37+
WHERE `\(singularCol)` = ? COLLATE NOCASE
38+
"""
39+
40+
queries.append(PluralQueryInfo(
41+
query: query,
42+
outputCols: [singularCol, pluralCol],
43+
args: [word.lowercased()]
44+
))
45+
}
46+
47+
return queries
48+
}
49+
50+
/**Builds a query to get all plural forms for a language
51+
- Parameter contract: The data contract defining plural columns
52+
- Returns: Query info to execute, or nil if contract is invalid
53+
*/
54+
func buildAllPluralsQuery(contract: DataContract?) -> PluralQueryInfo? {
55+
guard let contract = contract,
56+
let numbers = contract.numbers,
57+
!numbers.isEmpty else {
58+
NSLog("PluralManager: No valid plural columns in contract")
59+
return nil
60+
}
61+
62+
let pluralColumns = Array(numbers.values)
63+
let columns = pluralColumns.map { "`\($0)`" }.joined(separator: ", ")
64+
let query = "SELECT \(columns) FROM nouns"
65+
66+
return PluralQueryInfo(
67+
query: query,
68+
outputCols: pluralColumns,
69+
args: []
70+
)
71+
}
72+
}

Keyboards/KeyboardsBase/InterfaceVariables.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ var shiftButtonState: ShiftButtonState = .normal
118118
var commandState: CommandState = .idle
119119
var autoActionState: AutoActionState = .suggest
120120
var conjViewShiftButtonsState: ConjViewShiftButtonsState = .bothInactive
121+
var pluralWords: Set<String>?
121122

122123
// Variables and functions to determine display parameters.
123124
enum DeviceType {

Keyboards/KeyboardsBase/KeyboardViewController.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2057,6 +2057,11 @@ class KeyboardViewController: UIInputViewController {
20572057

20582058
// Drop non-unique values in case the lexicon has added words that were already present.
20592059
LanguageDBManager.shared.deleteNonUniqueAutocompletions()
2060+
2061+
// Load plural words for the current language
2062+
if let allPlurals = LanguageDBManager.shared.queryAllPluralForms() {
2063+
pluralWords = Set(allPlurals.map { $0.lowercased() })
2064+
}
20602065
}
20612066

20622067
setKeyboard()

Keyboards/KeyboardsBase/ScribeFunctionality/Annotate.swift

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,15 @@ func convertFullGenderToAbbr(_ genderFull: String) -> String {
5252
/// - wordToAnnotate: the word that an annotation should be created for.
5353
/// - KVC: the keyboard view controller.
5454
func wordAnnotation(wordToAnnotate: String, KVC: KeyboardViewController) {
55-
let nounForm = LanguageDBManager.shared.queryNounForm(of: wordToAnnotate)[0]
55+
var nounForm: String
56+
57+
// Check if word is plural first
58+
if let pluralWords = pluralWords, pluralWords.contains(wordToAnnotate.lowercased()) {
59+
nounForm = "PL"
60+
} else {
61+
nounForm = LanguageDBManager.shared.queryNounForm(of: wordToAnnotate)[0]
62+
}
63+
5664
prepAnnotationForm = LanguageDBManager.shared.queryPrepForm(of: wordToAnnotate.lowercased())[0]
5765

5866
hasNounForm = !nounForm.isEmpty
@@ -254,7 +262,14 @@ func typedWordAnnotation(KVC: KeyboardViewController) {
254262
/// - index: the auto action key index that the annotation should be set for.
255263
/// - KVC: the keyboard view controller.
256264
func autoActionAnnotation(autoActionWord: String, index: Int, KVC: KeyboardViewController) {
257-
let nounForm = LanguageDBManager.shared.queryNounForm(of: autoActionWord)[0]
265+
var nounForm: String
266+
267+
// Check if word is plural first
268+
if let pluralWords = pluralWords, pluralWords.contains(autoActionWord.lowercased()) {
269+
nounForm = "PL"
270+
} else {
271+
nounForm = LanguageDBManager.shared.queryNounForm(of: autoActionWord)[0]
272+
}
258273

259274
hasNounForm = !nounForm.isEmpty
260275

Keyboards/KeyboardsBase/ScribeFunctionality/Plural.swift

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import UIKit
1313
func queryPlural(commandBar: UILabel) {
1414
// Cancel via a return press.
1515
if let commandBarText = commandBar.text,
16-
commandBarText == pluralPromptAndCursor || commandBarText == pluralPromptAndCursor {
16+
commandBarText == pluralPromptAndCursor {
1717
return
1818
}
1919

@@ -37,25 +37,25 @@ func queryPluralNoun(queriedNoun: String) {
3737
noun = noun.lowercased()
3838
}
3939

40-
wordToReturn = LanguageDBManager.shared.queryNounPlural(of: noun)[0]
40+
// Check if the word is already plural (always use lowercase for Set lookup).
41+
let lowercaseNoun = noun.lowercased()
42+
let isAlreadyPlural = pluralWords?.contains(lowercaseNoun) == true
4143

42-
guard !wordToReturn.isEmpty else {
43-
commandState = .invalid
44-
return
44+
if isAlreadyPlural {
45+
wordToReturn = noun // Use original capitalization.
46+
commandState = .alreadyPlural
47+
} else {
48+
let result = LanguageDBManager.shared.queryNounPlural(of: noun)
49+
guard !result.isEmpty, !result[0].isEmpty else {
50+
commandState = .invalid
51+
return
52+
}
53+
wordToReturn = result[0]
4554
}
4655

47-
if wordToReturn != "isPlural" {
48-
if inputWordIsCapitalized {
49-
proxy.insertText(wordToReturn.capitalized + getOptionalSpace())
50-
} else {
51-
proxy.insertText(wordToReturn + getOptionalSpace())
52-
}
56+
if inputWordIsCapitalized {
57+
proxy.insertText(wordToReturn.capitalized + getOptionalSpace())
5358
} else {
54-
if inputWordIsCapitalized {
55-
proxy.insertText(noun.capitalized + getOptionalSpace())
56-
} else {
57-
proxy.insertText(noun + getOptionalSpace())
58-
}
59-
commandState = .alreadyPlural
59+
proxy.insertText(wordToReturn + getOptionalSpace())
6060
}
6161
}

0 commit comments

Comments
 (0)