From ee17b238a20876a5e590233ad4d667f55c7ba93a Mon Sep 17 00:00:00 2001 From: Miwa / Ensan Date: Sat, 12 Jul 2025 01:52:59 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E3=82=A4=E3=83=B3=E3=83=87=E3=83=83?= =?UTF-8?q?=E3=82=AF=E3=82=B9=E3=81=AE=E6=95=B4=E5=90=88=E6=80=A7=E3=82=92?= =?UTF-8?q?=E5=8F=96=E3=82=8B=E3=81=9F=E3=82=81=E3=81=AEAPI=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InputManagement/ComposingText.swift | 34 +++++++++++++++++++ .../ComposingTextTests.swift | 26 ++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/Sources/KanaKanjiConverterModule/InputManagement/ComposingText.swift b/Sources/KanaKanjiConverterModule/InputManagement/ComposingText.swift index e185097..4b5d051 100644 --- a/Sources/KanaKanjiConverterModule/InputManagement/ComposingText.swift +++ b/Sources/KanaKanjiConverterModule/InputManagement/ComposingText.swift @@ -384,6 +384,40 @@ public struct ComposingText: Sendable { return text } + public func inputIndexToSurfaceIndexMap() -> [Int: Int] { + // i2c: input indexからconvert target indexへのmap + // c2i: convert target indexからinput indexへのmap + + // 例1. + // [k, y, o, u, h, a, i, i, t, e, n, k, i, d, a] + // [き, ょ, う, は, い, い, て, ん, き, だ] + // i2c: [0: 0, 3: 2(きょ), 4: 3(う), 6: 4(は), 7: 5(い), 8: 6(い), 10: 7(て), 13: 9(んき), 15: 10(だ)] + + var map: [Int: (surfaceIndex: Int, surface: String)] = [0: (0, "")] + + // 逐次更新用のバッファ + var convertTargetElements: [ConvertTargetElement] = [] + + for (idx, element) in self.input.enumerated() { + // 要素を追加して表層文字列を更新 + Self.updateConvertTargetElements(currentElements: &convertTargetElements, newElement: element) + // 表層側の長さを再計算 + let currentSurface = convertTargetElements.reduce(into: "") { $0 += $1.string } + // idx 個の要素を処理し終えた直後(= 次の要素を処理する前)の + // カーソル位置は idx + 1 + map[idx + 1] = (currentSurface.count, currentSurface) + } + // 最終的なサーフェスと一致したものだけ残す + let finalSurface = convertTargetElements.reduce(into: "") { $0 += $1.string } + return map + .filter { + finalSurface.hasPrefix($0.value.surface) + } + .mapValues { + $0.surfaceIndex + } + } + public mutating func stopComposition() { self.input = [] self.convertTarget = "" diff --git a/Tests/KanaKanjiConverterModuleTests/ComposingTextTests.swift b/Tests/KanaKanjiConverterModuleTests/ComposingTextTests.swift index da96691..3082036 100644 --- a/Tests/KanaKanjiConverterModuleTests/ComposingTextTests.swift +++ b/Tests/KanaKanjiConverterModuleTests/ComposingTextTests.swift @@ -216,4 +216,30 @@ final class ComposingTextTests: XCTestCase { XCTAssertEqual(c2.differenceSuffix(to: c1).addedInput, 1) } } + + func testIndexMap() throws { + var c = ComposingText() + sequentialInput(&c, sequence: "kyouhaiitenkida", inputStyle: .roman2kana) + let map = c.inputIndexToSurfaceIndexMap() + + // Note: 現在の実装では、アドホックな対処によってnが"ん"に切り替わる + XCTAssertEqual(c.input[10], .init(character: "ん", inputStyle: .direct)) + + XCTAssertEqual(map[0], 0) // "" + XCTAssertEqual(map[1], nil) // k + XCTAssertEqual(map[2], nil) // y + XCTAssertEqual(map[3], 2) // o + XCTAssertEqual(map[4], 3) // u + XCTAssertEqual(map[5], nil) // h + XCTAssertEqual(map[6], 4) // a + XCTAssertEqual(map[7], 5) // i + XCTAssertEqual(map[8], 6) // i + XCTAssertEqual(map[9], nil) // t + XCTAssertEqual(map[10], 7) // e + XCTAssertEqual(map[11], 8) // n // アドホックな対処の影響。nの場合はnilであるべき。 + XCTAssertEqual(map[12], nil) // k + XCTAssertEqual(map[13], 9) // i + XCTAssertEqual(map[14], nil) // d + XCTAssertEqual(map[15], 10) // a + } }