|
12 | 12 | :skk-input-mode |
13 | 13 | :skk-preedit |
14 | 14 | :skk-henkan-mode-p |
| 15 | + :skk-henkan-start |
15 | 16 | :skk-henkan-key |
16 | 17 | :skk-okurigana-consonant |
17 | 18 | :skk-okurigana-kana |
18 | 19 | :skk-candidates |
19 | 20 | :skk-candidate-index |
20 | 21 | :clear-skk-state) |
| 22 | + (:import-from :lem-skk-mode/conversion |
| 23 | + :next-candidate |
| 24 | + :prev-candidate) |
21 | 25 | (:import-from :lem-fake-interface |
22 | 26 | :with-fake-interface)) |
23 | 27 | (in-package :lem-skk-mode/tests/main) |
@@ -1173,3 +1177,188 @@ Uppercase starts henkan, Space would trigger conversion (simulated as henkan bou |
1173 | 1177 |
|
1174 | 1178 | ;; Cleanup |
1175 | 1179 | (kill-buffer buf))))) |
| 1180 | + |
| 1181 | +;;; ============================================================ |
| 1182 | +;;; Candidate Cycling Tests |
| 1183 | +;;; ============================================================ |
| 1184 | +;;; These tests verify the fix for the candidate cycling bug where |
| 1185 | +;;; candidates were accumulating instead of being replaced. |
| 1186 | +;;; Bug: Space/x would show "漢字幹事" instead of replacing "漢字" with "幹事" |
| 1187 | + |
| 1188 | +(deftest test-candidate-cycling-replacement |
| 1189 | + (testing "Candidate cycling replaces instead of accumulates" |
| 1190 | + (with-fake-interface () |
| 1191 | + (let ((buf (make-buffer "*skk-candidate-test*" :temporary t))) |
| 1192 | + (switch-to-buffer buf) |
| 1193 | + (erase-buffer buf) |
| 1194 | + |
| 1195 | + ;; Set up SKK state with candidates |
| 1196 | + (let ((state (make-instance 'skk-state))) |
| 1197 | + (setf (buffer-value buf 'lem-skk-mode/state::skk-state) state) |
| 1198 | + |
| 1199 | + ;; Set henkan start point |
| 1200 | + (setf (skk-henkan-start state) |
| 1201 | + (copy-point (buffer-point buf) :left-inserting)) |
| 1202 | + (setf (skk-henkan-mode-p state) t) |
| 1203 | + (setf (skk-henkan-key state) "かんじ") |
| 1204 | + (setf (skk-candidates state) '("漢字" "幹事" "監事")) |
| 1205 | + (setf (skk-candidate-index state) 0) |
| 1206 | + |
| 1207 | + ;; Show first candidate |
| 1208 | + (lem-skk-mode/conversion::show-candidate state 0) |
| 1209 | + (ok (equal (buffer-text buf) "漢字") |
| 1210 | + "First candidate should be '漢字'") |
| 1211 | + |
| 1212 | + ;; Show second candidate - should REPLACE, not accumulate |
| 1213 | + (lem-skk-mode/conversion::show-candidate state 1) |
| 1214 | + (ok (equal (buffer-text buf) "幹事") |
| 1215 | + "Second candidate should replace to '幹事' (not '漢字幹事')") |
| 1216 | + |
| 1217 | + ;; Show third candidate - should REPLACE again |
| 1218 | + (lem-skk-mode/conversion::show-candidate state 2) |
| 1219 | + (ok (equal (buffer-text buf) "監事") |
| 1220 | + "Third candidate should replace to '監事'") |
| 1221 | + |
| 1222 | + ;; Go back to first - should still replace |
| 1223 | + (lem-skk-mode/conversion::show-candidate state 0) |
| 1224 | + (ok (equal (buffer-text buf) "漢字") |
| 1225 | + "Going back to first candidate should replace to '漢字'") |
| 1226 | + |
| 1227 | + ;; Cleanup point |
| 1228 | + (when (skk-henkan-start state) |
| 1229 | + (delete-point (skk-henkan-start state)))) |
| 1230 | + |
| 1231 | + (kill-buffer buf))))) |
| 1232 | + |
| 1233 | +(deftest test-candidate-cycling-with-okurigana |
| 1234 | + (testing "Candidate cycling with okurigana replaces correctly" |
| 1235 | + (with-fake-interface () |
| 1236 | + (let ((buf (make-buffer "*skk-okurigana-test*" :temporary t))) |
| 1237 | + (switch-to-buffer buf) |
| 1238 | + (erase-buffer buf) |
| 1239 | + |
| 1240 | + (let ((state (make-instance 'skk-state))) |
| 1241 | + (setf (buffer-value buf 'lem-skk-mode/state::skk-state) state) |
| 1242 | + |
| 1243 | + ;; Set up okurigana conversion: かk -> 書く, 描く |
| 1244 | + (setf (skk-henkan-start state) |
| 1245 | + (copy-point (buffer-point buf) :left-inserting)) |
| 1246 | + (setf (skk-henkan-mode-p state) t) |
| 1247 | + (setf (skk-henkan-key state) "か") |
| 1248 | + (setf (skk-okurigana-consonant state) "k") |
| 1249 | + (setf (skk-okurigana-kana state) "く") |
| 1250 | + (setf (skk-candidates state) '("書" "描" "掻")) |
| 1251 | + (setf (skk-candidate-index state) 0) |
| 1252 | + |
| 1253 | + ;; Show first candidate with okurigana |
| 1254 | + (lem-skk-mode/conversion::show-candidate state 0) |
| 1255 | + (ok (equal (buffer-text buf) "書く") |
| 1256 | + "First candidate with okurigana: '書く'") |
| 1257 | + |
| 1258 | + ;; Second candidate - should replace entire text |
| 1259 | + (lem-skk-mode/conversion::show-candidate state 1) |
| 1260 | + (ok (equal (buffer-text buf) "描く") |
| 1261 | + "Second candidate should replace to '描く' (not '書く描く')") |
| 1262 | + |
| 1263 | + ;; Third candidate |
| 1264 | + (lem-skk-mode/conversion::show-candidate state 2) |
| 1265 | + (ok (equal (buffer-text buf) "掻く") |
| 1266 | + "Third candidate should replace to '掻く'") |
| 1267 | + |
| 1268 | + (when (skk-henkan-start state) |
| 1269 | + (delete-point (skk-henkan-start state)))) |
| 1270 | + |
| 1271 | + (kill-buffer buf))))) |
| 1272 | + |
| 1273 | +(deftest test-next-prev-candidate-functions |
| 1274 | + (testing "next-candidate and prev-candidate cycle correctly" |
| 1275 | + (with-fake-interface () |
| 1276 | + (let ((buf (make-buffer "*skk-next-prev-test*" :temporary t))) |
| 1277 | + (switch-to-buffer buf) |
| 1278 | + (erase-buffer buf) |
| 1279 | + |
| 1280 | + (let ((state (make-instance 'skk-state))) |
| 1281 | + (setf (buffer-value buf 'lem-skk-mode/state::skk-state) state) |
| 1282 | + |
| 1283 | + ;; Set up state |
| 1284 | + (setf (skk-henkan-start state) |
| 1285 | + (copy-point (buffer-point buf) :left-inserting)) |
| 1286 | + (setf (skk-henkan-mode-p state) t) |
| 1287 | + (setf (skk-henkan-key state) "かんじ") |
| 1288 | + (setf (skk-candidates state) '("漢字" "幹事" "監事")) |
| 1289 | + (setf (skk-candidate-index state) 0) |
| 1290 | + |
| 1291 | + ;; Show initial candidate |
| 1292 | + (lem-skk-mode/conversion::show-candidate state 0) |
| 1293 | + (ok (equal (buffer-text buf) "漢字") "Initial: 漢字") |
| 1294 | + |
| 1295 | + ;; next-candidate |
| 1296 | + (next-candidate state) |
| 1297 | + (ok (equal (buffer-text buf) "幹事") "After next: 幹事") |
| 1298 | + (ok (= (skk-candidate-index state) 1) "Index should be 1") |
| 1299 | + |
| 1300 | + ;; next-candidate again |
| 1301 | + (next-candidate state) |
| 1302 | + (ok (equal (buffer-text buf) "監事") "After next: 監事") |
| 1303 | + (ok (= (skk-candidate-index state) 2) "Index should be 2") |
| 1304 | + |
| 1305 | + ;; next-candidate wraps around |
| 1306 | + (next-candidate state) |
| 1307 | + (ok (equal (buffer-text buf) "漢字") "After wrap: 漢字") |
| 1308 | + (ok (= (skk-candidate-index state) 0) "Index should wrap to 0") |
| 1309 | + |
| 1310 | + ;; prev-candidate |
| 1311 | + (prev-candidate state) |
| 1312 | + (ok (equal (buffer-text buf) "監事") "After prev: 監事") |
| 1313 | + (ok (= (skk-candidate-index state) 2) "Index should wrap to 2") |
| 1314 | + |
| 1315 | + ;; prev-candidate again |
| 1316 | + (prev-candidate state) |
| 1317 | + (ok (equal (buffer-text buf) "幹事") "After prev: 幹事") |
| 1318 | + (ok (= (skk-candidate-index state) 1) "Index should be 1") |
| 1319 | + |
| 1320 | + (when (skk-henkan-start state) |
| 1321 | + (delete-point (skk-henkan-start state)))) |
| 1322 | + |
| 1323 | + (kill-buffer buf))))) |
| 1324 | + |
| 1325 | +(deftest test-candidate-cycling-with-existing-text |
| 1326 | + (testing "Candidate cycling works when buffer has existing text" |
| 1327 | + (with-fake-interface () |
| 1328 | + (let ((buf (make-buffer "*skk-existing-text-test*" :temporary t))) |
| 1329 | + (switch-to-buffer buf) |
| 1330 | + (erase-buffer buf) |
| 1331 | + |
| 1332 | + ;; Insert some existing text first |
| 1333 | + (insert-string (buffer-point buf) "これは") |
| 1334 | + |
| 1335 | + (let ((state (make-instance 'skk-state))) |
| 1336 | + (setf (buffer-value buf 'lem-skk-mode/state::skk-state) state) |
| 1337 | + |
| 1338 | + ;; Set henkan start point at current position (after "これは") |
| 1339 | + (setf (skk-henkan-start state) |
| 1340 | + (copy-point (buffer-point buf) :left-inserting)) |
| 1341 | + (setf (skk-henkan-mode-p state) t) |
| 1342 | + (setf (skk-henkan-key state) "ほん") |
| 1343 | + (setf (skk-candidates state) '("本" "翻" "奔")) |
| 1344 | + (setf (skk-candidate-index state) 0) |
| 1345 | + |
| 1346 | + ;; Show first candidate |
| 1347 | + (lem-skk-mode/conversion::show-candidate state 0) |
| 1348 | + (ok (equal (buffer-text buf) "これは本") |
| 1349 | + "Buffer should have existing text + first candidate") |
| 1350 | + |
| 1351 | + ;; Cycle to second - only candidate part should be replaced |
| 1352 | + (lem-skk-mode/conversion::show-candidate state 1) |
| 1353 | + (ok (equal (buffer-text buf) "これは翻") |
| 1354 | + "Only candidate should be replaced, existing text preserved") |
| 1355 | + |
| 1356 | + ;; Cycle to third |
| 1357 | + (lem-skk-mode/conversion::show-candidate state 2) |
| 1358 | + (ok (equal (buffer-text buf) "これは奔") |
| 1359 | + "Candidate replacement should work correctly") |
| 1360 | + |
| 1361 | + (when (skk-henkan-start state) |
| 1362 | + (delete-point (skk-henkan-start state)))) |
| 1363 | + |
| 1364 | + (kill-buffer buf))))) |
0 commit comments