Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions app/src/main/java/helium314/keyboard/latin/ShortWordSwap.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-3.0-only
package helium314.keyboard.latin

import java.util.Locale

/**
* Exception to LeanType's rule that unknown words of up to 3 letters are not auto-corrected (see [Suggest]):
* two neighboring letters typed in the wrong order, like "dsa" for "das", "ihc" for "ich" or "teh" for "the",
* are corrected. Abbreviations like "db" or "yt" stay, their swapped form is no word.
*/
object ShortWordSwap {
const val LENGTH = 3

/** Whether [word] is [typed] with two neighboring (different) letters swapped, case is ignored */
fun isSwapOfNeighbors(typed: String, word: String): Boolean {
if (typed.length != LENGTH || word.length != LENGTH) return false
val a = typed.lowercase(Locale.ROOT)
val b = word.lowercase(Locale.ROOT)
val i = a.indices.firstOrNull { a[it] != b[it] } ?: return false
if (i + 1 >= a.length) return false
return a[i] == b[i + 1] && a[i + 1] == b[i] && a.substring(i + 2) == b.substring(i + 2)
&& a.all { it.isLetter() }
}
}
4 changes: 3 additions & 1 deletion app/src/main/java/helium314/keyboard/latin/Suggest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,9 @@ class Suggest(private val mDictionaryFacilitator: DictionaryFacilitator) {
val isUserHistory = firstSuggestion.mSourceDict?.mDictType == Dictionary.TYPE_USER_HISTORY
val isWhitelist = firstSuggestion.isKindOf(SuggestedWordInfo.KIND_WHITELIST)
val isShortcut = firstSuggestion.isKindOf(SuggestedWordInfo.KIND_SHORTCUT) && Settings.getValues().mAutoCorrectShortcuts
if (!isExactOrCaseMatch && !isUserHistory && !isWhitelist && !isShortcut && expectedContraction == null) {
// two neighboring letters typed in the wrong order, like "teh" for "the", are a typo, not an abbreviation
val isSwap = ShortWordSwap.isSwapOfNeighbors(consideredWord, firstSuggestion.mWord)
if (!isExactOrCaseMatch && !isUserHistory && !isWhitelist && !isShortcut && !isSwap && expectedContraction == null) {
return true to false
}
}
Expand Down
27 changes: 27 additions & 0 deletions app/src/test/java/helium314/keyboard/latin/ShortWordSwapTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-3.0-only
package helium314.keyboard.latin

import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test

class ShortWordSwapTest {
@Test
fun swappedNeighborsAreFound() {
for ((typed, word) in listOf("dsa" to "das", "ihc" to "ich", "dei" to "die", "nud" to "und", "teh" to "the",
"adn" to "and", "Dsa" to "Das", "IHC" to "ich", "eni" to "ein")) {
assertTrue("$typed -> $word", ShortWordSwap.isSwapOfNeighbors(typed, word))
}
}

@Test
fun otherDifferencesAreNoSwap() {
assertFalse(ShortWordSwap.isSwapOfNeighbors("das", "das")) // same word
assertFalse(ShortWordSwap.isSwapOfNeighbors("dsa", "sad")) // not neighbors
assertFalse(ShortWordSwap.isSwapOfNeighbors("das", "dar")) // a different letter
assertFalse(ShortWordSwap.isSwapOfNeighbors("db", "bd")) // only 3 letters
assertFalse(ShortWordSwap.isSwapOfNeighbors("ders", "dres")) // only 3 letters
assertFalse(ShortWordSwap.isSwapOfNeighbors("a1b", "1ab")) // only letters
assertFalse(ShortWordSwap.isSwapOfNeighbors("abc", "bca"))
}
}
Loading