diff --git a/Sources/KanaKanjiConverterModule/Converter/KanaKanjiConverter.swift b/Sources/KanaKanjiConverterModule/Converter/KanaKanjiConverter.swift index e3d5ad0..bbbbcee 100644 --- a/Sources/KanaKanjiConverterModule/Converter/KanaKanjiConverter.swift +++ b/Sources/KanaKanjiConverterModule/Converter/KanaKanjiConverter.swift @@ -1,14 +1,7 @@ -// -// KanaKanjiConverter.swift -// AzooKeyKanaKanjiConverter -// -// Created by ensan on 2020/09/03. -// Copyright © 2020 ensan. All rights reserved. -// - import Foundation import SwiftUtils import EfficientNGram +import TimeExpression /// かな漢字変換の管理を受け持つクラス @MainActor public final class KanaKanjiConverter { @@ -156,6 +149,7 @@ import EfficientNGram result.append(contentsOf: self.unicodeCandidates(inputData)) } result.append(contentsOf: self.toVersionCandidate(inputData, options: options)) + result.append(contentsOf: self.toTimeExpressionCandidates(inputData)) return result } @@ -786,4 +780,11 @@ import EfficientNGram results.append(contentsOf: zeroHints.min(count: 10 - results.count, sortedBy: {$0.value > $1.value})) return results } + + private func toTimeExpressionCandidates(_ inputData: ComposingText) -> [Candidate] { + guard let number = Int(inputData.convertTarget) else { + return [] + } + return self.convertToTimeExpression(number) + } } diff --git a/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift b/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift new file mode 100644 index 0000000..b962229 --- /dev/null +++ b/Sources/KanaKanjiConverterModule/Converter/TimeExpression.swift @@ -0,0 +1,39 @@ +import Foundation + +extension KanaKanjiConverter { + func convertToTimeExpression(_ inputData: ComposingText) -> [Candidate] { + var candidates: [Candidate] = [] + let numberString = inputData.convertTarget + let firstPart = Int(numberString.prefix(2))! + let secondPart = Int(numberString.suffix(2))! + + if numberString.count == 3 { + let firstDigit = Int(numberString.prefix(1))! + let lastTwoDigits = Int(numberString.suffix(2))! + if (0...9).contains(firstDigit) && (0...60).contains(lastTwoDigits) { + let timeExpression = "\(firstDigit):\(String(format: "%02d", lastTwoDigits))" + let candidate = Candidate( + text: timeExpression, + value: -10, + correspondingCount: numberString.count, + lastMid: MIDData.一般.mid, + data: [DicdataElement(word: timeExpression, ruby: numberString, cid: CIDData.固有名詞.cid, mid: MIDData.一般.mid, value: -10)] + ) + candidates.append(candidate) + } + } else if numberString.count == 4 { + if (0...12).contains(firstPart) && (0...60).contains(secondPart) { + let timeExpression = "\(String(format: "%02d", firstPart)):\(String(format: "%02d", secondPart))" + let candidate = Candidate( + text: timeExpression, + value: -10, + correspondingCount: numberString.count, + lastMid: MIDData.一般.mid, + data: [DicdataElement(word: timeExpression, ruby: numberString, cid: CIDData.固有名詞.cid, mid: MIDData.一般.mid, value: -10)] + ) + candidates.append(candidate) + } + } + return candidates + } +} diff --git a/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift b/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift new file mode 100644 index 0000000..bb6c9ea --- /dev/null +++ b/Tests/KanaKanjiConverterModuleTests/ConverterTests/TimeExpressionTests.swift @@ -0,0 +1,72 @@ +import XCTest +@testable import KanaKanjiConverterModule + +final class TimeExpressionTests: XCTestCase { + func testConvertToTimeExpression() { + let converter = KanaKanjiConverter() + + // Test 3-digit numbers + XCTAssertEqual(converter.convertToTimeExpression(123).first?.text, "1:23") + XCTAssertEqual(converter.convertToTimeExpression(945).first?.text, "9:45") + XCTAssertEqual(converter.convertToTimeExpression(760).first?.text, "7:60") + XCTAssertTrue(converter.convertToTimeExpression(761).isEmpty) // Invalid minute + + // Test 4-digit numbers + XCTAssertEqual(converter.convertToTimeExpression(1234).first?.text, "12:34") + XCTAssertEqual(converter.convertToTimeExpression(9450).first?.text, "09:45") + XCTAssertEqual(converter.convertToTimeExpression(7600).first?.text, "07:60") + XCTAssertTrue(converter.convertToTimeExpression(1360).isEmpty) // Invalid hour + XCTAssertTrue(converter.convertToTimeExpression(1261).isEmpty) // Invalid minute + } + + func testToTimeExpressionCandidates() async throws { + let converter = KanaKanjiConverter() + var c = ComposingText() + + // Test 3-digit numbers + c.insertAtCursorPosition("123", inputStyle: .direct) + var results = await converter.requestCandidates(c, options: ConvertRequestOptions()) + XCTAssertTrue(results.mainResults.contains(where: {$0.text == "1:23"})) + + c = ComposingText() + c.insertAtCursorPosition("945", inputStyle: .direct) + results = await converter.requestCandidates(c, options: ConvertRequestOptions()) + XCTAssertTrue(results.mainResults.contains(where: {$0.text == "9:45"})) + + c = ComposingText() + c.insertAtCursorPosition("760", inputStyle: .direct) + results = await converter.requestCandidates(c, options: ConvertRequestOptions()) + XCTAssertTrue(results.mainResults.contains(where: {$0.text == "7:60"})) + + c = ComposingText() + c.insertAtCursorPosition("761", inputStyle: .direct) + results = await converter.requestCandidates(c, options: ConvertRequestOptions()) + XCTAssertFalse(results.mainResults.contains(where: {$0.text == "7:61"})) // Invalid minute + + // Test 4-digit numbers + c = ComposingText() + c.insertAtCursorPosition("1234", inputStyle: .direct) + results = await converter.requestCandidates(c, options: ConvertRequestOptions()) + XCTAssertTrue(results.mainResults.contains(where: {$0.text == "12:34"})) + + c = ComposingText() + c.insertAtCursorPosition("9450", inputStyle: .direct) + results = await converter.requestCandidates(c, options: ConvertRequestOptions()) + XCTAssertTrue(results.mainResults.contains(where: {$0.text == "09:45"})) + + c = ComposingText() + c.insertAtCursorPosition("7600", inputStyle: .direct) + results = await converter.requestCandidates(c, options: ConvertRequestOptions()) + XCTAssertTrue(results.mainResults.contains(where: {$0.text == "07:60"})) + + c = ComposingText() + c.insertAtCursorPosition("1360", inputStyle: .direct) + results = await converter.requestCandidates(c, options: ConvertRequestOptions()) + XCTAssertFalse(results.mainResults.contains(where: {$0.text == "13:60"})) // Invalid hour + + c = ComposingText() + c.insertAtCursorPosition("1261", inputStyle: .direct) + results = await converter.requestCandidates(c, options: ConvertRequestOptions()) + XCTAssertFalse(results.mainResults.contains(where: {$0.text == "12:61"})) // Invalid minute + } +}