Compare commits
5 Commits
test/inlin
...
flest
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c1bfb60ba | ||
|
|
76702d6d68 | ||
|
|
28df80a1df | ||
|
|
56a81f86a3 | ||
|
|
ab65aac74a |
@@ -206,7 +206,6 @@ dependencies {
|
||||
implementation(libs.mikepenz.aboutlibraries.compose)
|
||||
implementation(libs.patrickgold.compose.tooltip)
|
||||
implementation(libs.patrickgold.jetpref.datastore.model)
|
||||
ksp(libs.patrickgold.jetpref.datastore.model.processor)
|
||||
implementation(libs.patrickgold.jetpref.datastore.ui)
|
||||
implementation(libs.patrickgold.jetpref.material.ui)
|
||||
|
||||
|
||||
@@ -23,10 +23,8 @@ import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Handler
|
||||
import android.util.Log
|
||||
import androidx.core.os.UserManagerCompat
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.clipboard.ClipboardManager
|
||||
import dev.patrickgold.florisboard.ime.core.SubtypeManager
|
||||
import dev.patrickgold.florisboard.ime.dictionary.DictionaryManager
|
||||
@@ -42,11 +40,7 @@ import dev.patrickgold.florisboard.lib.devtools.Flog
|
||||
import dev.patrickgold.florisboard.lib.devtools.LogTopic
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogError
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
|
||||
import dev.patrickgold.jetpref.datastore.runtime.initAndroid
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import dev.patrickgold.jetpref.datastore.JetPref
|
||||
import org.florisboard.lib.kotlin.io.deleteContentsRecursively
|
||||
import org.florisboard.lib.kotlin.tryOrNull
|
||||
import org.florisboard.libnative.dummyAdd
|
||||
@@ -69,9 +63,8 @@ class FlorisApplication : Application() {
|
||||
}
|
||||
}
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val mainHandler by lazy { Handler(mainLooper) }
|
||||
private val scope = CoroutineScope(Dispatchers.Default)
|
||||
val preferenceStoreLoaded = MutableStateFlow(false)
|
||||
|
||||
val cacheManager = lazy { CacheManager(this) }
|
||||
val clipboardManager = lazy { ClipboardManager(this) }
|
||||
@@ -87,6 +80,7 @@ class FlorisApplication : Application() {
|
||||
super.onCreate()
|
||||
FlorisApplicationReference = WeakReference(this)
|
||||
try {
|
||||
JetPref.configure(saveIntervalMs = 500)
|
||||
Flog.install(
|
||||
context = this,
|
||||
isFloggingEnabled = BuildConfig.DEBUG,
|
||||
@@ -114,14 +108,7 @@ class FlorisApplication : Application() {
|
||||
|
||||
fun init() {
|
||||
cacheDir?.deleteContentsRecursively()
|
||||
scope.launch {
|
||||
val result = FlorisPreferenceStore.initAndroid(
|
||||
context = this@FlorisApplication,
|
||||
datastoreName = FlorisPreferenceModel.NAME,
|
||||
)
|
||||
Log.i("PREFS", result.toString())
|
||||
preferenceStoreLoaded.value = true
|
||||
}
|
||||
prefs.initializeBlocking(this)
|
||||
extensionManager.value.init()
|
||||
clipboardManager.value.initializeForContext(this)
|
||||
DictionaryManager.init(this)
|
||||
|
||||
@@ -75,8 +75,8 @@ import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import dev.patrickgold.florisboard.app.FlorisAppActivity
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.devtools.DevtoolsOverlay
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.ImeUiMode
|
||||
import dev.patrickgold.florisboard.ime.clipboard.ClipboardInputLayout
|
||||
import dev.patrickgold.florisboard.ime.core.SelectSubtypePanel
|
||||
@@ -119,9 +119,8 @@ import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.isOrientationLandscape
|
||||
import org.florisboard.lib.android.isOrientationPortrait
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
import org.florisboard.lib.android.systemServiceOrNull
|
||||
import org.florisboard.lib.kotlin.collectIn
|
||||
import org.florisboard.lib.kotlin.collectLatestIn
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggButton
|
||||
import org.florisboard.lib.snygg.ui.SnyggRow
|
||||
@@ -247,12 +246,12 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
}
|
||||
}
|
||||
}
|
||||
ims.showShortToastSync("Failed to find voice IME, do you have one installed?")
|
||||
ims.showShortToast("Failed to find voice IME, do you have one installed?")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val editorInstance by editorInstance()
|
||||
private val keyboardManager by keyboardManager()
|
||||
private val nlpManager by nlpManager()
|
||||
@@ -278,21 +277,21 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
super.onCreate()
|
||||
FlorisImeServiceReference = WeakReference(this)
|
||||
WindowCompat.setDecorFitsSystemWindows(window.window!!, false)
|
||||
subtypeManager.activeSubtypeFlow.collectIn(lifecycleScope) { subtype ->
|
||||
subtypeManager.activeSubtypeFlow.collectLatestIn(lifecycleScope) { subtype ->
|
||||
val config = Configuration(resources.configuration)
|
||||
if (prefs.localization.displayKeyboardLabelsInSubtypeLanguage.get()) {
|
||||
config.setLocale(subtype.primaryLocale.base)
|
||||
}
|
||||
resourcesContext = createConfigurationContext(config)
|
||||
}
|
||||
prefs.localization.displayKeyboardLabelsInSubtypeLanguage.asFlow().collectIn(lifecycleScope) { shouldSync ->
|
||||
prefs.localization.displayKeyboardLabelsInSubtypeLanguage.observeForever { shouldSync ->
|
||||
val config = Configuration(resources.configuration)
|
||||
if (shouldSync) {
|
||||
config.setLocale(subtypeManager.activeSubtype.primaryLocale.base)
|
||||
}
|
||||
resourcesContext = createConfigurationContext(config)
|
||||
}
|
||||
prefs.physicalKeyboard.showOnScreenKeyboard.asFlow().collectIn(lifecycleScope) {
|
||||
prefs.physicalKeyboard.showOnScreenKeyboard.observeForever {
|
||||
updateInputViewShown()
|
||||
}
|
||||
@Suppress("DEPRECATION") // We do not retrieve the wallpaper but only listen to changes
|
||||
@@ -409,6 +408,7 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
flogInfo(LogTopic.IMS_EVENTS)
|
||||
}
|
||||
isWindowShown = true
|
||||
themeManager.updateActiveTheme()
|
||||
inputFeedbackController.updateSystemPrefsState()
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import android.service.textservice.SpellCheckerService
|
||||
import android.view.textservice.SentenceSuggestionsInfo
|
||||
import android.view.textservice.SuggestionsInfo
|
||||
import android.view.textservice.TextInfo
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.dictionary.DictionaryManager
|
||||
import dev.patrickgold.florisboard.ime.nlp.SpellingLanguageMode
|
||||
@@ -33,7 +33,7 @@ import kotlinx.coroutines.runBlocking
|
||||
import org.florisboard.lib.kotlin.map
|
||||
|
||||
class FlorisSpellCheckerService : SpellCheckerService() {
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val dictionaryManager get() = DictionaryManager.default()
|
||||
private val nlpManager by nlpManager()
|
||||
private val subtypeManager by subtypeManager()
|
||||
|
||||
@@ -57,8 +57,7 @@ import dev.patrickgold.florisboard.lib.compose.ColorPreferenceSerializer
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
|
||||
import dev.patrickgold.florisboard.lib.observeAsTransformingState
|
||||
import dev.patrickgold.florisboard.lib.util.VersionName
|
||||
import dev.patrickgold.jetpref.datastore.annotations.Preferences
|
||||
import dev.patrickgold.jetpref.datastore.jetprefDataStoreOf
|
||||
import dev.patrickgold.jetpref.datastore.JetPref
|
||||
import dev.patrickgold.jetpref.datastore.model.LocalTime
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceData
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceMigrationEntry
|
||||
@@ -70,14 +69,9 @@ import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.isOrientationPortrait
|
||||
import org.florisboard.lib.color.DEFAULT_GREEN
|
||||
|
||||
val FlorisPreferenceStore = jetprefDataStoreOf(FlorisPreferenceModel::class)
|
||||
|
||||
@Preferences
|
||||
abstract class FlorisPreferenceModel : PreferenceModel() {
|
||||
companion object {
|
||||
const val NAME = "florisboard-app-prefs"
|
||||
}
|
||||
fun florisPreferenceModel() = JetPref.getOrCreatePreferenceModel(AppPrefs::class, ::AppPrefs)
|
||||
|
||||
class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
val clipboard = Clipboard()
|
||||
inner class Clipboard {
|
||||
val useInternalClipboard = boolean(
|
||||
@@ -764,11 +758,11 @@ abstract class FlorisPreferenceModel : PreferenceModel() {
|
||||
},
|
||||
serializer = ColorPreferenceSerializer,
|
||||
)
|
||||
val sunriseTime = localTime(
|
||||
val sunriseTime = time(
|
||||
key = "theme__sunrise_time",
|
||||
default = LocalTime(6, 0),
|
||||
)
|
||||
val sunsetTime = localTime(
|
||||
val sunsetTime = time(
|
||||
key = "theme__sunset_time",
|
||||
default = LocalTime(18, 0),
|
||||
)
|
||||
|
||||
@@ -39,14 +39,12 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.apptheme.FlorisAppTheme
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionImportScreenType
|
||||
import dev.patrickgold.florisboard.app.setup.NotificationPermissionState
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.cacheManager
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
import dev.patrickgold.florisboard.lib.compose.LocalPreviewFieldController
|
||||
@@ -61,8 +59,6 @@ import dev.patrickgold.jetpref.datastore.ui.ProvideDefaultDialogPrefStrings
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.hideAppIcon
|
||||
import org.florisboard.lib.android.showAppIcon
|
||||
import org.florisboard.lib.kotlin.collectIn
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
enum class AppTheme(val id: String) {
|
||||
AUTO("auto"),
|
||||
@@ -77,8 +73,7 @@ val LocalNavController = staticCompositionLocalOf<NavController> {
|
||||
}
|
||||
|
||||
class FlorisAppActivity : ComponentActivity() {
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val appContext by appContext()
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val cacheManager by cacheManager()
|
||||
private var appTheme by mutableStateOf(AppTheme.AUTO)
|
||||
private var showAppIcon = true
|
||||
@@ -88,37 +83,37 @@ class FlorisAppActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// Splash screen should be installed before calling super.onCreate()
|
||||
installSplashScreen().apply {
|
||||
setKeepOnScreenCondition { !appContext.preferenceStoreLoaded.value }
|
||||
setKeepOnScreenCondition { !prefs.datastoreReadyStatus.get() }
|
||||
}
|
||||
super.onCreate(savedInstanceState)
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
prefs.other.settingsTheme.asFlow().collectIn(lifecycleScope) {
|
||||
prefs.other.settingsTheme.observe(this) {
|
||||
appTheme = it
|
||||
}
|
||||
prefs.other.settingsLanguage.asFlow().collectIn(lifecycleScope) {
|
||||
prefs.other.settingsLanguage.observe(this) {
|
||||
val config = Configuration(resources.configuration)
|
||||
val locale = if (it == "auto") FlorisLocale.default() else FlorisLocale.fromTag(it)
|
||||
config.setLocale(locale.base)
|
||||
resourcesContext = createConfigurationContext(config)
|
||||
}
|
||||
if (AndroidVersion.ATMOST_API28_P) {
|
||||
prefs.other.showAppIcon.asFlow().collectIn(lifecycleScope) {
|
||||
prefs.other.showAppIcon.observe(this) {
|
||||
showAppIcon = it
|
||||
}
|
||||
}
|
||||
|
||||
//Check if android 13+ is running and the NotificationPermission is not set
|
||||
if (AndroidVersion.ATLEAST_API33_T &&
|
||||
prefs.internal.notificationPermissionState.get() == NotificationPermissionState.NOT_SET
|
||||
) {
|
||||
// update pref value to show the setup screen again again
|
||||
prefs.internal.isImeSetUp.set(false)
|
||||
}
|
||||
|
||||
// We defer the setContent call until the datastore model is loaded, until then the splash screen stays drawn
|
||||
val isModelLoaded = AtomicBoolean(false)
|
||||
appContext.preferenceStoreLoaded.collectIn(lifecycleScope) { loaded ->
|
||||
if (!loaded || isModelLoaded.getAndSet(true)) return@collectIn
|
||||
// Check if android 13+ is running and the NotificationPermission is not set
|
||||
if (AndroidVersion.ATLEAST_API33_T &&
|
||||
prefs.internal.notificationPermissionState.get() == NotificationPermissionState.NOT_SET
|
||||
) {
|
||||
// update pref value to show the setup screen again
|
||||
prefs.internal.isImeSetUp.set(false)
|
||||
}
|
||||
prefs.datastoreReadyStatus.observe(this) { isModelLoaded ->
|
||||
if (!isModelLoaded) return@observe
|
||||
AppVersionUtils.updateVersionOnInstallAndLastUse(this, prefs)
|
||||
setContent {
|
||||
ProvideLocalizedResources(resourcesContext) {
|
||||
|
||||
@@ -29,8 +29,9 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowCompat
|
||||
import dev.patrickgold.florisboard.app.AppTheme
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.color.ColorMappings
|
||||
|
||||
/*private val AmoledDarkColorPalette = darkColorScheme(
|
||||
@@ -78,7 +79,7 @@ fun getColorScheme(
|
||||
context: Context,
|
||||
theme: AppTheme,
|
||||
): ColorScheme {
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val accentColor by prefs.other.accentColor.observeAsState()
|
||||
val isDark = isSystemInDarkTheme()
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import java.util.Locale
|
||||
@@ -67,9 +66,9 @@ fun AndroidLocalesScreen() = FlorisScreen {
|
||||
out.appendLine()
|
||||
}
|
||||
}
|
||||
context.showLongToastSync("Exported available system locales to \"${txtFile.path}\"")
|
||||
context.showLongToast("Exported available system locales to \"${txtFile.path}\"")
|
||||
} catch (e: Exception) {
|
||||
context.showLongToastSync(
|
||||
context.showLongToast(
|
||||
R.string.error__snackbar_message_template,
|
||||
"error_message" to e.message.toString(),
|
||||
)
|
||||
|
||||
@@ -29,6 +29,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -39,7 +40,7 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
import dev.patrickgold.florisboard.ime.keyboard.CachedLayout
|
||||
@@ -63,7 +64,7 @@ private val DateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss", FlorisLocale.
|
||||
@Composable
|
||||
fun DevtoolsOverlay(modifier: Modifier = Modifier) {
|
||||
val context = LocalContext.current
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val keyboardManager by context.keyboardManager()
|
||||
val themeManager by context.themeManager()
|
||||
|
||||
@@ -74,7 +75,7 @@ fun DevtoolsOverlay(modifier: Modifier = Modifier) {
|
||||
val showInlineAutofillOverlay by prefs.devtools.showInlineAutofillOverlay.observeAsState()
|
||||
|
||||
val debugLayoutResult by keyboardManager.layoutManager.debugLayoutComputationResultFlow.collectAsState()
|
||||
val themeInfo by themeManager.activeThemeInfo.collectAsState()
|
||||
val themeInfo by themeManager.activeThemeInfo.observeAsState()
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides Color.White,
|
||||
@@ -96,7 +97,7 @@ fun DevtoolsOverlay(modifier: Modifier = Modifier) {
|
||||
if (devtoolsEnabled && showInlineAutofillOverlay && AndroidVersion.ATLEAST_API30_R) {
|
||||
DevtoolsInlineAutofillOverlay()
|
||||
}
|
||||
val loadFailure = themeInfo.loadFailure
|
||||
val loadFailure = themeInfo?.loadFailure
|
||||
if (loadFailure != null) {
|
||||
DevtoolsStylesheetFailedToLoadOverlay(loadFailure)
|
||||
}
|
||||
@@ -162,13 +163,13 @@ private fun DevtoolsLastLayoutComputationOverlay(debugLayoutResult: DebugLayoutC
|
||||
return@DevtoolsOverlayBox
|
||||
}
|
||||
DevtoolsSubGroup(title = "main") {
|
||||
PrintResult(debugLayoutResult.main)
|
||||
PrintResult(debugLayoutResult!!.main)
|
||||
}
|
||||
DevtoolsSubGroup(title = "mod") {
|
||||
PrintResult(debugLayoutResult.mod)
|
||||
PrintResult(debugLayoutResult!!.mod)
|
||||
}
|
||||
DevtoolsSubGroup(title = "ext") {
|
||||
PrintResult(debugLayoutResult.ext)
|
||||
PrintResult(debugLayoutResult!!.ext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
@@ -39,9 +38,7 @@ import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
|
||||
class DebugOnPurposeCrashException : Exception(
|
||||
"Success! The app crashed purposely to display this beautiful screen we all love :)"
|
||||
@@ -55,7 +52,6 @@ fun DevtoolsScreen() = FlorisScreen {
|
||||
val context = LocalContext.current
|
||||
val navController = LocalNavController.current
|
||||
val extensionManager by context.extensionManager()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val (showDialog, setShowDialog) = remember { mutableStateOf(false) }
|
||||
|
||||
@@ -114,17 +110,15 @@ fun DevtoolsScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.devtools__reset_quick_actions_to_default__label),
|
||||
summary = stringRes(R.string.devtools__reset_quick_actions_to_default__summary),
|
||||
onClick = {
|
||||
scope.launch {
|
||||
prefs.smartbar.actionArrangement.set(QuickActionArrangement.Default)
|
||||
}
|
||||
context.showLongToastSync(R.string.devtools__reset_quick_actions_to_default__toast_success)
|
||||
prefs.smartbar.actionArrangement.set(QuickActionArrangement.Default)
|
||||
context.showLongToast(R.string.devtools__reset_quick_actions_to_default__toast_success)
|
||||
},
|
||||
enabledIf = { prefs.devtools.enabled isEqualTo true },
|
||||
)
|
||||
Preference(
|
||||
title = stringRes(R.string.devtools__reset_flag__label, "flag_name" to "isImeSetUp"),
|
||||
summary = stringRes(R.string.devtools__reset_flag_is_ime_set_up__summary),
|
||||
onClick = { scope.launch { prefs.internal.isImeSetUp.set(false) } },
|
||||
onClick = { prefs.internal.isImeSetUp.set(false) },
|
||||
enabledIf = { prefs.devtools.enabled isEqualTo true },
|
||||
)
|
||||
Preference(
|
||||
@@ -206,14 +200,14 @@ fun DevtoolsScreen() = FlorisScreen {
|
||||
title = "keyboardExtensions",
|
||||
summary = extensionManager.keyboardExtensions.internalModuleDir.absolutePath,
|
||||
onClick = {
|
||||
context.showLongToastSync(extensionManager.keyboardExtensions.internalModuleDir.absolutePath)
|
||||
context.showLongToast(extensionManager.keyboardExtensions.internalModuleDir.absolutePath)
|
||||
},
|
||||
)
|
||||
Preference(
|
||||
title = "themes",
|
||||
summary = extensionManager.themes.internalModuleDir.absolutePath,
|
||||
onClick = {
|
||||
context.showLongToastSync(extensionManager.themes.internalModuleDir.absolutePath)
|
||||
context.showLongToast(extensionManager.themes.internalModuleDir.absolutePath)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.sp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisButton
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
@@ -47,7 +47,6 @@ import dev.patrickgold.florisboard.lib.compose.florisScrollbar
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.devtools.Devtools
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
|
||||
// TODO: This screen is just a quick thrown-together thing and needs further enhancing in the UI
|
||||
@Composable
|
||||
@@ -55,7 +54,7 @@ fun ExportDebugLogScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.devtools__debuglog__title)
|
||||
scrollable = false
|
||||
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val context = LocalContext.current
|
||||
val clipboardManager by context.clipboardManager()
|
||||
|
||||
@@ -75,7 +74,7 @@ fun ExportDebugLogScreen() = FlorisScreen {
|
||||
FlorisButton(
|
||||
onClick = {
|
||||
clipboardManager.addNewPlaintext(debugLog!!.joinToString("\n"))
|
||||
context.showShortToastSync(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
|
||||
context.showShortToast(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
|
||||
},
|
||||
modifier = Modifier,
|
||||
text = stringRes(R.string.devtools__debuglog__copy_log),
|
||||
@@ -84,7 +83,7 @@ fun ExportDebugLogScreen() = FlorisScreen {
|
||||
FlorisButton(
|
||||
onClick = {
|
||||
clipboardManager.addNewPlaintext(formattedDebugLog!!.joinToString("\n"))
|
||||
context.showShortToastSync(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
|
||||
context.showShortToast(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
|
||||
},
|
||||
text = stringRes(R.string.devtools__debuglog__copy_for_github),
|
||||
enabled = debugLog != null,
|
||||
|
||||
@@ -59,9 +59,7 @@ import java.util.*
|
||||
import org.florisboard.lib.android.query
|
||||
import org.florisboard.lib.android.readToFile
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
import org.florisboard.lib.kotlin.io.parentDir
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
@@ -188,9 +186,9 @@ fun ExtensionEditFilesScreen(workspace: CacheManager.ExtEditorWorkspace<*>) = Fl
|
||||
allowOutsideDismissal = true,
|
||||
onNeutral = {
|
||||
if (file.delete()) {
|
||||
context.showShortToastSync("Successfully deleted")
|
||||
context.showShortToast("Successfully deleted")
|
||||
} else {
|
||||
context.showShortToastSync("Failed to delete")
|
||||
context.showShortToast("Failed to delete")
|
||||
}
|
||||
dialogFile = null
|
||||
version++
|
||||
@@ -198,18 +196,18 @@ fun ExtensionEditFilesScreen(workspace: CacheManager.ExtEditorWorkspace<*>) = Fl
|
||||
onConfirm = {
|
||||
val newFile = file.parentFile!!.subFile(fileNameInput).canonicalFile
|
||||
if (newFile.parentFile != file.canonicalFile.parentFile) {
|
||||
context.showLongToastSync("Invalid file name!")
|
||||
context.showLongToast("Invalid file name!")
|
||||
return@JetPrefAlertDialog
|
||||
}
|
||||
if (newFile.exists()) {
|
||||
context.showShortToastSync("Filename already exists.")
|
||||
context.showShortToast("Filename already exists.")
|
||||
return@JetPrefAlertDialog
|
||||
}
|
||||
val success = file.renameTo(newFile)
|
||||
if (success) {
|
||||
context.showShortToastSync("Successfully renamed")
|
||||
context.showShortToast("Successfully renamed")
|
||||
} else {
|
||||
context.showShortToastSync("Failed to rename the file.")
|
||||
context.showShortToast("Failed to rename the file.")
|
||||
}
|
||||
dialogFile = null
|
||||
version++
|
||||
@@ -259,13 +257,13 @@ fun ExtensionEditFilesScreen(workspace: CacheManager.ExtEditorWorkspace<*>) = Fl
|
||||
dir.mkdirs()
|
||||
val file = dir.subFile(fileName)
|
||||
if (file.parentDir != workspace.extDir.subDir(dest)) {
|
||||
context.showShortToastSync("Invalid file name")
|
||||
context.showShortToast("Invalid file name")
|
||||
} else if (file.exists()) {
|
||||
context.showShortToastSync("File already exists")
|
||||
context.showShortToast("File already exists")
|
||||
} else {
|
||||
val tempFile = result.first
|
||||
if (!tempFile.renameTo(file)) {
|
||||
context.showShortToastSync("Failed to rename file")
|
||||
context.showShortToast("Failed to rename file")
|
||||
tempFile.delete()
|
||||
}
|
||||
currentImportDest = null
|
||||
|
||||
@@ -90,7 +90,6 @@ import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefTextField
|
||||
import java.util.*
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.kotlin.io.deleteContentsRecursively
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
@@ -288,7 +287,7 @@ private fun EditScreen(
|
||||
stylesheetFile.writeText(stylesheet)
|
||||
}.onFailure {
|
||||
// TODO: better error handling
|
||||
context.showLongToastSync(it.message.toString())
|
||||
context.showLongToast(it.message.toString())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
@@ -666,7 +665,7 @@ private fun <T : ExtensionComponent> CreateComponentScreen(
|
||||
when (createFrom) {
|
||||
CreateFrom.EMPTY -> {
|
||||
if (editor.themes.any { it.id == newId.trim() }) {
|
||||
context.showLongToastSync("A theme with this ID already exists!")
|
||||
context.showLongToast("A theme with this ID already exists!")
|
||||
} else {
|
||||
val componentEditor = ThemeExtensionComponentEditor(
|
||||
id = newId.trim(),
|
||||
|
||||
@@ -27,7 +27,6 @@ import org.florisboard.lib.android.showLongToast
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.ext.Extension
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionDefaults
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
|
||||
@Composable
|
||||
fun ExtensionExportScreen(id: String) {
|
||||
@@ -62,9 +61,9 @@ private fun ExportScreen(ext: Extension) = FlorisScreen {
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
runCatching { extensionManager.export(ext, uri) }.onSuccess {
|
||||
context.showLongToastSync(R.string.ext__export__success)
|
||||
context.showLongToast(R.string.ext__export__success)
|
||||
}.onFailure { error ->
|
||||
context.showLongToastSync(R.string.ext__export__failure, "error_message" to error.localizedMessage)
|
||||
context.showLongToast(R.string.ext__export__failure, "error_message" to error.localizedMessage)
|
||||
}
|
||||
navController.popBackStack()
|
||||
},
|
||||
|
||||
@@ -61,7 +61,6 @@ import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.io.FileRegistry
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.kotlin.resultOk
|
||||
|
||||
enum class ExtensionImportScreenType(
|
||||
@@ -189,10 +188,10 @@ fun ExtensionImportScreen(type: ExtensionImportScreenType, initUuid: String?) =
|
||||
}
|
||||
}.onSuccess {
|
||||
workspace.close()
|
||||
context.showLongToastSync(R.string.ext__import__success)
|
||||
context.showLongToast(R.string.ext__import__success)
|
||||
navController.popBackStack()
|
||||
}.onFailure { error ->
|
||||
context.showLongToastSync(R.string.ext__import__failure, "error_message" to error.localizedMessage)
|
||||
context.showLongToast(R.string.ext__import__failure, "error_message" to error.localizedMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,6 @@ import dev.patrickgold.florisboard.lib.ext.Extension
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionMaintainer
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionMeta
|
||||
import dev.patrickgold.florisboard.lib.io.FlorisRef
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
|
||||
@Composable
|
||||
fun ExtensionViewScreen(id: String) {
|
||||
@@ -203,7 +202,7 @@ private fun ViewScreen(ext: Extension) = FlorisScreen {
|
||||
}.onSuccess {
|
||||
navController.popBackStack()
|
||||
}.onFailure { error ->
|
||||
context.showLongToastSync(
|
||||
context.showLongToast(
|
||||
R.string.error__snackbar_message,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
|
||||
@@ -32,7 +32,6 @@ import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -42,8 +41,6 @@ import androidx.core.app.ShareCompat
|
||||
import androidx.core.content.FileProvider
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.cacheManager
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
@@ -60,14 +57,11 @@ import dev.patrickgold.florisboard.lib.devtools.flogError
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
|
||||
import dev.patrickgold.florisboard.lib.io.FileRegistry
|
||||
import dev.patrickgold.florisboard.lib.io.ZipUtils
|
||||
import dev.patrickgold.jetpref.datastore.runtime.AndroidAppDataStorage
|
||||
import dev.patrickgold.jetpref.datastore.runtime.FileBasedStorage
|
||||
import dev.patrickgold.jetpref.datastore.jetprefDatastoreDir
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.android.writeFromFile
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
@@ -142,7 +136,6 @@ fun BackupScreen() = FlorisScreen {
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val cacheManager by context.cacheManager()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
var backupDestination by remember { mutableStateOf(Backup.Destination.FILE_SYS) }
|
||||
val backupFilesSelector = remember { Backup.FilesSelector() }
|
||||
@@ -162,24 +155,22 @@ fun BackupScreen() = FlorisScreen {
|
||||
context.contentResolver.writeFromFile(uri, backupWorkspace!!.zipFile)
|
||||
backupWorkspace!!.close()
|
||||
}.onSuccess {
|
||||
context.showLongToastSync(R.string.backup_and_restore__back_up__success)
|
||||
context.showLongToast(R.string.backup_and_restore__back_up__success)
|
||||
navController.popBackStack()
|
||||
}.onFailure { error ->
|
||||
flogError { error.stackTraceToString() }
|
||||
context.showLongToastSync(R.string.backup_and_restore__back_up__failure, "error_message" to error.message)
|
||||
context.showLongToast(R.string.backup_and_restore__back_up__failure, "error_message" to error.message)
|
||||
backupWorkspace = null
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
suspend fun prepareBackupWorkspace() {
|
||||
fun prepareBackupWorkspace() {
|
||||
val workspace = cacheManager.backupAndRestore.new()
|
||||
if (backupFilesSelector.jetprefDatastore) {
|
||||
val fileBasedStorage = workspace.inputDir
|
||||
.subDir(AndroidAppDataStorage.JETPREF_DIR_NAME)
|
||||
.subFile("${FlorisPreferenceModel.NAME}.${AndroidAppDataStorage.JETPREF_FILE_EXT}")
|
||||
.let { FileBasedStorage(it.path) }
|
||||
FlorisPreferenceStore.export(fileBasedStorage).getOrThrow()
|
||||
context.jetprefDatastoreDir.let { dir ->
|
||||
dir.copyRecursively(workspace.inputDir.subDir(dir.name))
|
||||
}
|
||||
}
|
||||
val workspaceFilesDir = workspace.inputDir.subDir("files")
|
||||
if (backupFilesSelector.imeKeyboard) {
|
||||
@@ -234,7 +225,7 @@ fun BackupScreen() = FlorisScreen {
|
||||
backupWorkspace = workspace
|
||||
}
|
||||
|
||||
suspend fun prepareAndPerformBackup() {
|
||||
fun prepareAndPerformBackup() {
|
||||
runCatching {
|
||||
if (backupWorkspace == null || backupWorkspace!!.isClosed()) {
|
||||
prepareBackupWorkspace()
|
||||
@@ -274,7 +265,7 @@ fun BackupScreen() = FlorisScreen {
|
||||
)
|
||||
ButtonBarButton(
|
||||
onClick = {
|
||||
scope.launch { prepareAndPerformBackup() }
|
||||
prepareAndPerformBackup()
|
||||
},
|
||||
text = stringRes(R.string.action__back_up),
|
||||
enabled = backupFilesSelector.atLeastOneSelected(),
|
||||
|
||||
@@ -43,9 +43,8 @@ import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.cacheManager
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardFileStorage
|
||||
@@ -61,16 +60,13 @@ import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
|
||||
import dev.patrickgold.florisboard.lib.io.ZipUtils
|
||||
import dev.patrickgold.jetpref.datastore.runtime.AndroidAppDataStorage
|
||||
import dev.patrickgold.jetpref.datastore.runtime.FileBasedStorage
|
||||
import dev.patrickgold.jetpref.datastore.runtime.ImportStrategy
|
||||
import dev.patrickgold.jetpref.datastore.JetPref
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.readToFile
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.kotlin.io.deleteContentsRecursively
|
||||
import org.florisboard.lib.kotlin.io.readJson
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
@@ -83,6 +79,11 @@ object Restore {
|
||||
const val MIN_VERSION_CODE = 64
|
||||
const val PACKAGE_NAME = "dev.patrickgold.florisboard"
|
||||
const val BACKUP_ARCHIVE_FILE_NAME = "backup.zip"
|
||||
|
||||
enum class Mode {
|
||||
MERGE,
|
||||
ERASE_AND_OVERWRITE;
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -90,12 +91,13 @@ fun RestoreScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.backup_and_restore__restore__title)
|
||||
previewFieldVisible = false
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val cacheManager by context.cacheManager()
|
||||
|
||||
val restoreFilesSelector = remember { Backup.FilesSelector() }
|
||||
var importStrategy by remember { mutableStateOf(ImportStrategy.Merge) }
|
||||
var restoreMode by remember { mutableStateOf(Restore.Mode.MERGE) }
|
||||
// TODO: rememberCoroutineScope() is unusable because it provides the scope in a cancelled state, which does
|
||||
// not make sense at all. I suspect that this is a bug and once it is resolved we can use it here again.
|
||||
val restoreScope = remember { CoroutineScope(Dispatchers.Main) }
|
||||
@@ -136,7 +138,7 @@ fun RestoreScreen() = FlorisScreen {
|
||||
}
|
||||
restoreWorkspace = workspace
|
||||
}.onFailure { error ->
|
||||
context.showLongToastSync(
|
||||
context.showLongToast(
|
||||
R.string.backup_and_restore__restore__failure,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
@@ -146,13 +148,15 @@ fun RestoreScreen() = FlorisScreen {
|
||||
|
||||
suspend fun performRestore() {
|
||||
val workspace = restoreWorkspace!!
|
||||
val shouldReset = importStrategy == ImportStrategy.Erase
|
||||
val shouldReset = restoreMode == Restore.Mode.ERASE_AND_OVERWRITE
|
||||
if (restoreFilesSelector.jetprefDatastore) {
|
||||
val fileBasedStorage = workspace.outputDir
|
||||
.subDir(AndroidAppDataStorage.JETPREF_DIR_NAME)
|
||||
.subFile("${FlorisPreferenceModel.NAME}.${AndroidAppDataStorage.JETPREF_FILE_EXT}")
|
||||
.let { FileBasedStorage(it.path) }
|
||||
FlorisPreferenceStore.import(importStrategy, fileBasedStorage).getOrThrow()
|
||||
val datastoreFile = workspace.outputDir
|
||||
.subDir(JetPref.JETPREF_DIR_NAME)
|
||||
.subFile("${prefs.name}.${JetPref.JETPREF_FILE_EXT}")
|
||||
if (datastoreFile.exists()) {
|
||||
prefs.datastorePersistenceHandler?.loadPrefs(datastoreFile, shouldReset)
|
||||
prefs.datastorePersistenceHandler?.persistPrefs()
|
||||
}
|
||||
}
|
||||
val workspaceFilesDir = workspace.outputDir.subDir("files")
|
||||
if (restoreFilesSelector.imeKeyboard) {
|
||||
@@ -271,16 +275,16 @@ fun RestoreScreen() = FlorisScreen {
|
||||
) {
|
||||
RadioListItem(
|
||||
onClick = {
|
||||
importStrategy = ImportStrategy.Merge
|
||||
restoreMode = Restore.Mode.MERGE
|
||||
},
|
||||
selected = importStrategy == ImportStrategy.Merge,
|
||||
selected = restoreMode == Restore.Mode.MERGE,
|
||||
text = stringRes(R.string.backup_and_restore__restore__mode_merge),
|
||||
)
|
||||
RadioListItem(
|
||||
onClick = {
|
||||
importStrategy = ImportStrategy.Erase
|
||||
restoreMode = Restore.Mode.ERASE_AND_OVERWRITE
|
||||
},
|
||||
selected = importStrategy == ImportStrategy.Erase,
|
||||
selected = restoreMode == Restore.Mode.ERASE_AND_OVERWRITE,
|
||||
text = stringRes(R.string.backup_and_restore__restore__mode_erase_and_overwrite),
|
||||
)
|
||||
}
|
||||
@@ -289,7 +293,7 @@ fun RestoreScreen() = FlorisScreen {
|
||||
runCatching {
|
||||
restoreDataFromFileSystemLauncher.launch("*/*")
|
||||
}.onFailure { error ->
|
||||
context.showLongToastSync(
|
||||
context.showLongToast(
|
||||
R.string.backup_and_restore__restore__failure,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
|
||||
@@ -68,7 +68,6 @@ import dev.patrickgold.jetpref.material.ui.JetPrefTextField
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.android.stringRes
|
||||
|
||||
private val AllLanguagesLocale = FlorisLocale.from(language = "zz")
|
||||
@@ -144,16 +143,16 @@ fun UserDictionaryScreen(type: UserDictionaryType) = FlorisScreen {
|
||||
UserDictionaryType.SYSTEM -> dictionaryManager.systemUserDictionaryDatabase()
|
||||
}
|
||||
if (db == null) {
|
||||
context.showLongToastSync("Database handle is null, failed to import")
|
||||
context.showLongToast("Database handle is null, failed to import")
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
runCatching {
|
||||
db.importCombinedList(context, uri)
|
||||
}.onSuccess {
|
||||
buildUi()
|
||||
context.showLongToastSync(R.string.settings__udm__dictionary_import_success)
|
||||
context.showLongToast(R.string.settings__udm__dictionary_import_success)
|
||||
}.onFailure { error ->
|
||||
context.showLongToastSync("Error: ${error.localizedMessage}")
|
||||
context.showLongToast("Error: ${error.localizedMessage}")
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -169,15 +168,15 @@ fun UserDictionaryScreen(type: UserDictionaryType) = FlorisScreen {
|
||||
UserDictionaryType.SYSTEM -> dictionaryManager.systemUserDictionaryDatabase()
|
||||
}
|
||||
if (db == null) {
|
||||
context.showLongToastSync("Database handle is null, failed to export")
|
||||
context.showLongToast("Database handle is null, failed to export")
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
runCatching {
|
||||
db.exportCombinedList(context, uri)
|
||||
}.onSuccess {
|
||||
context.showLongToastSync(R.string.settings__udm__dictionary_export_success)
|
||||
context.showLongToast(R.string.settings__udm__dictionary_export_success)
|
||||
}.onFailure { error ->
|
||||
context.showLongToastSync("Error: ${error.localizedMessage}")
|
||||
context.showLongToast("Error: ${error.localizedMessage}")
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -41,10 +41,10 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionImportScreenType
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.ime.nlp.LanguagePackComponent
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
@@ -61,7 +61,6 @@ import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
|
||||
enum class LanguagePackManagerScreenAction(val id: String) {
|
||||
MANAGE("manage-installed-language-packs");
|
||||
@@ -76,7 +75,7 @@ fun LanguagePackManagerScreen(action: LanguagePackManagerScreenAction?) = Floris
|
||||
else -> error("LanguagePack manager screen action must not be null")
|
||||
})
|
||||
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val extensionManager by context.extensionManager()
|
||||
@@ -200,7 +199,7 @@ fun LanguagePackManagerScreen(action: LanguagePackManagerScreenAction?) = Floris
|
||||
runCatching {
|
||||
extensionManager.delete(languagePackExtToDelete!!)
|
||||
}.onFailure { error ->
|
||||
context.showLongToastSync(
|
||||
context.showLongToast(
|
||||
R.string.error__snackbar_message,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
|
||||
@@ -44,8 +44,8 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
@@ -61,7 +61,7 @@ fun SelectLocaleScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.settings__localization__subtype_select_locale)
|
||||
scrollable = false
|
||||
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val navController = LocalNavController.current
|
||||
|
||||
val displayLanguageNamesIn by prefs.localization.displayLanguageNamesIn.observeAsState()
|
||||
|
||||
@@ -58,9 +58,9 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.core.SubtypeJsonConfig
|
||||
@@ -86,6 +86,7 @@ import dev.patrickgold.florisboard.subtypeManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import kotlinx.serialization.encodeToString
|
||||
|
||||
private val SelectComponentName = ExtensionComponentName("00", "00")
|
||||
private val SelectNlpProviderId = SelectComponentName.toString()
|
||||
@@ -185,7 +186,7 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
val selectValue = stringRes(R.string.settings__localization__subtype_select_placeholder)
|
||||
val selectListValues = remember(selectValue) { listOf(selectValue) }
|
||||
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val configuration = LocalConfiguration.current
|
||||
|
||||
@@ -27,8 +27,8 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiHistory
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiHistoryHelper
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSkinTone
|
||||
@@ -52,7 +52,7 @@ fun MediaScreen() = FlorisScreen {
|
||||
previewFieldVisible = true
|
||||
iconSpaceReserved = true
|
||||
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
|
||||
var shouldDelete by remember { mutableStateOf<ShouldDelete?>(null) }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
@@ -68,7 +68,7 @@ fun SmartbarScreen() = FlorisScreen {
|
||||
// TODO: schedule to remove this preference in the future, but keep it for now so users
|
||||
// know why the setting is not available anymore. Also force enable it for UI display.
|
||||
SideEffect {
|
||||
// prefs.smartbar.sharedActionsAutoExpandCollapse.set(true)
|
||||
prefs.smartbar.sharedActionsAutoExpandCollapse.set(true)
|
||||
}
|
||||
SwitchPreference(
|
||||
prefs.smartbar.sharedActionsAutoExpandCollapse,
|
||||
|
||||
@@ -96,7 +96,6 @@ import dev.patrickgold.jetpref.material.ui.JetPrefDropdown
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefTextField
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefTextFieldDefaults
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
import org.florisboard.lib.android.stringRes
|
||||
import org.florisboard.lib.kotlin.curlyFormat
|
||||
import org.florisboard.lib.snygg.SnyggAnnotationRule
|
||||
@@ -407,7 +406,7 @@ private fun EditCodeValueDialog(
|
||||
}
|
||||
if (!isFlorisBoardEnabled || !isFlorisBoardSelected) {
|
||||
lastRecordingToast?.cancel()
|
||||
lastRecordingToast = context.showShortToastSync(
|
||||
lastRecordingToast = context.showShortToast(
|
||||
R.string.settings__theme_editor__code_recording_requires_default_ime_floris,
|
||||
"app_name" to context.stringRes(R.string.floris_app_name),
|
||||
)
|
||||
@@ -433,12 +432,12 @@ private fun EditCodeValueDialog(
|
||||
val defaultReceiver = keyboardManager.inputEventDispatcher.keyEventReceiver
|
||||
keyboardManager.inputEventDispatcher.keyEventReceiver = receiver
|
||||
lastRecordingToast?.cancel()
|
||||
lastRecordingToast = context.showShortToastSync(R.string.settings__theme_editor__code_recording_started)
|
||||
lastRecordingToast = context.showShortToast(R.string.settings__theme_editor__code_recording_started)
|
||||
focusRequester.requestFocus()
|
||||
onDispose {
|
||||
keyboardManager.inputEventDispatcher.keyEventReceiver = defaultReceiver
|
||||
lastRecordingToast?.cancel()
|
||||
lastRecordingToast = context.showShortToastSync(R.string.settings__theme_editor__code_recording_stopped)
|
||||
lastRecordingToast = context.showShortToast(R.string.settings__theme_editor__code_recording_stopped)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceLayout
|
||||
@@ -37,7 +37,7 @@ fun FineTuneDialog(onDismiss: () -> Unit) {
|
||||
onDismiss = onDismiss,
|
||||
contentPadding = FineTuneContentPadding,
|
||||
) {
|
||||
PreferenceLayout(FlorisPreferenceStore, iconSpaceReserved = false) {
|
||||
PreferenceLayout(florisPreferenceModel(), iconSpaceReserved = false) {
|
||||
ListPreference(
|
||||
listPref = prefs.theme.editorLevel,
|
||||
title = stringRes(R.string.settings__theme_editor__fine_tune__level),
|
||||
|
||||
@@ -30,6 +30,7 @@ import androidx.compose.material.icons.automirrored.filled.FormatAlignLeft
|
||||
import androidx.compose.material.icons.automirrored.filled.FormatAlignRight
|
||||
import androidx.compose.material.icons.automirrored.filled.WrapText
|
||||
import androidx.compose.material.icons.filled.AttachFile
|
||||
import androidx.compose.material.icons.filled.CheckBox
|
||||
import androidx.compose.material.icons.filled.CheckBoxOutlineBlank
|
||||
import androidx.compose.material.icons.filled.FontDownload
|
||||
import androidx.compose.material.icons.filled.FormatAlignCenter
|
||||
@@ -56,7 +57,7 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.snygg.value.SnyggCutCornerDpShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggDefinedVarValue
|
||||
@@ -120,7 +121,7 @@ internal fun SnyggValueIcon(
|
||||
modifier: Modifier = Modifier,
|
||||
spec: SnyggValueIcon.Spec = SnyggValueIcon.Normal,
|
||||
) {
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val context = LocalContext.current
|
||||
val accentColor by prefs.theme.accentColor.observeAsState()
|
||||
|
||||
|
||||
@@ -70,9 +70,9 @@ import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.apptheme.Shapes
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionComponentView
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponent
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponentEditor
|
||||
@@ -100,7 +100,6 @@ import dev.patrickgold.jetpref.material.ui.JetPrefTextField
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import org.florisboard.lib.snygg.SnyggAnnotationRule
|
||||
import org.florisboard.lib.snygg.SnyggElementRule
|
||||
@@ -146,7 +145,7 @@ fun ThemeEditorScreen(
|
||||
title = stringRes(R.string.ext__editor__edit_component__title_theme)
|
||||
scrollable = false
|
||||
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val context = LocalContext.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
val themeManager by context.themeManager()
|
||||
@@ -310,14 +309,14 @@ fun ThemeEditorScreen(
|
||||
}
|
||||
|
||||
DisposableEffect(workspace.version) {
|
||||
themeManager.previewThemeInfo.value = ThemeManager.ThemeInfo.DEFAULT.copy(
|
||||
themeManager.previewThemeInfo = ThemeManager.ThemeInfo.DEFAULT.copy(
|
||||
name = extPreviewTheme(System.currentTimeMillis().toString()),
|
||||
config = editor.build(),
|
||||
stylesheet = stylesheetEditor.build(),
|
||||
loadedDir = workspace.extDir,
|
||||
)
|
||||
onDispose {
|
||||
themeManager.previewThemeInfo.value = null
|
||||
themeManager.previewThemeInfo = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -634,7 +633,7 @@ private fun ComponentMetaEditorDialog(
|
||||
if (!allFieldsValid) {
|
||||
showValidationErrors = true
|
||||
} else if (id != editor.id && (workspace.editor as? ThemeExtensionEditor)?.themes?.find { it.id == id.trim() } != null) {
|
||||
context.showLongToastSync("A theme with this ID already exists!")
|
||||
context.showLongToast("A theme with this ID already exists!")
|
||||
} else {
|
||||
workspace.update {
|
||||
editor.id = id.trim()
|
||||
|
||||
@@ -29,11 +29,10 @@ import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponent
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
|
||||
@@ -46,7 +45,6 @@ import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
import dev.patrickgold.florisboard.themeManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
enum class ThemeManagerScreenAction(val id: String) {
|
||||
SELECT_DAY("select-day"),
|
||||
@@ -62,11 +60,10 @@ fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
|
||||
})
|
||||
previewFieldVisible = true
|
||||
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val context = LocalContext.current
|
||||
val extensionManager by context.extensionManager()
|
||||
val themeManager by context.themeManager()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val indexedThemeExtensions by extensionManager.themes.observeAsNonNullState()
|
||||
val extGroupedThemes = remember(indexedThemeExtensions) {
|
||||
@@ -86,7 +83,7 @@ fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
|
||||
val extComponentName = ExtensionComponentName(extId, componentId)
|
||||
when (action) {
|
||||
ThemeManagerScreenAction.SELECT_DAY,
|
||||
ThemeManagerScreenAction.SELECT_NIGHT -> scope.launch {
|
||||
ThemeManagerScreenAction.SELECT_NIGHT -> {
|
||||
getThemeIdPref().set(extComponentName)
|
||||
}
|
||||
}
|
||||
@@ -99,9 +96,9 @@ fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
|
||||
|
||||
content {
|
||||
DisposableEffect(activeThemeId) {
|
||||
themeManager.previewThemeId.value = activeThemeId
|
||||
themeManager.previewThemeId = activeThemeId
|
||||
onDispose {
|
||||
themeManager.previewThemeId.value = null
|
||||
themeManager.previewThemeId = null
|
||||
}
|
||||
}
|
||||
val grayColor = LocalContentColor.current.copy(alpha = 0.56f)
|
||||
|
||||
@@ -24,8 +24,8 @@ import androidx.compose.material.icons.filled.DarkMode
|
||||
import androidx.compose.material.icons.filled.LightMode
|
||||
import androidx.compose.material.icons.filled.WbTwilight
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
@@ -59,8 +59,8 @@ fun ThemeScreen() = FlorisScreen {
|
||||
|
||||
@Composable
|
||||
fun ThemeManager.getThemeLabel(id: ExtensionComponentName): String {
|
||||
val configs by indexedThemeConfigs.collectAsState()
|
||||
configs[id]?.let { return it.label }
|
||||
val configs by indexedThemeConfigs.observeAsState()
|
||||
configs?.get(id)?.let { return it.label }
|
||||
return id.toString()
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -41,11 +40,11 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.AppPrefs
|
||||
import dev.patrickgold.florisboard.app.FlorisAppActivity
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisBulletSpacer
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreenScope
|
||||
@@ -58,11 +57,10 @@ import dev.patrickgold.florisboard.lib.util.launchActivity
|
||||
import dev.patrickgold.florisboard.lib.util.launchUrl
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceUiScope
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
|
||||
|
||||
@Composable
|
||||
fun SetupScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.setup__title)
|
||||
@@ -72,8 +70,7 @@ fun SetupScreen() = FlorisScreen {
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
|
||||
val prefs by FlorisPreferenceStore
|
||||
val scope = rememberCoroutineScope()
|
||||
val prefs by florisPreferenceModel()
|
||||
|
||||
val isFlorisBoardEnabled by InputMethodUtils.observeIsFlorisboardEnabled(foregroundOnly = true)
|
||||
val isFlorisBoardSelected by InputMethodUtils.observeIsFlorisboardSelected(foregroundOnly = true)
|
||||
@@ -81,12 +78,10 @@ fun SetupScreen() = FlorisScreen {
|
||||
|
||||
val requestNotification =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
||||
scope.launch {
|
||||
if (isGranted) {
|
||||
prefs.internal.notificationPermissionState.set(NotificationPermissionState.GRANTED)
|
||||
} else {
|
||||
prefs.internal.notificationPermissionState.set(NotificationPermissionState.DENIED)
|
||||
}
|
||||
if (isGranted) {
|
||||
prefs.internal.notificationPermissionState.set(NotificationPermissionState.GRANTED)
|
||||
} else {
|
||||
prefs.internal.notificationPermissionState.set(NotificationPermissionState.DENIED)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,8 +91,7 @@ fun SetupScreen() = FlorisScreen {
|
||||
context,
|
||||
navController,
|
||||
requestNotification,
|
||||
hasNotificationPermission,
|
||||
scope,
|
||||
hasNotificationPermission
|
||||
)
|
||||
}
|
||||
|
||||
@@ -109,7 +103,6 @@ private fun FlorisScreenScope.content(
|
||||
navController: NavController,
|
||||
requestNotification: ManagedActivityResultLauncher<String, Boolean>,
|
||||
hasNotificationPermission: NotificationPermissionState,
|
||||
scope: CoroutineScope,
|
||||
) {
|
||||
|
||||
val stepState = rememberSaveable(saver = FlorisStepState.Saver) {
|
||||
@@ -165,7 +158,7 @@ private fun FlorisScreenScope.content(
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
},
|
||||
steps = steps(
|
||||
context, navController, requestNotification, scope
|
||||
context, navController, requestNotification
|
||||
),
|
||||
footer = {
|
||||
footer(context)
|
||||
@@ -196,11 +189,10 @@ private fun footer(context: Context) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PreferenceUiScope<FlorisPreferenceModel>.steps(
|
||||
private fun PreferenceUiScope<AppPrefs>.steps(
|
||||
context: Context,
|
||||
navController: NavController,
|
||||
requestNotification: ManagedActivityResultLauncher<String, Boolean>,
|
||||
scope: CoroutineScope,
|
||||
): List<FlorisStep> {
|
||||
|
||||
return listOfNotNull(
|
||||
@@ -240,7 +232,7 @@ private fun PreferenceUiScope<FlorisPreferenceModel>.steps(
|
||||
StepText(stringRes(R.string.setup__finish_up__description_p1))
|
||||
StepText(stringRes(R.string.setup__finish_up__description_p2))
|
||||
StepButton(label = stringRes(R.string.setup__finish_up__finish_btn)) {
|
||||
scope.launch { this@steps.prefs.internal.isImeSetUp.set(true) }
|
||||
this@steps.prefs.internal.isImeSetUp.set(true)
|
||||
navController.navigate(Routes.Settings.Home) {
|
||||
popUpTo(Routes.Setup.Screen) {
|
||||
inclusive = true
|
||||
|
||||
@@ -74,7 +74,6 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.mutableStateSetOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -88,7 +87,7 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.ime.ImeUiMode
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardFileStorage
|
||||
@@ -113,11 +112,9 @@ import dev.patrickgold.florisboard.lib.observeAsTransformingState
|
||||
import dev.patrickgold.florisboard.lib.util.NetworkUtils
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.AndroidKeyguardManager
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
import org.florisboard.lib.android.systemService
|
||||
import org.florisboard.lib.snygg.SnyggQueryAttributes
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
@@ -139,8 +136,7 @@ const val CLIPBOARD_HISTORY_NUM_GRID_COLUMNS_AUTO: Int = 0
|
||||
fun ClipboardInputLayout(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val prefs by FlorisPreferenceStore
|
||||
val scope = rememberCoroutineScope()
|
||||
val prefs by florisPreferenceModel()
|
||||
val context = LocalContext.current
|
||||
val clipboardManager by context.clipboardManager()
|
||||
val keyboardManager by context.keyboardManager()
|
||||
@@ -207,7 +203,7 @@ fun ClipboardInputLayout(
|
||||
)
|
||||
SnyggIconButton(
|
||||
elementName = FlorisImeUi.ClipboardHeaderButton.elementName,
|
||||
onClick = { scope.launch { prefs.clipboard.historyEnabled.set(!historyEnabled) } },
|
||||
onClick = { prefs.clipboard.historyEnabled.set(!historyEnabled) },
|
||||
modifier = sizeModifier.autoMirrorForRtl(),
|
||||
enabled = !deviceLocked && !isPopupSurfaceActive(),
|
||||
) {
|
||||
@@ -587,7 +583,7 @@ fun ClipboardInputLayout(
|
||||
attributes = mapOf("action" to "yes"),
|
||||
onClick = {
|
||||
clipboardManager.clearHistory()
|
||||
context.showShortToastSync(R.string.clipboard__cleared_history)
|
||||
context.showShortToast(R.string.clipboard__cleared_history)
|
||||
showClearAllHistory = false
|
||||
isFilterRowShown = false
|
||||
},
|
||||
@@ -633,7 +629,7 @@ fun ClipboardInputLayout(
|
||||
text = stringRes(R.string.clipboard__disabled__message),
|
||||
)
|
||||
SnyggButton(FlorisImeUi.ClipboardHistoryDisabledButton.elementName,
|
||||
onClick = { scope.launch { prefs.clipboard.historyEnabled.set(true) } },
|
||||
onClick = { prefs.clipboard.historyEnabled.set(true) },
|
||||
modifier = Modifier.align(Alignment.End),
|
||||
) {
|
||||
SnyggText(
|
||||
|
||||
@@ -17,10 +17,11 @@
|
||||
package dev.patrickgold.florisboard.ime.clipboard
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardHistoryDao
|
||||
@@ -43,7 +44,6 @@ import org.florisboard.lib.android.AndroidClipboardManager
|
||||
import org.florisboard.lib.android.AndroidClipboardManager_OnPrimaryClipChangedListener
|
||||
import org.florisboard.lib.android.setOrClearPrimaryClip
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
import org.florisboard.lib.android.systemService
|
||||
import org.florisboard.lib.kotlin.tryOrNull
|
||||
import java.io.Closeable
|
||||
@@ -91,7 +91,7 @@ class ClipboardManager(
|
||||
}
|
||||
}
|
||||
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val appContext by context.appContext()
|
||||
private val editorInstance by context.editorInstance()
|
||||
private val systemClipboardManager = context.systemService(AndroidClipboardManager::class)
|
||||
@@ -355,7 +355,7 @@ class ClipboardManager(
|
||||
val editorInstance by appContext.editorInstance()
|
||||
editorInstance.commitClipboardItem(item).also { result ->
|
||||
if (!result) {
|
||||
appContext.showShortToastSync("Failed to paste item.")
|
||||
appContext.showShortToast("Failed to paste item.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +47,8 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.apptheme.FlorisAppTheme
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.lib.compose.ProvideLocalizedResources
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
@@ -135,7 +135,7 @@ class FlorisCopyToClipboardActivity : ComponentActivity() {
|
||||
|
||||
@Composable
|
||||
private fun Content() {
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
ProvideLocalizedResources(this, forceLayoutDirection = LayoutDirection.Ltr) {
|
||||
val theme by prefs.other.settingsTheme.observeAsState()
|
||||
FlorisAppTheme(theme) {
|
||||
|
||||
@@ -17,18 +17,15 @@
|
||||
package dev.patrickgold.florisboard.ime.core
|
||||
|
||||
import android.content.Context
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.keyboard.CurrencySet
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogDebug
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.florisboard.lib.kotlin.collectLatestIn
|
||||
|
||||
val SubtypeJsonConfig = Json {
|
||||
encodeDefaults = true
|
||||
@@ -41,9 +38,8 @@ val SubtypeJsonConfig = Json {
|
||||
* helper methods for the in-keyboard language switch process.
|
||||
*/
|
||||
class SubtypeManager(context: Context) {
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val keyboardManager by context.keyboardManager()
|
||||
private val scope = CoroutineScope(Dispatchers.Default)
|
||||
|
||||
private val _subtypesFlow = MutableStateFlow(listOf<Subtype>())
|
||||
val subtypesFlow = _subtypesFlow.asStateFlow()
|
||||
@@ -58,7 +54,7 @@ class SubtypeManager(context: Context) {
|
||||
private set(v) { _activeSubtypeFlow.value = v }
|
||||
|
||||
init {
|
||||
prefs.localization.subtypes.asFlow().collectLatestIn(scope) { listRaw ->
|
||||
prefs.localization.subtypes.observeForever { listRaw ->
|
||||
flogDebug { listRaw }
|
||||
val list = if (listRaw.isNotBlank()) {
|
||||
SubtypeJsonConfig.decodeFromString<List<Subtype>>(listRaw)
|
||||
@@ -70,7 +66,7 @@ class SubtypeManager(context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun persistNewSubtypeList(list: List<Subtype>) = scope.launch {
|
||||
private fun persistNewSubtypeList(list: List<Subtype>) {
|
||||
val listRaw = SubtypeJsonConfig.encodeToString(list)
|
||||
prefs.localization.subtypes.set(listRaw)
|
||||
}
|
||||
@@ -82,7 +78,7 @@ class SubtypeManager(context: Context) {
|
||||
* @return The active subtype or null, if the subtype list is empty or no new active subtype
|
||||
* could be determined.
|
||||
*/
|
||||
private fun evaluateActiveSubtype(list: List<Subtype>) = scope.launch {
|
||||
private fun evaluateActiveSubtype(list: List<Subtype>) {
|
||||
val activeSubtypeId = prefs.localization.activeSubtypeId.get()
|
||||
val subtype = list.find { it.id == activeSubtypeId } ?: list.firstOrNull() ?: Subtype.DEFAULT
|
||||
if (subtype.id != activeSubtypeId) {
|
||||
@@ -188,7 +184,7 @@ class SubtypeManager(context: Context) {
|
||||
/**
|
||||
* Switch to the previous subtype in the subtype list if possible.
|
||||
*/
|
||||
fun switchToPrevSubtype() = scope.launch {
|
||||
fun switchToPrevSubtype() {
|
||||
val subtypeList = subtypes
|
||||
val cachedActiveSubtype = activeSubtype
|
||||
var triggerNextSubtype = false
|
||||
@@ -211,7 +207,7 @@ class SubtypeManager(context: Context) {
|
||||
/**
|
||||
* Switch to the next subtype in the subtype list if possible.
|
||||
*/
|
||||
fun switchToNextSubtype() = scope.launch {
|
||||
fun switchToNextSubtype() {
|
||||
val subtypeList = subtypes
|
||||
val cachedActiveSubtype = activeSubtype
|
||||
var triggerNextSubtype = false
|
||||
@@ -231,7 +227,7 @@ class SubtypeManager(context: Context) {
|
||||
activeSubtype = newActiveSubtype
|
||||
}
|
||||
|
||||
fun switchToSubtypeById(id: Long) = scope.launch {
|
||||
fun switchToSubtypeById(id: Long) {
|
||||
if (subtypes.any { it.id == id }) {
|
||||
activeSubtype = getSubtypeById(id)!!
|
||||
prefs.localization.activeSubtypeId.set(id)
|
||||
|
||||
@@ -18,7 +18,7 @@ package dev.patrickgold.florisboard.ime.dictionary
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Room
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.nlp.SuggestionCandidate
|
||||
import dev.patrickgold.florisboard.ime.nlp.WordSuggestionCandidate
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
@@ -29,7 +29,7 @@ import java.lang.ref.WeakReference
|
||||
*/
|
||||
class DictionaryManager private constructor(context: Context) {
|
||||
private val applicationContext: WeakReference<Context> = WeakReference(context.applicationContext ?: context)
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val prefs by florisPreferenceModel()
|
||||
|
||||
private var florisUserDictionaryDatabase: FlorisUserDictionaryDatabase? = null
|
||||
private var systemUserDictionaryDatabase: SystemUserDictionaryDatabase? = null
|
||||
|
||||
@@ -23,7 +23,7 @@ import android.view.KeyEvent
|
||||
import androidx.core.view.inputmethod.InputConnectionCompat
|
||||
import androidx.core.view.inputmethod.InputContentInfoCompat
|
||||
import dev.patrickgold.florisboard.FlorisImeService
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardFileStorage
|
||||
@@ -43,14 +43,13 @@ import dev.patrickgold.florisboard.subtypeManager
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
|
||||
class EditorInstance(context: Context) : AbstractEditorInstance(context) {
|
||||
companion object {
|
||||
private const val SPACE = " "
|
||||
}
|
||||
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val appContext by context.appContext()
|
||||
private val clipboardManager by context.clipboardManager()
|
||||
private val keyboardManager by context.keyboardManager()
|
||||
@@ -394,7 +393,7 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
|
||||
if (text != null) {
|
||||
clipboardManager.addNewPlaintext(text.toString())
|
||||
} else {
|
||||
appContext.showShortToastSync("Failed to retrieve selected text requested to cut: Eiter selection state is invalid or an error occurred within the input connection.")
|
||||
appContext.showShortToast("Failed to retrieve selected text requested to cut: Eiter selection state is invalid or an error occurred within the input connection.")
|
||||
}
|
||||
return deleteBackwards()
|
||||
}
|
||||
@@ -412,7 +411,7 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
|
||||
if (text != null) {
|
||||
clipboardManager.addNewPlaintext(text.toString())
|
||||
} else {
|
||||
appContext.showShortToastSync("Failed to retrieve selected text requested to copy: Eiter selection state is invalid or an error occurred within the input connection.")
|
||||
appContext.showShortToast("Failed to retrieve selected text requested to copy: Eiter selection state is invalid or an error occurred within the input connection.")
|
||||
}
|
||||
val activeSelection = activeContent.selection
|
||||
return setSelection(activeSelection.end, activeSelection.end)
|
||||
@@ -429,7 +428,7 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
|
||||
phantomSpace.setInactive()
|
||||
return commitClipboardItem(clipboardManager.primaryClip).also { result ->
|
||||
if (!result) {
|
||||
appContext.showShortToastSync("Failed to paste item.")
|
||||
appContext.showShortToast("Failed to paste item.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import android.view.ViewConfiguration
|
||||
import androidx.collection.SparseArrayCompat
|
||||
import androidx.collection.isNotEmpty
|
||||
import androidx.collection.set
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.keyboard.KeyData
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
@@ -48,7 +48,7 @@ class InputEventDispatcher private constructor(private val repeatableKeyCodes: I
|
||||
fun new(repeatableKeyCodes: IntArray = intArrayOf()) = InputEventDispatcher(repeatableKeyCodes.clone())
|
||||
}
|
||||
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
|
||||
|
||||
private val pressedKeys = guardedByLock { SparseArrayCompat<PressedKeyInfo>() }
|
||||
|
||||
@@ -21,7 +21,7 @@ import android.media.AudioManager
|
||||
import android.provider.Settings
|
||||
import android.view.HapticFeedbackConstants
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.keyboard.KeyData
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
@@ -46,7 +46,7 @@ class InputFeedbackController private constructor(private val ims: InputMethodSe
|
||||
fun new(ims: InputMethodService) = InputFeedbackController(ims)
|
||||
}
|
||||
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val prefs by florisPreferenceModel()
|
||||
|
||||
private val audioManager = ims.systemServiceOrNull(AudioManager::class)
|
||||
private val vibrator = ims.systemVibratorOrNull()
|
||||
|
||||
@@ -36,7 +36,8 @@ import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.onehanded.OneHandedMode
|
||||
import dev.patrickgold.florisboard.ime.smartbar.ExtendedActionsPlacement
|
||||
import dev.patrickgold.florisboard.ime.smartbar.SmartbarLayout
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyboard
|
||||
@@ -79,7 +80,7 @@ object FlorisImeSizing {
|
||||
|
||||
@Composable
|
||||
fun smartbarUiHeight(): Dp {
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val smartbarEnabled by prefs.smartbar.enabled.observeAsState()
|
||||
val smartbarLayout by prefs.smartbar.layout.observeAsState()
|
||||
val extendedActionsExpanded by prefs.smartbar.extendedActionsExpanded.observeAsState()
|
||||
@@ -112,7 +113,7 @@ object FlorisImeSizing {
|
||||
|
||||
@Composable
|
||||
fun ProvideKeyboardRowBaseHeight(content: @Composable () -> Unit) {
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val resources = LocalContext.current.resources
|
||||
val configuration = LocalConfiguration.current
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import dev.patrickgold.florisboard.FlorisImeService
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
@@ -74,9 +74,7 @@ import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import org.florisboard.lib.android.AndroidKeyguardManager
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
import org.florisboard.lib.android.systemService
|
||||
import org.florisboard.lib.kotlin.collectIn
|
||||
import org.florisboard.lib.kotlin.collectLatestIn
|
||||
@@ -85,7 +83,7 @@ import java.util.concurrent.atomic.AtomicInteger
|
||||
private val DoubleSpacePeriodMatcher = """([^.!?‽\s]\s)""".toRegex()
|
||||
|
||||
class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val appContext by context.appContext()
|
||||
private val clipboardManager by context.clipboardManager()
|
||||
private val editorInstance by context.editorInstance()
|
||||
@@ -131,21 +129,21 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
keyboardCache.clear()
|
||||
}
|
||||
}
|
||||
prefs.keyboard.numberRow.asFlow().collectLatestIn(scope) {
|
||||
prefs.keyboard.numberRow.observeForever {
|
||||
updateActiveEvaluators {
|
||||
keyboardCache.clear(KeyboardMode.CHARACTERS)
|
||||
}
|
||||
}
|
||||
prefs.keyboard.hintedNumberRowEnabled.asFlow().collectLatestIn(scope) {
|
||||
prefs.keyboard.hintedNumberRowEnabled.observeForever {
|
||||
updateActiveEvaluators()
|
||||
}
|
||||
prefs.keyboard.hintedSymbolsEnabled.asFlow().collectLatestIn(scope) {
|
||||
prefs.keyboard.hintedSymbolsEnabled.observeForever {
|
||||
updateActiveEvaluators()
|
||||
}
|
||||
prefs.keyboard.utilityKeyEnabled.asFlow().collectLatestIn(scope) {
|
||||
prefs.keyboard.utilityKeyEnabled.observeForever {
|
||||
updateActiveEvaluators()
|
||||
}
|
||||
prefs.keyboard.utilityKeyAction.asFlow().collectLatestIn(scope) {
|
||||
prefs.keyboard.utilityKeyAction.observeForever {
|
||||
updateActiveEvaluators()
|
||||
}
|
||||
activeState.collectLatestIn(scope) {
|
||||
@@ -166,10 +164,10 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
editorInstance.activeContentFlow.collectIn(scope) { content ->
|
||||
resetSuggestions(content)
|
||||
}
|
||||
prefs.devtools.enabled.asFlow().collectLatestIn(scope) {
|
||||
prefs.devtools.enabled.observeForever {
|
||||
reevaluateDebugFlags()
|
||||
}
|
||||
prefs.devtools.showDragAndDropHelpers.asFlow().collectLatestIn(scope) {
|
||||
prefs.devtools.showDragAndDropHelpers.observeForever {
|
||||
reevaluateDebugFlags()
|
||||
}
|
||||
}
|
||||
@@ -239,7 +237,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
return subtypeManager.subtypes.size > 1
|
||||
}
|
||||
|
||||
suspend fun toggleOneHandedMode() {
|
||||
fun toggleOneHandedMode() {
|
||||
prefs.keyboard.oneHandedModeEnabled.set(!prefs.keyboard.oneHandedModeEnabled.get())
|
||||
}
|
||||
|
||||
@@ -582,7 +580,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
/**
|
||||
* Handles a [KeyCode.TOGGLE_INCOGNITO_MODE] event.
|
||||
*/
|
||||
private suspend fun handleToggleIncognitoMode() {
|
||||
private fun handleToggleIncognitoMode() {
|
||||
prefs.suggestion.forceIncognitoModeFromDynamic.set(!prefs.suggestion.forceIncognitoModeFromDynamic.get())
|
||||
val newState = !activeState.isIncognitoMode
|
||||
activeState.isIncognitoMode = newState
|
||||
@@ -608,7 +606,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
private fun handleToggleAutocorrect() {
|
||||
lastToastReference.get()?.cancel()
|
||||
lastToastReference = WeakReference(
|
||||
appContext.showLongToastSync("Autocorrect toggle is a placeholder and not yet implemented")
|
||||
appContext.showLongToast("Autocorrect toggle is a placeholder and not yet implemented")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -718,14 +716,14 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
clipboardManager.primaryClip?.let { clipboardManager.deleteClip(it) }
|
||||
}
|
||||
clipboardManager.updatePrimaryClip(null)
|
||||
appContext.showShortToastSync(R.string.clipboard__cleared_primary_clip)
|
||||
appContext.showShortToast(R.string.clipboard__cleared_primary_clip)
|
||||
}
|
||||
KeyCode.TOGGLE_COMPACT_LAYOUT -> scope.launch { toggleOneHandedMode() }
|
||||
KeyCode.COMPACT_LAYOUT_TO_LEFT -> scope.launch {
|
||||
KeyCode.TOGGLE_COMPACT_LAYOUT -> toggleOneHandedMode()
|
||||
KeyCode.COMPACT_LAYOUT_TO_LEFT -> {
|
||||
prefs.keyboard.oneHandedMode.set(OneHandedMode.START)
|
||||
toggleOneHandedMode()
|
||||
}
|
||||
KeyCode.COMPACT_LAYOUT_TO_RIGHT -> scope.launch {
|
||||
KeyCode.COMPACT_LAYOUT_TO_RIGHT -> {
|
||||
prefs.keyboard.oneHandedMode.set(OneHandedMode.END)
|
||||
toggleOneHandedMode()
|
||||
}
|
||||
@@ -755,7 +753,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
}
|
||||
KeyCode.SYSTEM_PREV_INPUT_METHOD -> FlorisImeService.switchToPrevInputMethod()
|
||||
KeyCode.SYSTEM_NEXT_INPUT_METHOD -> FlorisImeService.switchToNextInputMethod()
|
||||
KeyCode.TOGGLE_SMARTBAR_VISIBILITY -> scope.launch {
|
||||
KeyCode.TOGGLE_SMARTBAR_VISIBILITY -> {
|
||||
prefs.smartbar.enabled.let { it.set(!it.get()) }
|
||||
}
|
||||
KeyCode.TOGGLE_ACTIONS_OVERFLOW -> {
|
||||
@@ -764,7 +762,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
KeyCode.TOGGLE_ACTIONS_EDITOR -> {
|
||||
activeState.isActionsEditorVisible = !activeState.isActionsEditorVisible
|
||||
}
|
||||
KeyCode.TOGGLE_INCOGNITO_MODE -> scope.launch { handleToggleIncognitoMode() }
|
||||
KeyCode.TOGGLE_INCOGNITO_MODE -> handleToggleIncognitoMode()
|
||||
KeyCode.TOGGLE_AUTOCORRECT -> handleToggleAutocorrect()
|
||||
KeyCode.UNDO -> editorInstance.performUndo()
|
||||
KeyCode.VIEW_CHARACTERS -> activeState.keyboardMode = KeyboardMode.CHARACTERS
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package dev.patrickgold.florisboard.ime.keyboard
|
||||
|
||||
import android.content.Context
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
@@ -78,7 +78,7 @@ data class DebugLayoutComputationResult(
|
||||
* Class which manages layout loading and caching.
|
||||
*/
|
||||
class LayoutManager(context: Context) {
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val appContext by context.appContext()
|
||||
private val extensionManager by context.extensionManager()
|
||||
private val keyboardManager by context.keyboardManager()
|
||||
|
||||
@@ -16,12 +16,13 @@
|
||||
|
||||
package dev.patrickgold.florisboard.ime.media.emoji
|
||||
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.AppPrefs
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogError
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceSerializer
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
@Serializable
|
||||
@@ -75,7 +76,7 @@ data class EmojiHistory(
|
||||
object EmojiHistoryHelper {
|
||||
private var emojiGuard = Mutex(locked = false)
|
||||
|
||||
suspend fun markEmojiUsed(prefs: FlorisPreferenceModel, emoji: Emoji): Unit = emojiGuard.withLock {
|
||||
suspend fun markEmojiUsed(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
@@ -120,7 +121,7 @@ object EmojiHistoryHelper {
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun pinEmoji(prefs: FlorisPreferenceModel, emoji: Emoji): Unit = emojiGuard.withLock {
|
||||
suspend fun pinEmoji(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
@@ -137,7 +138,7 @@ object EmojiHistoryHelper {
|
||||
prefs.emoji.historyData.set(dataMut.build())
|
||||
}
|
||||
|
||||
suspend fun unpinEmoji(prefs: FlorisPreferenceModel, emoji: Emoji): Unit = emojiGuard.withLock {
|
||||
suspend fun unpinEmoji(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
@@ -154,7 +155,7 @@ object EmojiHistoryHelper {
|
||||
prefs.emoji.historyData.set(dataMut.build())
|
||||
}
|
||||
|
||||
suspend fun moveEmoji(prefs: FlorisPreferenceModel, emoji: Emoji, offset: Int): Unit = emojiGuard.withLock {
|
||||
suspend fun moveEmoji(prefs: AppPrefs, emoji: Emoji, offset: Int) = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get() || offset == 0) {
|
||||
return
|
||||
}
|
||||
@@ -174,7 +175,7 @@ object EmojiHistoryHelper {
|
||||
prefs.emoji.historyData.set(dataMut.build())
|
||||
}
|
||||
|
||||
suspend fun removeEmoji(prefs: FlorisPreferenceModel, emoji: Emoji): Unit = emojiGuard.withLock {
|
||||
suspend fun removeEmoji(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
@@ -194,7 +195,7 @@ object EmojiHistoryHelper {
|
||||
prefs.emoji.historyData.set(dataMut.build())
|
||||
}
|
||||
|
||||
suspend fun deleteHistory(prefs: FlorisPreferenceModel): Unit = emojiGuard.withLock {
|
||||
suspend fun deleteHistory(prefs: AppPrefs) = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
@@ -202,7 +203,7 @@ object EmojiHistoryHelper {
|
||||
prefs.emoji.historyData.set(EmojiHistory(pinned = dataMut.pinned, listOf()))
|
||||
}
|
||||
|
||||
suspend fun deletePinned(prefs: FlorisPreferenceModel): Unit = emojiGuard.withLock {
|
||||
suspend fun deletePinned(prefs: AppPrefs) = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@@ -45,6 +46,8 @@ import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material.icons.outlined.PushPin
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.TabRow
|
||||
import androidx.compose.material3.TabRowDefaults
|
||||
@@ -80,7 +83,7 @@ import androidx.compose.ui.window.Popup
|
||||
import androidx.emoji2.text.EmojiCompat
|
||||
import androidx.emoji2.widget.EmojiTextView
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
import dev.patrickgold.florisboard.ime.input.LocalInputFeedbackController
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
@@ -130,7 +133,7 @@ fun EmojiPaletteView(
|
||||
fullEmojiMappings: EmojiData,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val context = LocalContext.current
|
||||
val editorInstance by context.editorInstance()
|
||||
val keyboardManager by context.keyboardManager()
|
||||
@@ -494,7 +497,7 @@ private fun EmojiHistoryPopup(
|
||||
onHistoryAction: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val scope = rememberCoroutineScope()
|
||||
val emojiKeyHeight = FlorisImeSizing.smartbarHeight
|
||||
val context = LocalContext.current
|
||||
|
||||
@@ -20,7 +20,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.stream.Collectors
|
||||
import android.content.Context
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.editor.EditorContent
|
||||
import dev.patrickgold.florisboard.ime.nlp.EmojiSuggestionCandidate
|
||||
@@ -41,7 +41,7 @@ import io.github.reactivecircus.cache4k.Cache
|
||||
class EmojiSuggestionProvider(private val context: Context) : SuggestionProvider {
|
||||
override val providerId = "org.florisboard.nlp.providers.emoji"
|
||||
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val lettersRegex = "^[A-Za-z]*$".toRegex()
|
||||
|
||||
private val cachedEmojiMappings = Cache.Builder().build<FlorisLocale, EmojiDataBySkinTone>()
|
||||
|
||||
@@ -20,7 +20,7 @@ import android.content.Context
|
||||
import android.os.SystemClock
|
||||
import android.util.LruCache
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardItem
|
||||
@@ -43,8 +43,8 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import org.florisboard.lib.kotlin.guardedByLock
|
||||
import org.florisboard.lib.kotlin.collectLatestIn
|
||||
import org.florisboard.lib.kotlin.guardedByLock
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlin.properties.Delegates
|
||||
@@ -54,7 +54,7 @@ private const val BLANK_STR_PATTERN = "^\\s*$"
|
||||
class NlpManager(context: Context) {
|
||||
private val blankStrRegex = Regex(BLANK_STR_PATTERN)
|
||||
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val clipboardManager by context.clipboardManager()
|
||||
private val editorInstance by context.editorInstance()
|
||||
private val keyboardManager by context.keyboardManager()
|
||||
@@ -93,13 +93,13 @@ class NlpManager(context: Context) {
|
||||
clipboardManager.primaryClipFlow.collectLatestIn(scope) {
|
||||
assembleCandidates()
|
||||
}
|
||||
prefs.suggestion.enabled.asFlow().collectLatestIn(scope) {
|
||||
prefs.suggestion.enabled.observeForever {
|
||||
assembleCandidates()
|
||||
}
|
||||
prefs.clipboard.suggestionEnabled.asFlow().collectLatestIn(scope) {
|
||||
prefs.clipboard.suggestionEnabled.observeForever {
|
||||
assembleCandidates()
|
||||
}
|
||||
prefs.emoji.suggestionEnabled.asFlow().collectLatestIn(scope) {
|
||||
prefs.emoji.suggestionEnabled.observeForever {
|
||||
assembleCandidates()
|
||||
}
|
||||
subtypeManager.activeSubtypeFlow.collectLatestIn(scope) { subtype ->
|
||||
@@ -317,10 +317,8 @@ class NlpManager(context: Context) {
|
||||
}*/
|
||||
val isSelection = editorInstance.activeContent.selection.isSelectionMode
|
||||
val isExpanded = list1.isNullOrEmpty() && list2.isNullOrEmpty() || isSelection
|
||||
scope.launch {
|
||||
prefs.smartbar.sharedActionsExpandWithAnimation.set(false)
|
||||
prefs.smartbar.sharedActionsExpanded.set(isExpanded)
|
||||
}
|
||||
prefs.smartbar.sharedActionsExpandWithAnimation.set(false)
|
||||
prefs.smartbar.sharedActionsExpanded.set(isExpanded)
|
||||
}
|
||||
|
||||
fun addToDebugOverlay(word: String, info: SpellingResult) {
|
||||
|
||||
@@ -26,16 +26,14 @@ import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.filled.ZoomOutMap
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.input.LocalInputFeedbackController
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.snygg.ui.SnyggColumn
|
||||
import org.florisboard.lib.snygg.ui.SnyggIcon
|
||||
import org.florisboard.lib.snygg.ui.SnyggIconButton
|
||||
@@ -46,8 +44,7 @@ fun RowScope.OneHandedPanel(
|
||||
panelSide: OneHandedMode,
|
||||
weight: Float,
|
||||
) {
|
||||
val prefs by FlorisPreferenceStore
|
||||
val scope = rememberCoroutineScope()
|
||||
val prefs by florisPreferenceModel()
|
||||
val inputFeedbackController = LocalInputFeedbackController.current
|
||||
|
||||
SnyggColumn(
|
||||
@@ -61,10 +58,8 @@ fun RowScope.OneHandedPanel(
|
||||
SnyggIconButton(
|
||||
FlorisImeUi.OneHandedPanelButton.elementName,
|
||||
onClick = {
|
||||
scope.launch {
|
||||
inputFeedbackController.keyPress()
|
||||
prefs.keyboard.oneHandedModeEnabled.set(false)
|
||||
}
|
||||
inputFeedbackController.keyPress()
|
||||
prefs.keyboard.oneHandedModeEnabled.set(false)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -79,10 +74,8 @@ fun RowScope.OneHandedPanel(
|
||||
SnyggIconButton(
|
||||
FlorisImeUi.OneHandedPanelButton.elementName,
|
||||
onClick = {
|
||||
scope.launch {
|
||||
inputFeedbackController.keyPress()
|
||||
prefs.keyboard.oneHandedMode.set(panelSide)
|
||||
}
|
||||
inputFeedbackController.keyPress()
|
||||
prefs.keyboard.oneHandedMode.set(panelSide)
|
||||
},
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
|
||||
@@ -28,7 +28,6 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import dev.patrickgold.florisboard.ime.keyboard.Key
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import org.florisboard.lib.snygg.SnyggQueryAttributes
|
||||
@@ -59,7 +58,7 @@ fun PopupBaseBox(
|
||||
.align(Alignment.TopCenter),
|
||||
) {
|
||||
SnyggText(
|
||||
modifier = Modifier.align(Alignment.Center).zIndex(100f),
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
text = label,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.zIndex
|
||||
import dev.patrickgold.florisboard.ime.keyboard.ComputingEvaluator
|
||||
import dev.patrickgold.florisboard.ime.keyboard.DefaultComputingEvaluator
|
||||
import dev.patrickgold.florisboard.ime.keyboard.Key
|
||||
@@ -39,7 +38,6 @@ import dev.patrickgold.florisboard.ime.keyboard.KeyData
|
||||
import dev.patrickgold.florisboard.ime.keyboard.computeImageVector
|
||||
import dev.patrickgold.florisboard.ime.keyboard.computeLabel
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSet
|
||||
import dev.patrickgold.florisboard.ime.smartbar.Temp
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyHintConfiguration
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKey
|
||||
@@ -454,12 +452,11 @@ class PopupUiController(
|
||||
FlorisImeUi.Attr.Mode to evaluator.keyboard.mode.toString(),
|
||||
FlorisImeUi.Attr.ShiftState to evaluator.state.inputShiftState.toString(),
|
||||
)
|
||||
Temp = !(baseRenderInfo != null || extRenderInfo != null)
|
||||
baseRenderInfo?.let { renderInfo ->
|
||||
PopupBaseBox(
|
||||
modifier = Modifier
|
||||
.requiredSize(renderInfo.bounds.size.toDpSize())
|
||||
.absoluteOffset { renderInfo.bounds.topLeft.toIntOffset() }.zIndex(100f),
|
||||
.absoluteOffset { renderInfo.bounds.topLeft.toIntOffset() },
|
||||
attributes = attributes,
|
||||
key = renderInfo.key,
|
||||
shouldIndicateExtendedPopups = renderInfo.shouldIndicateExtendedPopups && extRenderInfo == null,
|
||||
|
||||
@@ -20,6 +20,7 @@ import androidx.compose.foundation.gestures.awaitEachGesture
|
||||
import androidx.compose.foundation.gestures.awaitFirstDown
|
||||
import androidx.compose.foundation.gestures.waitForUpOrCancellation
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.width
|
||||
@@ -39,7 +40,7 @@ import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.nlp.ClipboardSuggestionCandidate
|
||||
import dev.patrickgold.florisboard.ime.nlp.SuggestionCandidate
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
@@ -61,7 +62,7 @@ val CandidatesRowScrollbarHeight = 2.dp
|
||||
|
||||
@Composable
|
||||
fun CandidatesRow(modifier: Modifier = Modifier) {
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
val nlpManager by context.nlpManager()
|
||||
|
||||
@@ -16,11 +16,8 @@
|
||||
|
||||
package dev.patrickgold.florisboard.ime.smartbar
|
||||
|
||||
import android.graphics.PixelFormat
|
||||
import android.os.Build
|
||||
import android.view.SurfaceView
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
@@ -35,18 +32,16 @@ import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.layout.positionInParent
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.view.forEach
|
||||
import dev.patrickgold.florisboard.ime.nlp.NlpInlineAutofillSuggestion
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
|
||||
import dev.patrickgold.florisboard.lib.toIntOffset
|
||||
import org.florisboard.lib.snygg.SnyggPropertySet
|
||||
import org.florisboard.lib.snygg.SnyggSinglePropertySet
|
||||
import org.florisboard.lib.snygg.ui.rememberSnyggThemeQuery
|
||||
|
||||
var CachedInlineSuggestionsChipStyleSet: SnyggSinglePropertySet? = null
|
||||
|
||||
var Temp: Boolean = false
|
||||
|
||||
@Composable
|
||||
fun InlineSuggestionsStyleCache() {
|
||||
val chipStyleSet = rememberSnyggThemeQuery(FlorisImeUi.InlineAutofillChip.elementName)
|
||||
@@ -64,16 +59,13 @@ fun InlineSuggestionsUi(
|
||||
val scrollState = rememberScrollState()
|
||||
val almostEmptyRect = remember { android.graphics.Rect(0, 0, 1, 1) }
|
||||
|
||||
val backgroundColor = rememberSnyggThemeQuery(FlorisImeUi.SmartbarCandidatesRow.elementName).background()
|
||||
|
||||
Row(
|
||||
modifier
|
||||
.fillMaxSize()
|
||||
.florisHorizontalScroll(
|
||||
state = scrollState,
|
||||
scrollbarHeight = CandidatesRowScrollbarHeight,
|
||||
)
|
||||
.background(backgroundColor),
|
||||
),
|
||||
) {
|
||||
val xMin = scrollState.value
|
||||
val xMax = scrollState.value + scrollState.viewportSize
|
||||
@@ -81,16 +73,6 @@ fun InlineSuggestionsUi(
|
||||
if (inlineSuggestion.view == null) {
|
||||
continue
|
||||
}
|
||||
//inlineSuggestion.view.background = ColorDrawable(backgroundColor.toArgb())
|
||||
inlineSuggestion.view.forEach {
|
||||
with (it as SurfaceView) {
|
||||
//this.setBackgroundColor(backgroundColor.toArgb())
|
||||
setZOrderOnTop(false)
|
||||
holder.setFormat(PixelFormat.OPAQUE)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
var chipPos by remember { mutableStateOf(IntOffset.Zero) }
|
||||
AndroidView(
|
||||
modifier = Modifier.onGloballyPositioned { chipPos = it.positionInParent().toIntOffset() },
|
||||
|
||||
@@ -24,6 +24,7 @@ import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.absoluteOffset
|
||||
@@ -42,7 +43,6 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
@@ -52,7 +52,7 @@ import androidx.compose.ui.graphics.isUnspecified
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.nlp.NlpInlineAutofill
|
||||
import dev.patrickgold.florisboard.ime.smartbar.quickaction.QuickActionButton
|
||||
@@ -65,7 +65,6 @@ import dev.patrickgold.florisboard.lib.compose.verticalTween
|
||||
import dev.patrickgold.florisboard.nlpManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.datastore.ui.vectorResource
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggColumn
|
||||
@@ -90,7 +89,7 @@ private val NoAnimationTween = tween<Float>(0)
|
||||
|
||||
@Composable
|
||||
fun Smartbar() {
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val smartbarEnabled by prefs.smartbar.enabled.observeAsState()
|
||||
val extendedActionsPlacement by prefs.smartbar.extendedActionsPlacement.observeAsState()
|
||||
|
||||
@@ -139,12 +138,10 @@ fun Smartbar() {
|
||||
|
||||
@Composable
|
||||
private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
val activeEvaluator by keyboardManager.activeEvaluator.collectAsState()
|
||||
val nlpManager by context.nlpManager()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val inlineSuggestions by NlpInlineAutofill.suggestions.collectAsState()
|
||||
LaunchedEffect(inlineSuggestions) {
|
||||
@@ -167,9 +164,7 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
if (/* was */ sharedActionsExpanded) {
|
||||
keyboardManager.activeState.isActionsOverflowVisible = false
|
||||
}
|
||||
scope.launch {
|
||||
prefs.smartbar.sharedActionsExpanded.set(!sharedActionsExpanded)
|
||||
}
|
||||
prefs.smartbar.sharedActionsExpanded.set(!sharedActionsExpanded)
|
||||
},
|
||||
modifier = Modifier.sizeIn(maxHeight = FlorisImeSizing.smartbarHeight).aspectRatio(1f)
|
||||
) {
|
||||
@@ -249,9 +244,7 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
if (/* was */ extendedActionsExpanded) {
|
||||
keyboardManager.activeState.isActionsOverflowVisible = false
|
||||
}
|
||||
scope.launch {
|
||||
prefs.smartbar.extendedActionsExpanded.set(!extendedActionsExpanded)
|
||||
}
|
||||
prefs.smartbar.extendedActionsExpanded.set(!extendedActionsExpanded)
|
||||
},
|
||||
modifier = Modifier.sizeIn(maxHeight = FlorisImeSizing.smartbarHeight).aspectRatio(1f)
|
||||
) {
|
||||
@@ -311,9 +304,7 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
|
||||
SideEffect {
|
||||
if (!shouldAnimate) {
|
||||
scope.launch {
|
||||
prefs.smartbar.sharedActionsExpandWithAnimation.set(true)
|
||||
}
|
||||
prefs.smartbar.sharedActionsExpandWithAnimation.set(true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,7 +359,7 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
|
||||
@Composable
|
||||
private fun SmartbarSecondaryRow(modifier: Modifier = Modifier) {
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val smartbarLayout by prefs.smartbar.layout.observeAsState()
|
||||
val secondaryRowStyle = rememberSnyggThemeQuery(FlorisImeUi.SmartbarExtendedActionsRow.elementName)
|
||||
val windowStyle = rememberSnyggThemeQuery(FlorisImeUi.Window.elementName)
|
||||
|
||||
@@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
||||
import androidx.compose.foundation.lazy.grid.LazyGridItemInfo
|
||||
@@ -39,7 +40,6 @@ import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -52,7 +52,7 @@ import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.toSize
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
@@ -60,8 +60,8 @@ import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.toIntOffset
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggButton
|
||||
import org.florisboard.lib.snygg.ui.SnyggColumn
|
||||
import org.florisboard.lib.snygg.ui.SnyggIcon
|
||||
import org.florisboard.lib.snygg.ui.SnyggIconButton
|
||||
@@ -74,9 +74,8 @@ private val DragMarkerAction = QuickAction.InsertKey(TextKeyData(code = KeyCode.
|
||||
|
||||
@Composable
|
||||
fun QuickActionsEditorPanel() {
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val keyboardManager by context.keyboardManager()
|
||||
|
||||
// We get the current arrangement once and do not observe on purpose
|
||||
@@ -236,9 +235,7 @@ fun QuickActionsEditorPanel() {
|
||||
dynamicActions.filter { it != NoopAction && it != DragMarkerAction },
|
||||
hiddenActions.filter { it != NoopAction && it != DragMarkerAction },
|
||||
)
|
||||
scope.launch {
|
||||
prefs.smartbar.actionArrangement.set(newActionArrangement)
|
||||
}
|
||||
prefs.smartbar.actionArrangement.set(newActionArrangement)
|
||||
if (keyboardManager.activeState.isActionsEditorVisible) {
|
||||
keyboardManager.activeState.isActionsEditorVisible = false
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
@@ -42,7 +42,7 @@ import org.florisboard.lib.snygg.ui.SnyggText
|
||||
|
||||
@Composable
|
||||
fun QuickActionsOverflowPanel() {
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package dev.patrickgold.florisboard.ime.smartbar.quickaction
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@@ -29,7 +28,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.smartbar.SmartbarLayout
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
@@ -38,13 +37,12 @@ import org.florisboard.lib.snygg.ui.SnyggRow
|
||||
|
||||
internal val ToggleOverflowPanelAction = QuickAction.InsertKey(TextKeyData.TOGGLE_ACTIONS_OVERFLOW)
|
||||
|
||||
@SuppressLint("UnusedBoxWithConstraintsScope")
|
||||
@Composable
|
||||
fun QuickActionsRow(
|
||||
elementName: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) = with(LocalDensity.current) {
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.smartbar.IncognitoDisplayMode
|
||||
import dev.patrickgold.florisboard.ime.smartbar.InlineSuggestionsStyleCache
|
||||
import dev.patrickgold.florisboard.ime.smartbar.Smartbar
|
||||
@@ -49,7 +49,7 @@ fun TextInputLayout(
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
|
||||
val state by keyboardManager.activeState.collectAsState()
|
||||
val evaluator by keyboardManager.activeEvaluator.collectAsState()
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package dev.patrickgold.florisboard.ime.text.gestures
|
||||
|
||||
import android.content.Context
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.nlp.WordSuggestionCandidate
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKey
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
@@ -39,7 +39,7 @@ class GlideTypingManager(context: Context) : GlideTypingGesture.Listener {
|
||||
private const val MAX_SUGGESTION_COUNT = 8
|
||||
}
|
||||
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val keyboardManager by context.keyboardManager()
|
||||
private val nlpManager by context.nlpManager()
|
||||
private val subtypeManager by context.subtypeManager()
|
||||
|
||||
@@ -19,7 +19,7 @@ package dev.patrickgold.florisboard.ime.text.gestures
|
||||
import android.view.MotionEvent
|
||||
import android.view.VelocityTracker
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.lib.Pointer
|
||||
import dev.patrickgold.florisboard.lib.PointerMap
|
||||
import dev.patrickgold.florisboard.lib.devtools.LogTopic
|
||||
@@ -39,7 +39,7 @@ abstract class SwipeGesture {
|
||||
* @property listener The listener to report detected swipes to.
|
||||
*/
|
||||
class Detector(private val listener: Listener) {
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val prefs by florisPreferenceModel()
|
||||
|
||||
var isEnabled: Boolean = true
|
||||
private var pointerMap: PointerMap<GesturePointer> = PointerMap { GesturePointer() }
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package dev.patrickgold.florisboard.ime.text.keyboard
|
||||
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.keyboard.AbstractKeyData
|
||||
import dev.patrickgold.florisboard.ime.keyboard.ComputingEvaluator
|
||||
import dev.patrickgold.florisboard.ime.keyboard.Key
|
||||
@@ -246,7 +246,7 @@ class TextKey(override val data: AbstractKeyData) : Key(data) {
|
||||
else -> null
|
||||
}
|
||||
} else if (!data.isSpaceKey() || data.type == KeyType.NUMERIC) {
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
computedPopups.getPopupKeys(prefs.keyboard.keyHintConfiguration()).hint.let { hintData ->
|
||||
if (hintData?.isSpaceKey() == false) {
|
||||
hintedLabel = hintData.asString(isForDisplay = true)
|
||||
|
||||
@@ -56,7 +56,7 @@ import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.toSize
|
||||
import dev.patrickgold.florisboard.FlorisImeService
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
import dev.patrickgold.florisboard.glideTypingManager
|
||||
import dev.patrickgold.florisboard.ime.input.InputEventDispatcher
|
||||
@@ -104,7 +104,7 @@ fun TextKeyboardLayout(
|
||||
evaluator: ComputingEvaluator,
|
||||
isPreview: Boolean = false,
|
||||
): Unit = with(LocalDensity.current) {
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val context = LocalContext.current
|
||||
val configuration = LocalConfiguration.current
|
||||
val glideTypingManager by context.glideTypingManager()
|
||||
@@ -132,7 +132,7 @@ fun TextKeyboardLayout(
|
||||
controller.onTouchEventInternal(event)
|
||||
controller.popupUiController.hide()
|
||||
event.recycle()
|
||||
} catch (_: Throwable) {
|
||||
} catch (e: Throwable) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
@@ -337,7 +337,7 @@ private fun TextKeyButton(
|
||||
key.label?.let { label ->
|
||||
var customLabel = label
|
||||
if (key.computedData.code == KeyCode.SPACE) {
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val spaceBarMode by prefs.keyboard.spaceBarMode.observeAsState()
|
||||
when (spaceBarMode) {
|
||||
SpaceBarMode.NOTHING -> return@let
|
||||
@@ -385,7 +385,7 @@ private fun TextKeyButton(
|
||||
private class TextKeyboardLayoutController(
|
||||
context: Context,
|
||||
) : SwipeGesture.Listener, GlideTypingGesture.Listener {
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val editorInstance by context.editorInstance()
|
||||
private val keyboardManager by context.keyboardManager()
|
||||
|
||||
|
||||
@@ -27,10 +27,13 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
import dev.patrickgold.florisboard.themeManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.snygg.SnyggAttributes
|
||||
import org.florisboard.lib.snygg.SnyggQueryAttributes
|
||||
import org.florisboard.lib.snygg.ui.ProvideSnyggTheme
|
||||
import org.florisboard.lib.snygg.ui.rememberSnyggTheme
|
||||
|
||||
@@ -49,10 +52,10 @@ fun FlorisImeTheme(content: @Composable () -> Unit) {
|
||||
val keyboardManager by context.keyboardManager()
|
||||
val themeManager by context.themeManager()
|
||||
|
||||
val prefs by FlorisPreferenceStore
|
||||
val prefs by florisPreferenceModel()
|
||||
val accentColor by prefs.theme.accentColor.observeAsState()
|
||||
|
||||
val activeThemeInfo by themeManager.activeThemeInfo.collectAsState()
|
||||
val activeThemeInfo by themeManager.activeThemeInfo.observeAsNonNullState()
|
||||
val activeConfig = remember(activeThemeInfo) { activeThemeInfo.config }
|
||||
val activeStyle = remember(activeThemeInfo) { activeThemeInfo.stylesheet }
|
||||
|
||||
|
||||
@@ -39,8 +39,10 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.ime.smartbar.CachedInlineSuggestionsChipStyleSet
|
||||
@@ -50,67 +52,70 @@ import dev.patrickgold.florisboard.lib.ext.ExtensionMeta
|
||||
import dev.patrickgold.florisboard.lib.io.ZipUtils
|
||||
import dev.patrickgold.florisboard.lib.util.TimeUtils.javaLocalTime
|
||||
import dev.patrickgold.florisboard.lib.util.ViewUtils
|
||||
import java.time.LocalTime
|
||||
import java.util.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import org.florisboard.lib.kotlin.collectIn
|
||||
import org.florisboard.lib.kotlin.io.FsDir
|
||||
import org.florisboard.lib.kotlin.io.deleteContentsRecursively
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import org.florisboard.lib.snygg.SnyggStylesheet
|
||||
import org.florisboard.lib.snygg.value.SnyggStaticColorValue
|
||||
import java.time.LocalTime
|
||||
import java.util.*
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
/**
|
||||
* Core class which manages the keyboard theme. Note, that this does not affect the UI theme of the
|
||||
* Settings Activities.
|
||||
*/
|
||||
class ThemeManager(context: Context) {
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val appContext by context.appContext()
|
||||
private val extensionManager by context.extensionManager()
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
|
||||
|
||||
private val _indexedThemeConfigs = MutableStateFlow(mapOf<ExtensionComponentName, ThemeExtensionComponent>())
|
||||
val indexedThemeConfigs get() = _indexedThemeConfigs.asStateFlow()
|
||||
val previewThemeId = MutableStateFlow<ExtensionComponentName?>(null)
|
||||
val previewThemeInfo = MutableStateFlow<ThemeInfo?>(null)
|
||||
val wallpaperChangedCounter = MutableStateFlow(0)
|
||||
private val _indexedThemeConfigs = MutableLiveData(mapOf<ExtensionComponentName, ThemeExtensionComponent>())
|
||||
val indexedThemeConfigs: LiveData<Map<ExtensionComponentName, ThemeExtensionComponent>> get() = _indexedThemeConfigs
|
||||
var previewThemeId: ExtensionComponentName? by Delegates.observable(null) { _, _, _ ->
|
||||
updateActiveTheme()
|
||||
}
|
||||
var previewThemeInfo: ThemeInfo? by Delegates.observable(null) { _, _, _ ->
|
||||
updateActiveTheme()
|
||||
}
|
||||
|
||||
private val cachedThemeInfos = mutableListOf<ThemeInfo>()
|
||||
private val activeThemeGuard = Mutex(locked = false)
|
||||
private val _activeThemeInfo = MutableStateFlow(ThemeInfo.DEFAULT)
|
||||
val activeThemeInfo get() = _activeThemeInfo.asStateFlow()
|
||||
private val _activeThemeInfo = MutableLiveData(ThemeInfo.DEFAULT)
|
||||
val activeThemeInfo: LiveData<ThemeInfo> get() = _activeThemeInfo
|
||||
|
||||
init {
|
||||
extensionManager.themes.observeForever { themeExtensions ->
|
||||
_indexedThemeConfigs.value = buildMap {
|
||||
val map = buildMap {
|
||||
for (themeExtension in themeExtensions) {
|
||||
for (themeComponent in themeExtension.themes) {
|
||||
put(ExtensionComponentName(themeExtension.meta.id, themeComponent.id), themeComponent)
|
||||
}
|
||||
}
|
||||
}
|
||||
_indexedThemeConfigs.postValue(map)
|
||||
}
|
||||
indexedThemeConfigs.collectIn(scope) {
|
||||
updateActiveTheme { cachedThemeInfos.clear() }
|
||||
indexedThemeConfigs.observeForever {
|
||||
updateActiveTheme {
|
||||
cachedThemeInfos.clear()
|
||||
}
|
||||
}
|
||||
combine(
|
||||
prefs.theme.mode.asFlow(),
|
||||
prefs.theme.dayThemeId.asFlow(),
|
||||
prefs.theme.nightThemeId.asFlow(),
|
||||
previewThemeId,
|
||||
previewThemeInfo,
|
||||
wallpaperChangedCounter,
|
||||
) {}.collectIn(scope) {
|
||||
prefs.theme.mode.observeForever {
|
||||
updateActiveTheme()
|
||||
}
|
||||
prefs.theme.dayThemeId.observeForever {
|
||||
updateActiveTheme()
|
||||
}
|
||||
prefs.theme.nightThemeId.observeForever {
|
||||
updateActiveTheme()
|
||||
}
|
||||
}
|
||||
@@ -119,54 +124,56 @@ class ThemeManager(context: Context) {
|
||||
* Updates the current theme ref and loads the corresponding theme, as well as notifies all
|
||||
* callback receivers about the new theme.
|
||||
*/
|
||||
suspend fun updateActiveTheme(action: () -> Unit = { }) = activeThemeGuard.withLock {
|
||||
action()
|
||||
previewThemeInfo.value?.let { previewThemeInfo ->
|
||||
_activeThemeInfo.value = previewThemeInfo
|
||||
return@withLock
|
||||
fun updateActiveTheme(action: () -> Unit = { }) = scope.launch {
|
||||
activeThemeGuard.withLock {
|
||||
action()
|
||||
previewThemeInfo?.let { previewThemeInfo ->
|
||||
_activeThemeInfo.postValue(previewThemeInfo)
|
||||
return@withLock
|
||||
}
|
||||
val activeName = evaluateActiveThemeName()
|
||||
val cachedInfo = cachedThemeInfos.find { it.name == activeName }
|
||||
if (cachedInfo != null) {
|
||||
_activeThemeInfo.postValue(cachedInfo)
|
||||
return@withLock
|
||||
}
|
||||
val themeExt = extensionManager.getExtensionById(activeName.extensionId) as? ThemeExtension
|
||||
val themeExtRef = themeExt?.sourceRef
|
||||
if (themeExtRef == null) {
|
||||
return@withLock
|
||||
}
|
||||
val themeConfig = themeExt.themes.find { it.id == activeName.componentId }
|
||||
if (themeConfig == null) {
|
||||
return@withLock
|
||||
}
|
||||
// TODO: loaded dir is implemented already...
|
||||
// TODO: this leaks the loaded dir, but at least the state is not kaputt from compose viewpoint
|
||||
val loadedDir = appContext.cacheDir.subDir("loaded").subDir(UUID.randomUUID().toString())
|
||||
runCatching {
|
||||
loadedDir.mkdirs()
|
||||
loadedDir.deleteContentsRecursively()
|
||||
ZipUtils.unzip(appContext, themeExtRef, loadedDir).getOrThrow()
|
||||
flogInfo { "Loaded extension ${themeExt.meta.id} into $loadedDir" }
|
||||
val stylesheetFile = loadedDir.subFile(themeConfig.stylesheetPath())
|
||||
val stylesheetJson = stylesheetFile.readText()
|
||||
SnyggStylesheet.fromJson(stylesheetJson).getOrThrow()
|
||||
}.fold(
|
||||
onSuccess = { newStylesheet ->
|
||||
val newInfo = ThemeInfo(activeName, themeConfig, newStylesheet, loadedDir, null)
|
||||
cachedThemeInfos.add(newInfo)
|
||||
_activeThemeInfo.postValue(newInfo)
|
||||
},
|
||||
onFailure = { cause ->
|
||||
_activeThemeInfo.postValue(ThemeInfo.DEFAULT.copy(
|
||||
loadFailure = LoadFailure(themeExt.meta, themeConfig, cause)
|
||||
))
|
||||
},
|
||||
)
|
||||
}
|
||||
val activeName = evaluateActiveThemeName()
|
||||
val cachedInfo = cachedThemeInfos.find { it.name == activeName }
|
||||
if (cachedInfo != null) {
|
||||
_activeThemeInfo.value = cachedInfo
|
||||
return@withLock
|
||||
}
|
||||
val themeExt = extensionManager.getExtensionById(activeName.extensionId) as? ThemeExtension
|
||||
val themeExtRef = themeExt?.sourceRef
|
||||
if (themeExtRef == null) {
|
||||
return@withLock
|
||||
}
|
||||
val themeConfig = themeExt.themes.find { it.id == activeName.componentId }
|
||||
if (themeConfig == null) {
|
||||
return@withLock
|
||||
}
|
||||
// TODO: loaded dir is implemented already...
|
||||
// TODO: this leaks the loaded dir, but at least the state is not kaputt from compose viewpoint
|
||||
val loadedDir = appContext.cacheDir.subDir("loaded").subDir(UUID.randomUUID().toString())
|
||||
runCatching {
|
||||
loadedDir.mkdirs()
|
||||
loadedDir.deleteContentsRecursively()
|
||||
ZipUtils.unzip(appContext, themeExtRef, loadedDir).getOrThrow()
|
||||
flogInfo { "Loaded extension ${themeExt.meta.id} into $loadedDir" }
|
||||
val stylesheetFile = loadedDir.subFile(themeConfig.stylesheetPath())
|
||||
val stylesheetJson = stylesheetFile.readText()
|
||||
SnyggStylesheet.fromJson(stylesheetJson).getOrThrow()
|
||||
}.fold(
|
||||
onSuccess = { newStylesheet ->
|
||||
val newInfo = ThemeInfo(activeName, themeConfig, newStylesheet, loadedDir, null)
|
||||
cachedThemeInfos.add(newInfo)
|
||||
_activeThemeInfo.value = newInfo
|
||||
},
|
||||
onFailure = { cause ->
|
||||
_activeThemeInfo.value = ThemeInfo.DEFAULT.copy(
|
||||
loadFailure = LoadFailure(themeExt.meta, themeConfig, cause)
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun evaluateActiveThemeName(): ExtensionComponentName {
|
||||
previewThemeId.value?.let { return it }
|
||||
previewThemeId?.let { return it }
|
||||
return when (prefs.theme.mode.get()) {
|
||||
ThemeMode.ALWAYS_DAY -> {
|
||||
prefs.theme.dayThemeId.get()
|
||||
@@ -195,9 +202,10 @@ class ThemeManager(context: Context) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new inline suggestion UI bundle.
|
||||
* Creates a new inline suggestion UI bundle based on the attributes of the given [style].
|
||||
*
|
||||
* @param context The context of the parent view/controller.
|
||||
* @param style The style set which is responsible for styling the chips.
|
||||
*
|
||||
* @return A bundle containing all necessary attributes for the inline suggestion views to properly display.
|
||||
*/
|
||||
|
||||
@@ -21,7 +21,6 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogDebug
|
||||
import dev.patrickgold.florisboard.themeManager
|
||||
import kotlinx.coroutines.flow.update
|
||||
|
||||
class WallpaperChangeReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
@@ -30,8 +29,7 @@ class WallpaperChangeReceiver : BroadcastReceiver() {
|
||||
@Suppress("DEPRECATION") // We do not retrieve the wallpaper but only listen to changes
|
||||
if (intent.action == Intent.ACTION_WALLPAPER_CHANGED) {
|
||||
flogDebug { "Wallpaper changed" }
|
||||
val themeManager by context.themeManager()
|
||||
themeManager.wallpaperChangedCounter.update { it + 1 }
|
||||
context.themeManager().value.updateActiveTheme()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,13 +16,11 @@
|
||||
|
||||
package dev.patrickgold.florisboard.lib
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisallowComposableCalls
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.SnapshotMutationPolicy
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.structuralEqualityPolicy
|
||||
@@ -30,16 +28,21 @@ import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceData
|
||||
import kotlinx.coroutines.flow.map
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceObserver
|
||||
|
||||
@SuppressLint("StateFlowValueCalledInComposition")
|
||||
@Composable
|
||||
inline fun <V : Any, R : Any> PreferenceData<V>.observeAsTransformingState(
|
||||
policy: SnapshotMutationPolicy<R> = structuralEqualityPolicy(),
|
||||
crossinline transform: @DisallowComposableCalls (V) -> R,
|
||||
): State<R> {
|
||||
return asFlow().let { flow ->
|
||||
flow.map { transform(it) }.collectAsState(transform(flow.value))
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
val state = remember(key) { mutableStateOf(transform(get()), policy) }
|
||||
DisposableEffect(this, lifecycleOwner) {
|
||||
val observer = PreferenceObserver<V> { newValue -> state.value = transform(newValue) }
|
||||
observe(lifecycleOwner, observer)
|
||||
onDispose { removeObserver(observer) }
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -37,9 +37,9 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.AppPrefs
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceLayout
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceUiContent
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
@@ -53,7 +53,7 @@ fun FlorisScreen(builder: @Composable FlorisScreenScope.() -> Unit) {
|
||||
|
||||
typealias FlorisScreenActions = @Composable RowScope.() -> Unit
|
||||
typealias FlorisScreenBottomBar = @Composable () -> Unit
|
||||
typealias FlorisScreenContent = PreferenceUiContent<FlorisPreferenceModel>
|
||||
typealias FlorisScreenContent = PreferenceUiContent<AppPrefs>
|
||||
typealias FlorisScreenFab = @Composable () -> Unit
|
||||
typealias FlorisScreenNavigationIcon = @Composable () -> Unit
|
||||
|
||||
@@ -152,7 +152,7 @@ private class FlorisScreenScopeImpl : FlorisScreenScope {
|
||||
Modifier
|
||||
}
|
||||
PreferenceLayout(
|
||||
FlorisPreferenceStore,
|
||||
florisPreferenceModel(),
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxWidth()
|
||||
|
||||
@@ -56,7 +56,6 @@ import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import dev.patrickgold.florisboard.lib.util.InputMethodUtils
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
|
||||
private const val AnimationDuration = 200
|
||||
|
||||
@@ -116,7 +115,7 @@ fun PreviewKeyboardField(
|
||||
Row {
|
||||
IconButton(onClick = {
|
||||
if (!InputMethodUtils.showImePicker(context)) {
|
||||
context.showShortToastSync("Error: InputMethodManager service not available!")
|
||||
context.showShortToast("Error: InputMethodManager service not available!")
|
||||
}
|
||||
}) {
|
||||
Icon(
|
||||
|
||||
@@ -23,35 +23,22 @@ import android.inputmethodservice.InputMethodService
|
||||
import android.view.Window
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.graphics.luminance
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.snygg.ui.rememberSnyggThemeQuery
|
||||
import org.florisboard.lib.snygg.ui.uriOrNull
|
||||
|
||||
@Composable
|
||||
fun SystemUiIme() {
|
||||
val backgroundQuery = rememberSnyggThemeQuery(FlorisImeUi.Window.elementName)
|
||||
val backgroundColor = backgroundQuery.background()
|
||||
val backgroundImage = backgroundQuery.backgroundImage.uriOrNull()
|
||||
|
||||
val hasBackgroundImage = backgroundImage != null
|
||||
val useDarkIcons = if (backgroundImage == null) {
|
||||
backgroundColor.luminance() >= 0.5
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
val useDarkIcons = !FlorisImeTheme.config.isNightTheme
|
||||
val view = LocalView.current
|
||||
val window = view.context.findWindow()!!
|
||||
val windowInsetsController = WindowInsetsControllerCompat(window, view)
|
||||
|
||||
LaunchedEffect(useDarkIcons, hasBackgroundImage) {
|
||||
LaunchedEffect(useDarkIcons) {
|
||||
windowInsetsController.isAppearanceLightNavigationBars = useDarkIcons
|
||||
if (AndroidVersion.ATLEAST_API29_Q) {
|
||||
window.isNavigationBarContrastEnforced = hasBackgroundImage
|
||||
window.isNavigationBarContrastEnforced = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ import android.widget.Toolbar
|
||||
import androidx.activity.ComponentActivity
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.AppPrefs
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import org.florisboard.lib.android.stringRes
|
||||
import dev.patrickgold.florisboard.lib.devtools.Devtools
|
||||
import dev.patrickgold.florisboard.lib.devtools.LogTopic
|
||||
@@ -39,14 +39,14 @@ import dev.patrickgold.florisboard.lib.devtools.flogWarning
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
private class SafePreferenceInstanceWrapper : ReadOnlyProperty<Any?, FlorisPreferenceModel?> {
|
||||
private class SafePreferenceInstanceWrapper : ReadOnlyProperty<Any?, AppPrefs?> {
|
||||
val cachedPreferenceModel = try {
|
||||
FlorisPreferenceStore
|
||||
florisPreferenceModel()
|
||||
} catch (_: Throwable) {
|
||||
null
|
||||
}
|
||||
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): FlorisPreferenceModel? {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): AppPrefs? {
|
||||
return cachedPreferenceModel?.getValue(thisRef, property)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import android.os.Build
|
||||
import android.os.Debug
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.AppPrefs
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.lib.titlecase
|
||||
import dev.patrickgold.florisboard.lib.util.TimeUtils
|
||||
@@ -35,7 +35,7 @@ import java.io.InputStreamReader
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
object Devtools {
|
||||
fun generateDebugLog(context: Context, prefs: FlorisPreferenceModel? = null, includeLogcat: Boolean = false): String {
|
||||
fun generateDebugLog(context: Context, prefs: AppPrefs? = null, includeLogcat: Boolean = false): String {
|
||||
return buildString {
|
||||
append(generateDebugLogHeader(context, prefs))
|
||||
if (includeLogcat) {
|
||||
@@ -45,7 +45,7 @@ object Devtools {
|
||||
}
|
||||
}
|
||||
|
||||
fun generateDebugLogHeader(context: Context, prefs: FlorisPreferenceModel? = null): String {
|
||||
fun generateDebugLogHeader(context: Context, prefs: AppPrefs? = null): String {
|
||||
return buildString {
|
||||
append(generateSystemInfoLog(context))
|
||||
appendLine()
|
||||
@@ -61,7 +61,7 @@ object Devtools {
|
||||
}
|
||||
}
|
||||
|
||||
fun generateDebugLogForGithub(context: Context, prefs: FlorisPreferenceModel? = null, includeLogcat: Boolean = false): String {
|
||||
fun generateDebugLogForGithub(context: Context, prefs: AppPrefs? = null, includeLogcat: Boolean = false): String {
|
||||
return buildString {
|
||||
appendLine("<details>")
|
||||
appendLine("<summary>Detailed info (Debug log header)</summary>")
|
||||
@@ -113,7 +113,7 @@ object Devtools {
|
||||
}
|
||||
}
|
||||
|
||||
fun generateFeatureConfigLog(prefs: FlorisPreferenceModel, withTitle: Boolean = true): String {
|
||||
fun generateFeatureConfigLog(prefs: AppPrefs, withTitle: Boolean = true): String {
|
||||
return buildString {
|
||||
if (withTitle) appendLine("======= FEATURE CONFIG =======")
|
||||
append("Smartbar enabled : ").appendLine(prefs.smartbar.enabled.get())
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package dev.patrickgold.florisboard.lib.util
|
||||
|
||||
import android.content.Context
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.AppPrefs
|
||||
|
||||
object AppVersionUtils {
|
||||
private fun getRawVersionName(context: Context): String {
|
||||
@@ -28,7 +28,7 @@ object AppVersionUtils {
|
||||
}
|
||||
}
|
||||
|
||||
fun shouldShowChangelog(context: Context, prefs: FlorisPreferenceModel): Boolean {
|
||||
fun shouldShowChangelog(context: Context, prefs: AppPrefs): Boolean {
|
||||
val installVersion =
|
||||
VersionName.fromString(prefs.internal.versionOnInstall.get()) ?: VersionName.DEFAULT
|
||||
val lastChangelogVersion =
|
||||
@@ -39,14 +39,14 @@ object AppVersionUtils {
|
||||
return lastChangelogVersion < currentVersion && installVersion != currentVersion
|
||||
}
|
||||
|
||||
suspend fun updateVersionOnInstallAndLastUse(context: Context, prefs: FlorisPreferenceModel) {
|
||||
fun updateVersionOnInstallAndLastUse(context: Context, prefs: AppPrefs) {
|
||||
if (prefs.internal.versionOnInstall.get() == VersionName.DEFAULT_RAW) {
|
||||
prefs.internal.versionOnInstall.set(getRawVersionName(context))
|
||||
}
|
||||
prefs.internal.versionLastUse.set(getRawVersionName(context))
|
||||
}
|
||||
|
||||
suspend fun updateVersionLastChangelog(context: Context, prefs: FlorisPreferenceModel) {
|
||||
fun updateVersionLastChangelog(context: Context, prefs: AppPrefs) {
|
||||
prefs.internal.versionLastChangelog.set(getRawVersionName(context))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[versions]
|
||||
# Main
|
||||
android-gradle-plugin = "8.10.1"
|
||||
android-gradle-plugin = "8.9.3"
|
||||
androidx-activity = "1.10.1"
|
||||
androidx-autofill = "1.1.0"
|
||||
androidx-collection = "1.5.0"
|
||||
@@ -11,16 +11,16 @@ androidx-emoji2 = "1.5.0"
|
||||
androidx-exifinterface = "1.4.1"
|
||||
androidx-navigation = "2.9.0"
|
||||
androidx-profileinstaller = "1.4.1"
|
||||
androidx-room = "2.7.2"
|
||||
androidx-room = "2.6.1"
|
||||
cache4k = "0.7.0"
|
||||
coil = "3.2.0"
|
||||
kotlin = "2.2.0"
|
||||
kotlin = "2.1.20"
|
||||
kotlinx-coroutines = "1.10.2"
|
||||
kotlinx-serialization-json = "1.9.0"
|
||||
ksp = "2.2.0-2.0.2"
|
||||
kotlinx-serialization-json = "1.8.1"
|
||||
ksp = "2.1.20-1.0.32"
|
||||
mikepenz-aboutlibraries = "12.1.2"
|
||||
patrickgold-compose-tooltip = "0.2.0-rc02"
|
||||
patrickgold-jetpref = "0.3.0-beta01"
|
||||
patrickgold-jetpref = "0.2.0-rc04"
|
||||
|
||||
# Testing
|
||||
androidx-benchmark = "1.3.4"
|
||||
@@ -63,7 +63,6 @@ mikepenz-aboutlibraries-core = { module = "com.mikepenz:aboutlibraries-core", ve
|
||||
mikepenz-aboutlibraries-compose = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "mikepenz-aboutlibraries" }
|
||||
patrickgold-compose-tooltip = { module = "dev.patrickgold.compose.tooltip:tooltip", version.ref = "patrickgold-compose-tooltip" }
|
||||
patrickgold-jetpref-datastore-model = { module = "dev.patrickgold.jetpref:jetpref-datastore-model", version.ref = "patrickgold-jetpref" }
|
||||
patrickgold-jetpref-datastore-model-processor = { module = "dev.patrickgold.jetpref:jetpref-datastore-model-processor", version.ref = "patrickgold-jetpref" }
|
||||
patrickgold-jetpref-datastore-ui = { module = "dev.patrickgold.jetpref:jetpref-datastore-ui", version.ref = "patrickgold-jetpref" }
|
||||
patrickgold-jetpref-material-ui = { module = "dev.patrickgold.jetpref:jetpref-material-ui", version.ref = "patrickgold-jetpref" }
|
||||
|
||||
|
||||
@@ -19,9 +19,6 @@ package org.florisboard.lib.android
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.StringRes
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.florisboard.lib.kotlin.CurlyArg
|
||||
|
||||
/**
|
||||
@@ -29,8 +26,8 @@ import org.florisboard.lib.kotlin.CurlyArg
|
||||
*
|
||||
* @param text The text to show in the toast popup.
|
||||
*/
|
||||
suspend fun Context.showShortToast(text: String): Toast = withContext(Dispatchers.Main.immediate) {
|
||||
Toast.makeText(this@showShortToast, text, Toast.LENGTH_SHORT).also { it.show() }
|
||||
fun Context.showShortToast(text: String): Toast {
|
||||
return Toast.makeText(this, text, Toast.LENGTH_SHORT).also { it.show() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,9 +35,9 @@ suspend fun Context.showShortToast(text: String): Toast = withContext(Dispatcher
|
||||
*
|
||||
* @param id The string resource id of the text to display. Must not be 0.
|
||||
*/
|
||||
suspend fun Context.showShortToast(@StringRes id: Int): Toast {
|
||||
fun Context.showShortToast(@StringRes id: Int): Toast {
|
||||
val text = this.stringRes(id)
|
||||
return showShortToast(text)
|
||||
return Toast.makeText(this, text, Toast.LENGTH_SHORT).also { it.show() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,9 +47,9 @@ suspend fun Context.showShortToast(@StringRes id: Int): Toast {
|
||||
* @param id The string resource id of the text to display. Must not be 0.
|
||||
* @param args The curly arguments which will be filled into the string template identified by [id].
|
||||
*/
|
||||
suspend fun Context.showShortToast(@StringRes id: Int, vararg args: CurlyArg): Toast {
|
||||
fun Context.showShortToast(@StringRes id: Int, vararg args: CurlyArg): Toast {
|
||||
val text = this.stringRes(id, *args)
|
||||
return showShortToast(text)
|
||||
return Toast.makeText(this, text, Toast.LENGTH_SHORT).also { it.show() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,8 +57,8 @@ suspend fun Context.showShortToast(@StringRes id: Int, vararg args: CurlyArg): T
|
||||
*
|
||||
* @param text The text to show in the toast popup.
|
||||
*/
|
||||
suspend fun Context.showLongToast(text: String): Toast = withContext(Dispatchers.Main.immediate) {
|
||||
Toast.makeText(this@showLongToast, text, Toast.LENGTH_LONG).also { it.show() }
|
||||
fun Context.showLongToast(text: String): Toast {
|
||||
return Toast.makeText(this, text, Toast.LENGTH_LONG).also { it.show() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,9 +66,9 @@ suspend fun Context.showLongToast(text: String): Toast = withContext(Dispatchers
|
||||
*
|
||||
* @param id The string resource id of the text to display. Must not be 0.
|
||||
*/
|
||||
suspend fun Context.showLongToast(@StringRes id: Int): Toast {
|
||||
fun Context.showLongToast(@StringRes id: Int): Toast {
|
||||
val text = this.stringRes(id)
|
||||
return showLongToast(text)
|
||||
return Toast.makeText(this, text, Toast.LENGTH_LONG).also { it.show() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,63 +78,7 @@ suspend fun Context.showLongToast(@StringRes id: Int): Toast {
|
||||
* @param id The string resource id of the text to display. Must not be 0.
|
||||
* @param args The curly arguments which will be filled into the string template identified by [id].
|
||||
*/
|
||||
suspend fun Context.showLongToast(@StringRes id: Int, vararg args: CurlyArg): Toast {
|
||||
fun Context.showLongToast(@StringRes id: Int, vararg args: CurlyArg): Toast {
|
||||
val text = this.stringRes(id, *args)
|
||||
return showLongToast(text)
|
||||
return Toast.makeText(this, text, Toast.LENGTH_LONG).also { it.show() }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// These wrappers are temporary, but needed.
|
||||
// Gradually in the future all event logic will be suspendable, then these wrappers will not be needed anymore.
|
||||
// DO NOT USE THESE IN SUSPENDABLE CONTEXTS, THIS CAUSES ISSUES
|
||||
|
||||
@Deprecated(
|
||||
"Use suspend showShortToast instead",
|
||||
ReplaceWith("showShortToast(text)")
|
||||
)
|
||||
fun Context.showShortToastSync(text: String): Toast = runBlocking {
|
||||
showShortToast(text)
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
"Use suspend showShortToast instead",
|
||||
ReplaceWith("showShortToast(id)")
|
||||
)
|
||||
fun Context.showShortToastSync(@StringRes id: Int): Toast = runBlocking {
|
||||
showShortToast(id)
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
"Use suspend showShortToast instead",
|
||||
ReplaceWith("showShortToast(id, *args)")
|
||||
)
|
||||
fun Context.showShortToastSync(@StringRes id: Int, vararg args: CurlyArg): Toast = runBlocking {
|
||||
showShortToast(id, *args)
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
"Use suspend showLongToast instead",
|
||||
ReplaceWith("showLongToast(text)")
|
||||
)
|
||||
fun Context.showLongToastSync(text: String): Toast = runBlocking {
|
||||
showLongToast(text)
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
"Use suspend showLongToast instead",
|
||||
ReplaceWith("showLongToast(id)")
|
||||
)
|
||||
fun Context.showLongToastSync(@StringRes id: Int): Toast = runBlocking {
|
||||
showLongToast(id)
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
"Use suspend showLongToast instead",
|
||||
ReplaceWith("showLongToast(id, *args)")
|
||||
)
|
||||
fun Context.showLongToastSync(@StringRes id: Int, vararg args: CurlyArg): Toast = runBlocking {
|
||||
showLongToast(id, *args)
|
||||
}
|
||||
|
||||
|
||||
@@ -19,14 +19,13 @@ package org.florisboard.lib.kotlin
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.FlowCollector
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
fun <T> Flow<T>.collectIn(scope: CoroutineScope, collector: FlowCollector<T>) {
|
||||
scope.launch { collect(collector) }
|
||||
scope.launch { this@collectIn.collect(collector) }
|
||||
}
|
||||
|
||||
fun <T> StateFlow<T>.collectLatestIn(scope: CoroutineScope, action: suspend (value: T) -> Unit) {
|
||||
scope.launch { collectLatest(action) }
|
||||
fun <T> Flow<T>.collectLatestIn(scope: CoroutineScope, action: suspend (value: T) -> Unit) {
|
||||
scope.launch { this@collectLatestIn.collectLatest(action) }
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ import androidx.compose.ui.unit.takeOrElse
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.florisboard.lib.color.ColorMappings
|
||||
import org.florisboard.lib.snygg.CompiledFontFamilyData
|
||||
import org.florisboard.lib.snygg.SnyggAttributes
|
||||
import org.florisboard.lib.snygg.SnyggQueryAttributes
|
||||
import org.florisboard.lib.snygg.SnyggRule
|
||||
import org.florisboard.lib.snygg.SnyggSelector
|
||||
@@ -363,7 +364,7 @@ internal fun SnyggValue.dpSize(default: Dp = Dp.Unspecified): Dp {
|
||||
}
|
||||
}
|
||||
|
||||
fun SnyggValue.uriOrNull(): String? {
|
||||
internal fun SnyggValue.uriOrNull(): String? {
|
||||
return when (this) {
|
||||
is SnyggUriValue -> this.uri
|
||||
else -> null
|
||||
|
||||
828
libnative/flest/Cargo.lock
generated
Normal file
828
libnative/flest/Cargo.lock
generated
Normal file
@@ -0,0 +1,828 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "calendrical_calculations"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cec493b209a1b81fa32312d7ceca1b547d341c7b5f16a3edbf32b1d8b455bbdf"
|
||||
dependencies = [
|
||||
"core_maths",
|
||||
"displaydoc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core_maths"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3b02505ccb8c50b0aa21ace0fc08c3e53adebd4e58caa18a36152803c7709a3"
|
||||
dependencies = [
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "fixed_decimal"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0febbeb1118a9ecdee6e4520ead6b54882e843dd0592ad233247dbee84c53db8"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"smallvec",
|
||||
"writeable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flest"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"textutils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff5e3018d703f168b00dcefa540a65f1bbc50754ae32f3f5f0e43fe5ee51502"
|
||||
dependencies = [
|
||||
"icu_calendar",
|
||||
"icu_casemap",
|
||||
"icu_collator",
|
||||
"icu_collections",
|
||||
"icu_datetime",
|
||||
"icu_decimal",
|
||||
"icu_experimental",
|
||||
"icu_list",
|
||||
"icu_locid",
|
||||
"icu_locid_transform",
|
||||
"icu_normalizer",
|
||||
"icu_plurals",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"icu_segmenter",
|
||||
"icu_timezone",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_calendar"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7265b2137f9a36f7634a308d91f984574bbdba8cfd95ceffe1c345552275a8ff"
|
||||
dependencies = [
|
||||
"calendrical_calculations",
|
||||
"displaydoc",
|
||||
"icu_calendar_data",
|
||||
"icu_locid",
|
||||
"icu_locid_transform",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_calendar_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e009b7f0151ee6fb28c40b1283594397e0b7183820793e9ace3dcd13db126d0"
|
||||
|
||||
[[package]]
|
||||
name = "icu_casemap"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ff0c8ae9f8d31b12e27fc385ff9ab1f3cd9b17417c665c49e4ec958c37da75f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_casemap_data",
|
||||
"icu_collections",
|
||||
"icu_locid",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_casemap_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d57966d5ab748f74513be4046867f9a20e801e2775d41f91d04a0f560b61f08"
|
||||
|
||||
[[package]]
|
||||
name = "icu_collator"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d370371887d31d56f361c3eaa15743e54f13bc677059c9191c77e099ed6966b2"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collator_data",
|
||||
"icu_collections",
|
||||
"icu_locid_transform",
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"utf16_iter",
|
||||
"utf8_iter",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_collator_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ee3f88741364b7d6269cce6827a3e6a8a2cf408a78f766c9224ab479d5e4ae5"
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_datetime"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d115efb85e08df3fd77e77f52e7e087545a783fffba8be80bfa2102f306b1780"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"either",
|
||||
"fixed_decimal",
|
||||
"icu_calendar",
|
||||
"icu_datetime_data",
|
||||
"icu_decimal",
|
||||
"icu_locid",
|
||||
"icu_locid_transform",
|
||||
"icu_plurals",
|
||||
"icu_provider",
|
||||
"icu_timezone",
|
||||
"smallvec",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_datetime_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ba7e7f7a01269b9afb0a39eff4f8676f693b55f509b3120e43a0350a9f88bea"
|
||||
|
||||
[[package]]
|
||||
name = "icu_decimal"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb8fd98f86ec0448d85e1edf8884e4e318bb2e121bd733ec929a05c0a5e8b0eb"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"fixed_decimal",
|
||||
"icu_decimal_data",
|
||||
"icu_locid_transform",
|
||||
"icu_provider",
|
||||
"writeable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_decimal_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d424c994071c6f5644f999925fc868c85fec82295326e75ad5017bc94b41523"
|
||||
|
||||
[[package]]
|
||||
name = "icu_experimental"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "844ad7b682a165c758065d694bc4d74ac67f176da1c499a04d85d492c0f193b7"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"fixed_decimal",
|
||||
"icu_collections",
|
||||
"icu_decimal",
|
||||
"icu_experimental_data",
|
||||
"icu_locid",
|
||||
"icu_locid_transform",
|
||||
"icu_normalizer",
|
||||
"icu_pattern",
|
||||
"icu_plurals",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"litemap",
|
||||
"num-bigint",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
"smallvec",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerofrom",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_experimental_data"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c178b9a34083fca5bd70d61f647575335e9c197d0f30c38e8ccd187babc69d0"
|
||||
|
||||
[[package]]
|
||||
name = "icu_list"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbfeda1d7775b6548edd4e8b7562304a559a91ed56ab56e18961a053f367c365"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_list_data",
|
||||
"icu_locid_transform",
|
||||
"icu_provider",
|
||||
"regex-automata 0.2.0",
|
||||
"writeable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_list_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1825170d2c6679cb20dbd96a589d034e49f698aed9a2ef4fafc9a0101ed298f"
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid_transform"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_locid_transform_data",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid_transform_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"utf16_iter",
|
||||
"utf8_iter",
|
||||
"write16",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
|
||||
|
||||
[[package]]
|
||||
name = "icu_pattern"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7f36aafd098d6717de34e668a8120822275c1fba22b936e757b7de8a2fd7e4"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"either",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_plurals"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba5a70e7c025dbd5c501b0a5c188cd11666a424f0dadcd4f0a95b7dafde3b114"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"fixed_decimal",
|
||||
"icu_locid_transform",
|
||||
"icu_plurals_data",
|
||||
"icu_provider",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_plurals_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e3e8f775b215d45838814a090a2227247a7431d74e9156407d9c37f6ef0f208"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locid_transform",
|
||||
"icu_properties_data",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_provider_macros",
|
||||
"stable_deref_trait",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider_macros"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_segmenter"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a717725612346ffc2d7b42c94b820db6908048f39434504cb130e8b46256b0de"
|
||||
dependencies = [
|
||||
"core_maths",
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locid",
|
||||
"icu_provider",
|
||||
"icu_segmenter_data",
|
||||
"utf8_iter",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_segmenter_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f739ee737260d955e330bc83fdeaaf1631f7fb7ed218761d3c04bb13bb7d79df"
|
||||
|
||||
[[package]]
|
||||
name = "icu_timezone"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa91ba6a585939a020c787235daa8aee856d9bceebd6355e283c0c310bc6de96"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_calendar",
|
||||
"icu_provider",
|
||||
"icu_timezone_data",
|
||||
"tinystr",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_timezone_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c588878c508a3e2ace333b3c50296053e6483c6a7541251b546cc59dcd6ced8e"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
|
||||
|
||||
[[package]]
|
||||
name = "linkify"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1dfa36d52c581e9ec783a7ce2a5e0143da6237be5811a0b3153fedfdbe9f780"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||
dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.4.8",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9368763f5a9b804326f3af749e16f9abf378d227bcdee7634b13d8f17793782"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.210"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.210"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textutils"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"icu",
|
||||
"icu_segmenter",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"linkify",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"unicase",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf16_iter"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "write16"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb594dd55d87335c5f60177cee24f19457a5ec10a065e0a3014722ad252d0a1f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
7
libnative/flest/Cargo.toml
Normal file
7
libnative/flest/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "flest"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
textutils = { path = "../textutils" }
|
||||
125
libnative/flest/src/candidates.rs
Normal file
125
libnative/flest/src/candidates.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Candidate {
|
||||
pub text: String,
|
||||
pub secondary_text: Option<String>,
|
||||
pub confidence: u8,
|
||||
}
|
||||
|
||||
impl PartialOrd for Candidate {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
// Reverse ordering
|
||||
other.confidence.partial_cmp(&self.confidence)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Candidate {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
// Reverse ordering
|
||||
other.confidence.cmp(&self.confidence)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CandidateQueue {
|
||||
entries: Vec<Candidate>,
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
impl CandidateQueue {
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
let capacity = capacity.max(1);
|
||||
CandidateQueue {
|
||||
entries: Vec::with_capacity(capacity),
|
||||
capacity,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, text: String, confidence: f64) {
|
||||
if confidence.is_nan() {
|
||||
return;
|
||||
}
|
||||
|
||||
let confidence = confidence.clamp(0.0, 1.0);
|
||||
let confidence = ((u8::MAX as f64) * confidence) as u8;
|
||||
let entry = Candidate { text, secondary_text: None, confidence };
|
||||
|
||||
if self.entries.is_empty() {
|
||||
self.entries.push(entry);
|
||||
return;
|
||||
}
|
||||
|
||||
let existing_entry = self.entries.iter_mut().find(|it| it.text == entry.text);
|
||||
if let Some(entry) = existing_entry {
|
||||
entry.confidence = entry.confidence.max(confidence);
|
||||
} else {
|
||||
if self.entries.len() < self.capacity {
|
||||
self.entries.push(entry);
|
||||
} else {
|
||||
let last = &mut self.entries[self.capacity - 1];
|
||||
if last.confidence < entry.confidence {
|
||||
*last = entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.entries.sort();
|
||||
}
|
||||
|
||||
pub fn into_sorted_vec(self) -> Vec<Candidate> {
|
||||
self.entries
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn basic_insertions() {
|
||||
let mut queue = CandidateQueue::with_capacity(3);
|
||||
queue.push("foo".to_string(), 0.5);
|
||||
queue.push("bar".to_string(), 0.7);
|
||||
queue.push("baz".to_string(), 0.6);
|
||||
queue.push("qux".to_string(), 0.8);
|
||||
queue.push("quux".to_string(), 0.9);
|
||||
let vec = queue.into_sorted_vec();
|
||||
assert_eq!(vec.len(), 3);
|
||||
assert_eq!(vec[0].text, "quux");
|
||||
assert_eq!(vec[1].text, "qux");
|
||||
assert_eq!(vec[2].text, "bar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_insertions_with_duplicates() {
|
||||
let mut queue = CandidateQueue::with_capacity(3);
|
||||
queue.push("foo".to_string(), 0.5);
|
||||
queue.push("bar".to_string(), 0.7);
|
||||
queue.push("baz".to_string(), 0.6);
|
||||
queue.push("qux".to_string(), 0.8);
|
||||
queue.push("quux".to_string(), 0.9);
|
||||
queue.push("quux".to_string(), 0.9);
|
||||
let vec = queue.into_sorted_vec();
|
||||
assert_eq!(vec.len(), 3);
|
||||
assert_eq!(vec[0].text, "quux");
|
||||
assert_eq!(vec[1].text, "qux");
|
||||
assert_eq!(vec[2].text, "bar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_candidate_set() {
|
||||
let queue = CandidateQueue::with_capacity(3);
|
||||
let vec = queue.into_sorted_vec();
|
||||
assert_eq!(vec.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nan_confidence_insertions() {
|
||||
let mut queue = CandidateQueue::with_capacity(3);
|
||||
queue.push("foo".to_string(), 0.5);
|
||||
queue.push("bar".to_string(), f64::NAN);
|
||||
queue.push("baz".to_string(), 0.6);
|
||||
let vec = queue.into_sorted_vec();
|
||||
assert_eq!(vec.len(), 2);
|
||||
assert_eq!(vec[0].text, "baz");
|
||||
assert_eq!(vec[1].text, "foo");
|
||||
}
|
||||
}
|
||||
148
libnative/flest/src/dyntrie.rs
Normal file
148
libnative/flest/src/dyntrie.rs
Normal file
@@ -0,0 +1,148 @@
|
||||
use core::fmt;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub const TOKEN_SEPARATOR: char = '\u{00}';
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DynTrieNode<T> {
|
||||
children: HashMap<char, DynTrieNode<T>>,
|
||||
pub value: Option<T>,
|
||||
}
|
||||
|
||||
impl<T: Default> DynTrieNode<T> {
|
||||
pub fn new() -> Self {
|
||||
DynTrieNode {
|
||||
children: HashMap::new(),
|
||||
value: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn traverse(&self, c: char) -> Option<&DynTrieNode<T>> {
|
||||
return self.children.get(&c);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn traverse_mut(&mut self, c: char) -> Option<&mut DynTrieNode<T>> {
|
||||
return self.children.get_mut(&c);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn traverse_or_insert(&mut self, c: char) -> &mut DynTrieNode<T> {
|
||||
return self.children.entry(c).or_insert_with(|| DynTrieNode::default());
|
||||
}
|
||||
|
||||
pub fn get(&self, token: &Vec<char>) -> Option<&DynTrieNode<T>> {
|
||||
let mut node = self;
|
||||
for c in token {
|
||||
node = node.traverse(*c)?;
|
||||
}
|
||||
return Some(node);
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, token: &Vec<char>) -> Option<&mut DynTrieNode<T>> {
|
||||
let mut node = self;
|
||||
for c in token {
|
||||
node = node.traverse_mut(*c)?;
|
||||
}
|
||||
return Some(node);
|
||||
}
|
||||
|
||||
pub fn get_or_insert(&mut self, token: &Vec<char>) -> &mut DynTrieNode<T> {
|
||||
let mut node = self;
|
||||
for c in token {
|
||||
node = node.traverse_or_insert(*c);
|
||||
}
|
||||
if node.value.is_none() {
|
||||
node.value = Some(T::default());
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
pub fn get_ngram(&self, ngram: &[Vec<char>]) -> Option<&DynTrieNode<T>> {
|
||||
let mut node = self;
|
||||
for (i, token) in ngram.iter().enumerate() {
|
||||
if i > 0 {
|
||||
node = node.traverse(TOKEN_SEPARATOR)?;
|
||||
}
|
||||
node = node.get(token)?;
|
||||
}
|
||||
return Some(node);
|
||||
}
|
||||
|
||||
pub fn get_ngram_mut(&mut self, ngram: &[Vec<char>]) -> Option<&mut DynTrieNode<T>> {
|
||||
let mut node = self;
|
||||
for (i, token) in ngram.iter().enumerate() {
|
||||
if i > 0 {
|
||||
node = node.traverse_mut(TOKEN_SEPARATOR)?;
|
||||
}
|
||||
node = node.get_mut(token)?;
|
||||
}
|
||||
return Some(node);
|
||||
}
|
||||
|
||||
pub fn get_ngram_or_insert(&mut self, ngram: &[Vec<char>]) -> &mut DynTrieNode<T> {
|
||||
let mut node = self;
|
||||
for (i, token) in ngram.iter().enumerate() {
|
||||
if i > 0 {
|
||||
node = node.traverse_or_insert(TOKEN_SEPARATOR);
|
||||
}
|
||||
node = node.get_or_insert(token);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
pub fn for_each<F>(&self, f: &F) where F: Fn(&Vec<char>, &DynTrieNode<T>) {
|
||||
let mut token = Vec::with_capacity(32);
|
||||
self.for_each_recursive(&mut token, f);
|
||||
}
|
||||
|
||||
fn for_each_recursive<F>(&self, token: &mut Vec<char>, f: &F) where F: Fn(&Vec<char>, &DynTrieNode<T>) {
|
||||
if self.value.is_some() {
|
||||
f(token, self);
|
||||
}
|
||||
for (c, child) in &self.children {
|
||||
if *c == TOKEN_SEPARATOR {
|
||||
continue;
|
||||
}
|
||||
token.push(*c);
|
||||
child.for_each_recursive(token, f);
|
||||
token.pop();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_each_fnmut<F>(&self, f: &mut F) where F: FnMut(&Vec<char>, &DynTrieNode<T>) {
|
||||
let mut token = Vec::with_capacity(32);
|
||||
self.for_each_recursive_fnmut(&mut token, f);
|
||||
}
|
||||
|
||||
fn for_each_recursive_fnmut<F>(&self, token: &mut Vec<char>, f: &mut F) where F: FnMut(&Vec<char>, &DynTrieNode<T>) {
|
||||
if self.value.is_some() {
|
||||
f(token, self);
|
||||
}
|
||||
for (c, child) in &self.children {
|
||||
if *c == TOKEN_SEPARATOR {
|
||||
continue;
|
||||
}
|
||||
token.push(*c);
|
||||
child.for_each_recursive_fnmut(token, f);
|
||||
token.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default + fmt::Debug> DynTrieNode<T> {
|
||||
pub fn debug_pretty_print(&self) {
|
||||
self.debug_pretty_print_recursive(0);
|
||||
}
|
||||
|
||||
fn debug_pretty_print_recursive(&self, depth: usize) {
|
||||
for (c, child) in &self.children {
|
||||
for _ in 0..depth {
|
||||
print!(" ");
|
||||
}
|
||||
println!("{:?}: {:?}", c, child.value);
|
||||
child.debug_pretty_print_recursive(depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
4
libnative/flest/src/lib.rs
Normal file
4
libnative/flest/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
mod candidates;
|
||||
mod dyntrie;
|
||||
pub mod model;
|
||||
mod types;
|
||||
68
libnative/flest/src/model/mod.rs
Normal file
68
libnative/flest/src/model/mod.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
mod prediction;
|
||||
mod serialization;
|
||||
mod spellcheck;
|
||||
mod training;
|
||||
mod version;
|
||||
|
||||
pub use prediction::*;
|
||||
pub use serialization::*;
|
||||
pub use spellcheck::*;
|
||||
pub use training::*;
|
||||
pub use version::*;
|
||||
|
||||
use crate::dyntrie::DynTrieNode;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct NgramData {
|
||||
time: u64,
|
||||
count: u64,
|
||||
is_offensive: bool,
|
||||
// flag should only be set for 1st level words!!
|
||||
is_dictionary_word: bool,
|
||||
}
|
||||
|
||||
pub struct NgramModelMeta {
|
||||
version: NgramModelVersion,
|
||||
global_time: u64,
|
||||
global_count: u64,
|
||||
pub sentence_token: String,
|
||||
}
|
||||
|
||||
pub struct NgramModelOptions {
|
||||
pub max_candidates: usize,
|
||||
pub max_ngram_size: usize,
|
||||
pub allow_offensive: bool,
|
||||
}
|
||||
|
||||
pub struct NgramModel {
|
||||
pub trie_root: DynTrieNode<NgramData>,
|
||||
pub meta: NgramModelMeta,
|
||||
pub options: NgramModelOptions,
|
||||
}
|
||||
|
||||
impl NgramModel {
|
||||
pub fn new() -> Self {
|
||||
NgramModel {
|
||||
trie_root: DynTrieNode::new(),
|
||||
meta: NgramModelMeta {
|
||||
version: NgramModelVersion::latest(),
|
||||
global_time: 0,
|
||||
global_count: 0,
|
||||
sentence_token: "\u{1e}".to_owned(),
|
||||
},
|
||||
options: NgramModelOptions {
|
||||
max_candidates: 5,
|
||||
max_ngram_size: 3,
|
||||
allow_offensive: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NgramModelMeta {
|
||||
fn update_and_get_time(&mut self) -> u64 {
|
||||
self.global_time += 1;
|
||||
self.global_count += 1;
|
||||
return self.global_time;
|
||||
}
|
||||
}
|
||||
149
libnative/flest/src/model/prediction.rs
Normal file
149
libnative/flest/src/model/prediction.rs
Normal file
@@ -0,0 +1,149 @@
|
||||
use textutils::{fuzzy, normalization::StringNormalizationHelpers};
|
||||
|
||||
use crate::{candidates::{Candidate, CandidateQueue}, dyntrie::TOKEN_SEPARATOR};
|
||||
|
||||
use super::NgramModel;
|
||||
|
||||
impl NgramModel {
|
||||
pub fn predict(&self, partial_sentence: &Vec<&str>) -> Vec<Candidate> {
|
||||
if partial_sentence.is_empty() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let mut partial_sentence_nfd = Vec::with_capacity(partial_sentence.len() + 1);
|
||||
partial_sentence_nfd.push(self.meta.sentence_token.to_nfd_chars());
|
||||
for word in partial_sentence {
|
||||
partial_sentence_nfd.push(word.to_nfd_chars());
|
||||
}
|
||||
let curr_word_nfd = &partial_sentence_nfd.last().unwrap();
|
||||
let history_nfd = &partial_sentence_nfd[..partial_sentence_nfd.len() - 1];
|
||||
|
||||
if curr_word_nfd.is_empty() {
|
||||
return self.predict_next_word(history_nfd);
|
||||
}
|
||||
|
||||
return self.predict_curr_word(curr_word_nfd, history_nfd);
|
||||
}
|
||||
|
||||
fn predict_next_word(&self, history_nfd: &[Vec<char>]) -> Vec<Candidate> {
|
||||
let mut candidate_queue = CandidateQueue::with_capacity(self.options.max_candidates);
|
||||
let max_history_depth = (self.options.max_ngram_size - 1).min(history_nfd.len());
|
||||
|
||||
let tmax = self.meta.global_time;
|
||||
let tmin = if tmax >= 300 { tmax - 300 } else { 0 };
|
||||
let cmax = self.meta.global_count;
|
||||
let cmin = 0;
|
||||
|
||||
self.trie_root.for_each_fnmut(&mut |word, word_node| {
|
||||
let node = word_node.traverse(TOKEN_SEPARATOR);
|
||||
if node.is_none() {
|
||||
return;
|
||||
}
|
||||
let value = word_node.value.as_ref().unwrap();
|
||||
let time_conf = norm_weight(value.time, tmin, tmax);
|
||||
let count_conf = norm_weight(value.count, cmin, cmax);
|
||||
|
||||
let mut hist_node = node.unwrap();
|
||||
for hist_index in 0..max_history_depth {
|
||||
let hist_word = &history_nfd[history_nfd.len() - hist_index - 1];
|
||||
// TODO: instead of get use fuzzy get with:
|
||||
// case-insentive match and accent-insensitive match
|
||||
let hist_node_opt = hist_node.get(hist_word);
|
||||
if hist_node_opt.is_none() {
|
||||
return;
|
||||
}
|
||||
hist_node = hist_node_opt.unwrap();
|
||||
let hist_value = hist_node.value.as_ref();
|
||||
if hist_value.is_none() {
|
||||
return;
|
||||
}
|
||||
let hist_value = hist_value.unwrap();
|
||||
let hist_time_conf = norm_weight(hist_value.time, tmin, tmax);
|
||||
let hist_count_conf = norm_weight(hist_value.count, cmin, cmax);
|
||||
let hist_conf = calc_confidence(hist_time_conf, hist_count_conf, 1.0);
|
||||
let conf = calc_confidence(time_conf, count_conf, hist_conf);
|
||||
candidate_queue.push(word.iter().collect(), conf);
|
||||
|
||||
let hist_node_opt = hist_node.traverse(TOKEN_SEPARATOR);
|
||||
if hist_node_opt.is_none() {
|
||||
return;
|
||||
}
|
||||
hist_node = hist_node_opt.unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
return candidate_queue.into_sorted_vec();
|
||||
}
|
||||
|
||||
fn predict_curr_word(&self, curr_word_nfd: &Vec<char>, history_nfd: &[Vec<char>]) -> Vec<Candidate> {
|
||||
let mut candidate_queue = CandidateQueue::with_capacity(self.options.max_candidates);
|
||||
let max_history_depth = (self.options.max_ngram_size - 1).min(history_nfd.len());
|
||||
|
||||
let tmax = self.meta.global_time;
|
||||
let tmin = if tmax >= 300 { tmax - 300 } else { 0 };
|
||||
let cmax = self.meta.global_count;
|
||||
let cmin = 0;
|
||||
|
||||
// TODO: implement fuzzy_for_each_fnmut
|
||||
self.trie_root.for_each_fnmut(&mut |word, word_node| {
|
||||
// TODO: the fuzzy matcher needs to be written completely froms cratch, return a FuzzyResult instead of f64
|
||||
if fuzzy::str_match_live(word, curr_word_nfd) < 0.5 {
|
||||
return;
|
||||
}
|
||||
let node = word_node.traverse(TOKEN_SEPARATOR);
|
||||
if node.is_none() {
|
||||
return;
|
||||
}
|
||||
let value = word_node.value.as_ref().unwrap();
|
||||
let time_conf = norm_weight(value.time, tmin, tmax);
|
||||
let count_conf = norm_weight(value.count, cmin, cmax);
|
||||
|
||||
let mut hist_node = node.unwrap();
|
||||
for hist_index in 0..max_history_depth {
|
||||
let hist_word = &history_nfd[history_nfd.len() - hist_index - 1];
|
||||
// TODO: instead of get use fuzzy get with:
|
||||
// case-insentive match and accent-insensitive match
|
||||
let hist_node_opt = hist_node.get(hist_word);
|
||||
if hist_node_opt.is_none() {
|
||||
return;
|
||||
}
|
||||
hist_node = hist_node_opt.unwrap();
|
||||
let hist_value = hist_node.value.as_ref();
|
||||
if hist_value.is_none() {
|
||||
return;
|
||||
}
|
||||
let hist_value = hist_value.unwrap();
|
||||
let hist_time_conf = norm_weight(hist_value.time, tmin, tmax);
|
||||
let hist_count_conf = norm_weight(hist_value.count, cmin, cmax);
|
||||
let hist_conf = calc_confidence(hist_time_conf, hist_count_conf, 1.0);
|
||||
let conf = calc_confidence(time_conf, count_conf, hist_conf);
|
||||
candidate_queue.push(word.iter().collect(), conf);
|
||||
|
||||
let hist_node_opt = hist_node.traverse(TOKEN_SEPARATOR);
|
||||
if hist_node_opt.is_none() {
|
||||
return;
|
||||
}
|
||||
hist_node = hist_node_opt.unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
return candidate_queue.into_sorted_vec();
|
||||
}
|
||||
}
|
||||
|
||||
fn calc_confidence(time_conf: f64, count_conf: f64, hist_conf: f64) -> f64 {
|
||||
println!("time_conf: {}, count_conf: {}, hist_conf: {}", time_conf, count_conf, hist_conf);
|
||||
// TODO: count_conf is messed up
|
||||
return 0.45 * time_conf + 0.10 * count_conf + 0.45 * hist_conf;
|
||||
}
|
||||
|
||||
fn norm_weight(x: u64, xmin: u64, xmax: u64) -> f64 {
|
||||
if x <= xmin {
|
||||
return 0.0;
|
||||
}
|
||||
if x >= xmax {
|
||||
return 1.0;
|
||||
}
|
||||
let xnorm = (x - xmin) as f64 / (xmax - xmin) as f64;
|
||||
return 2.0 * xnorm - xnorm.powi(2);
|
||||
}
|
||||
19
libnative/flest/src/model/serialization.rs
Normal file
19
libnative/flest/src/model/serialization.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use crate::types::FlestResult;
|
||||
|
||||
use super::NgramModel;
|
||||
|
||||
impl NgramModel {
|
||||
pub fn from_file(path: &str) -> FlestResult<Self> {
|
||||
let mut model = NgramModel::new();
|
||||
model.load_from_file(path)?;
|
||||
return Ok(model);
|
||||
}
|
||||
|
||||
pub fn load_from_file(&mut self, path: &str) -> FlestResult<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn persist_to_file(&self, path: &str) -> FlestResult<()> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
13
libnative/flest/src/model/spellcheck.rs
Normal file
13
libnative/flest/src/model/spellcheck.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
use crate::candidates::Candidate;
|
||||
|
||||
use super::NgramModel;
|
||||
|
||||
impl NgramModel {
|
||||
fn spell(&self, curr_word: &str, history: &Vec<&str>) -> Vec<Candidate> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn spell_sentence(&self, sentence: &Vec<&str>) -> Vec<Option<Vec<Candidate>>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
41
libnative/flest/src/model/training.rs
Normal file
41
libnative/flest/src/model/training.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use textutils::normalization::StringNormalizationHelpers;
|
||||
|
||||
use super::NgramModel;
|
||||
|
||||
impl NgramModel {
|
||||
pub fn train_from_sentence(&mut self, sentence: &Vec<&str>) {
|
||||
let mut sentence_nfd = Vec::with_capacity(sentence.len() + 1);
|
||||
sentence_nfd.push(self.meta.sentence_token.to_nfd_chars());
|
||||
for word in sentence {
|
||||
sentence_nfd.push(word.to_nfd_chars());
|
||||
}
|
||||
|
||||
for sent_i in 0..sentence_nfd.len() {
|
||||
for ngram_i in 0..=(self.options.max_ngram_size - 1).clamp(0, sent_i) {
|
||||
let i = sent_i - ngram_i;
|
||||
assert!(i < sentence_nfd.len()); // catch overflow issues
|
||||
let ngram = &sentence_nfd[i..=sent_i].iter().rev().cloned().collect::<Vec<_>>();
|
||||
let node = self.trie_root.get_ngram_or_insert(ngram);
|
||||
let data = node.value.as_mut().unwrap();
|
||||
data.time = self.meta.update_and_get_time();
|
||||
data.count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn train_from_tokens(&mut self, tokens: &Vec<&str>) {
|
||||
for n in 1..=self.options.max_ngram_size {
|
||||
if n > tokens.len() {
|
||||
continue;
|
||||
}
|
||||
for i in 0..tokens.len() - n + 1 {
|
||||
let ngram = &tokens[i..(i + n)];
|
||||
let ngram = ngram.iter().rev().map(|&x| x.to_nfd_chars()).collect::<Vec<_>>();
|
||||
let node = self.trie_root.get_ngram_or_insert(ngram.as_slice());
|
||||
let data = node.value.as_mut().unwrap();
|
||||
data.time = self.meta.update_and_get_time();
|
||||
data.count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
70
libnative/flest/src/model/version.rs
Normal file
70
libnative/flest/src/model/version.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use core::fmt;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct NgramModelVersion {
|
||||
major: u8,
|
||||
minor: u8,
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! ngram_model_version {
|
||||
($major:expr, $minor:expr) => {
|
||||
NgramModelVersion { major: $major, minor: $minor }
|
||||
};
|
||||
($major:expr) => {
|
||||
NgramModelVersion { major: $major, minor: 0 }
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
impl NgramModelVersion {
|
||||
pub const vDEV: NgramModelVersion = ngram_model_version!(0, 0);
|
||||
pub const v0_1: NgramModelVersion = ngram_model_version!(0, 1);
|
||||
|
||||
pub fn latest() -> NgramModelVersion {
|
||||
NgramModelVersion::v0_1
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NgramModelVersion {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.major == 0 && self.minor == 0 {
|
||||
write!(f, "vDEV")
|
||||
} else {
|
||||
write!(f, "v{}.{}", self.major, self.minor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NgramModelVersion {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{} (0x{:02x}{:02x})", self, self.major, self.minor)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn display() {
|
||||
assert_eq!(format!("{}", ngram_model_version!(0, 1)), "v0.1");
|
||||
assert_eq!(format!("{:?}", ngram_model_version!(0, 1)), "v0.1 (0x0001)");
|
||||
assert_eq!(format!("{}", ngram_model_version!(1, 0)), "v1.0");
|
||||
assert_eq!(format!("{:?}", ngram_model_version!(1, 0)), "v1.0 (0x0100)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equality() {
|
||||
assert_eq!(ngram_model_version!(0, 1), ngram_model_version!(0, 1));
|
||||
assert_eq!(ngram_model_version!(1, 0), ngram_model_version!(1, 0));
|
||||
assert_ne!(ngram_model_version!(0, 1), ngram_model_version!(1, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comparison() {
|
||||
assert!(ngram_model_version!(0, 1) > ngram_model_version!(0, 0));
|
||||
assert!(ngram_model_version!(1, 0) > ngram_model_version!(0, 1));
|
||||
assert!(ngram_model_version!(1, 0) > ngram_model_version!(0, 42));
|
||||
}
|
||||
}
|
||||
3
libnative/flest/src/types.rs
Normal file
3
libnative/flest/src/types.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
use std::error::Error;
|
||||
|
||||
pub type FlestResult<T> = Result<T, Box<dyn Error>>;
|
||||
821
libnative/textutils/Cargo.lock
generated
Normal file
821
libnative/textutils/Cargo.lock
generated
Normal file
@@ -0,0 +1,821 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "calendrical_calculations"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cec493b209a1b81fa32312d7ceca1b547d341c7b5f16a3edbf32b1d8b455bbdf"
|
||||
dependencies = [
|
||||
"core_maths",
|
||||
"displaydoc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core_maths"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3b02505ccb8c50b0aa21ace0fc08c3e53adebd4e58caa18a36152803c7709a3"
|
||||
dependencies = [
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "fixed_decimal"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0febbeb1118a9ecdee6e4520ead6b54882e843dd0592ad233247dbee84c53db8"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"smallvec",
|
||||
"writeable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff5e3018d703f168b00dcefa540a65f1bbc50754ae32f3f5f0e43fe5ee51502"
|
||||
dependencies = [
|
||||
"icu_calendar",
|
||||
"icu_casemap",
|
||||
"icu_collator",
|
||||
"icu_collections",
|
||||
"icu_datetime",
|
||||
"icu_decimal",
|
||||
"icu_experimental",
|
||||
"icu_list",
|
||||
"icu_locid",
|
||||
"icu_locid_transform",
|
||||
"icu_normalizer",
|
||||
"icu_plurals",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"icu_segmenter",
|
||||
"icu_timezone",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_calendar"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7265b2137f9a36f7634a308d91f984574bbdba8cfd95ceffe1c345552275a8ff"
|
||||
dependencies = [
|
||||
"calendrical_calculations",
|
||||
"displaydoc",
|
||||
"icu_calendar_data",
|
||||
"icu_locid",
|
||||
"icu_locid_transform",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_calendar_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e009b7f0151ee6fb28c40b1283594397e0b7183820793e9ace3dcd13db126d0"
|
||||
|
||||
[[package]]
|
||||
name = "icu_casemap"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ff0c8ae9f8d31b12e27fc385ff9ab1f3cd9b17417c665c49e4ec958c37da75f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_casemap_data",
|
||||
"icu_collections",
|
||||
"icu_locid",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_casemap_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d57966d5ab748f74513be4046867f9a20e801e2775d41f91d04a0f560b61f08"
|
||||
|
||||
[[package]]
|
||||
name = "icu_collator"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d370371887d31d56f361c3eaa15743e54f13bc677059c9191c77e099ed6966b2"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collator_data",
|
||||
"icu_collections",
|
||||
"icu_locid_transform",
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"utf16_iter",
|
||||
"utf8_iter",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_collator_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ee3f88741364b7d6269cce6827a3e6a8a2cf408a78f766c9224ab479d5e4ae5"
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_datetime"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d115efb85e08df3fd77e77f52e7e087545a783fffba8be80bfa2102f306b1780"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"either",
|
||||
"fixed_decimal",
|
||||
"icu_calendar",
|
||||
"icu_datetime_data",
|
||||
"icu_decimal",
|
||||
"icu_locid",
|
||||
"icu_locid_transform",
|
||||
"icu_plurals",
|
||||
"icu_provider",
|
||||
"icu_timezone",
|
||||
"smallvec",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_datetime_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ba7e7f7a01269b9afb0a39eff4f8676f693b55f509b3120e43a0350a9f88bea"
|
||||
|
||||
[[package]]
|
||||
name = "icu_decimal"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb8fd98f86ec0448d85e1edf8884e4e318bb2e121bd733ec929a05c0a5e8b0eb"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"fixed_decimal",
|
||||
"icu_decimal_data",
|
||||
"icu_locid_transform",
|
||||
"icu_provider",
|
||||
"writeable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_decimal_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d424c994071c6f5644f999925fc868c85fec82295326e75ad5017bc94b41523"
|
||||
|
||||
[[package]]
|
||||
name = "icu_experimental"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "844ad7b682a165c758065d694bc4d74ac67f176da1c499a04d85d492c0f193b7"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"fixed_decimal",
|
||||
"icu_collections",
|
||||
"icu_decimal",
|
||||
"icu_experimental_data",
|
||||
"icu_locid",
|
||||
"icu_locid_transform",
|
||||
"icu_normalizer",
|
||||
"icu_pattern",
|
||||
"icu_plurals",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"litemap",
|
||||
"num-bigint",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
"smallvec",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerofrom",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_experimental_data"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c178b9a34083fca5bd70d61f647575335e9c197d0f30c38e8ccd187babc69d0"
|
||||
|
||||
[[package]]
|
||||
name = "icu_list"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbfeda1d7775b6548edd4e8b7562304a559a91ed56ab56e18961a053f367c365"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_list_data",
|
||||
"icu_locid_transform",
|
||||
"icu_provider",
|
||||
"regex-automata 0.2.0",
|
||||
"writeable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_list_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1825170d2c6679cb20dbd96a589d034e49f698aed9a2ef4fafc9a0101ed298f"
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid_transform"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_locid_transform_data",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid_transform_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"utf16_iter",
|
||||
"utf8_iter",
|
||||
"write16",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
|
||||
|
||||
[[package]]
|
||||
name = "icu_pattern"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7f36aafd098d6717de34e668a8120822275c1fba22b936e757b7de8a2fd7e4"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"either",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_plurals"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba5a70e7c025dbd5c501b0a5c188cd11666a424f0dadcd4f0a95b7dafde3b114"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"fixed_decimal",
|
||||
"icu_locid_transform",
|
||||
"icu_plurals_data",
|
||||
"icu_provider",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_plurals_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e3e8f775b215d45838814a090a2227247a7431d74e9156407d9c37f6ef0f208"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locid_transform",
|
||||
"icu_properties_data",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_provider_macros",
|
||||
"stable_deref_trait",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider_macros"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_segmenter"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a717725612346ffc2d7b42c94b820db6908048f39434504cb130e8b46256b0de"
|
||||
dependencies = [
|
||||
"core_maths",
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locid",
|
||||
"icu_provider",
|
||||
"icu_segmenter_data",
|
||||
"utf8_iter",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_segmenter_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f739ee737260d955e330bc83fdeaaf1631f7fb7ed218761d3c04bb13bb7d79df"
|
||||
|
||||
[[package]]
|
||||
name = "icu_timezone"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa91ba6a585939a020c787235daa8aee856d9bceebd6355e283c0c310bc6de96"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_calendar",
|
||||
"icu_provider",
|
||||
"icu_timezone_data",
|
||||
"tinystr",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_timezone_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c588878c508a3e2ace333b3c50296053e6483c6a7541251b546cc59dcd6ced8e"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
|
||||
|
||||
[[package]]
|
||||
name = "linkify"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1dfa36d52c581e9ec783a7ce2a5e0143da6237be5811a0b3153fedfdbe9f780"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||
dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.4.8",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9368763f5a9b804326f3af749e16f9abf378d227bcdee7634b13d8f17793782"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.210"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.210"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textutils"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"icu",
|
||||
"icu_segmenter",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"linkify",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"unicase",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf16_iter"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "write16"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb594dd55d87335c5f60177cee24f19457a5ec10a065e0a3014722ad252d0a1f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
17
libnative/textutils/Cargo.toml
Normal file
17
libnative/textutils/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "textutils"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
icu = { version = "1.5.0", features = [
|
||||
"compiled_data"
|
||||
] }
|
||||
icu_segmenter = "1.5.0"
|
||||
itertools = "0.13.0"
|
||||
lazy_static = "1.5.0"
|
||||
linkify = "0.10.0"
|
||||
once_cell = "1.20.2"
|
||||
regex = "1.11.1"
|
||||
unicase = "2.8.0"
|
||||
unicode-normalization = "0.1.24"
|
||||
20
libnative/textutils/src/filter.rs
Normal file
20
libnative/textutils/src/filter.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use lazy_static::lazy_static;
|
||||
use linkify::{self, LinkFinder};
|
||||
use regex::Regex;
|
||||
|
||||
lazy_static! {
|
||||
static ref LINK_FINDER: LinkFinder = LinkFinder::new();
|
||||
static ref REDDIT_REGEX: Regex = Regex::new(r"\/?(r\/[a-zA-Z0-9_]{3}[a-zA-Z0-9_]{0,18}|u\/[a-zA-Z0-9_-]{3}[a-zA-Z0-9_-]{0,17})").unwrap();
|
||||
}
|
||||
|
||||
pub fn preprocess_auto(text: &str) -> String {
|
||||
let mut cleaned_text = String::new();
|
||||
let mut begin_cleaned_index = 0;
|
||||
for span in LINK_FINDER.links(text) {
|
||||
cleaned_text.push_str(&text[begin_cleaned_index..span.start()]);
|
||||
begin_cleaned_index = span.end();
|
||||
}
|
||||
cleaned_text.push_str(&text[begin_cleaned_index..]);
|
||||
cleaned_text = REDDIT_REGEX.replace_all(&cleaned_text, "").to_string();
|
||||
return cleaned_text;
|
||||
}
|
||||
292
libnative/textutils/src/fuzzy.rs
Normal file
292
libnative/textutils/src/fuzzy.rs
Normal file
@@ -0,0 +1,292 @@
|
||||
use icu::properties::sets;
|
||||
|
||||
const NULLCHAR: char = '\0';
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum FuzzyMatchStrategy {
|
||||
MaxScore,
|
||||
ScoreLhs,
|
||||
ScoreRhs,
|
||||
}
|
||||
|
||||
fn str_match_impl(word1: &[char], word2: &[char], strategy: FuzzyMatchStrategy) -> f64 {
|
||||
let len1: usize = word1.len();
|
||||
let len2: usize = word2.len();
|
||||
let mut score1: f64 = 0.0;
|
||||
let mut score2: f64 = 0.0;
|
||||
let mut penalty: f64 = 0.0;
|
||||
let mut last_penalty_awarded: f64 = 0.0;
|
||||
|
||||
let mut i1: usize = 0;
|
||||
let mut i2: usize = 0;
|
||||
let mut last_ch1: char = NULLCHAR;
|
||||
let mut last_ch2: char = NULLCHAR;
|
||||
|
||||
fn next(word: &[char], i: &mut usize) -> char {
|
||||
let mut ch: char;
|
||||
loop {
|
||||
ch = *word.get(*i).unwrap_or(&NULLCHAR);
|
||||
if ch == NULLCHAR || !sets::diacritic().contains(ch) {
|
||||
break;
|
||||
}
|
||||
*i += 1;
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
while i1 < len1 && strategy != FuzzyMatchStrategy::ScoreRhs || i2 < len2 && strategy != FuzzyMatchStrategy::ScoreLhs {
|
||||
let ch1 = next(word1, &mut i1);
|
||||
let ch2 = next(word2, &mut i2);
|
||||
|
||||
if ch1 == NULLCHAR && ch2 == NULLCHAR {
|
||||
break;
|
||||
}
|
||||
let mut ch_not_null = NULLCHAR;
|
||||
if ch1 != NULLCHAR {
|
||||
score1 += 1.0;
|
||||
ch_not_null = ch1;
|
||||
}
|
||||
if ch2 != NULLCHAR {
|
||||
score2 += 1.0;
|
||||
ch_not_null = ch2;
|
||||
}
|
||||
if ch1 == NULLCHAR || ch2 == NULLCHAR {
|
||||
if !sets::diacritic().contains(ch_not_null) {
|
||||
penalty += 1.0;
|
||||
}
|
||||
i1 += 1;
|
||||
i2 += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ch1 == ch2 {
|
||||
// no penalty
|
||||
} else if ch1.to_lowercase().eq(ch2.to_lowercase()) {
|
||||
penalty += 0.1;
|
||||
} else if ch1 == last_ch2 && ch2 == last_ch1 {
|
||||
// transposition
|
||||
// reduce penalty for transpositions
|
||||
penalty -= 0.5 * last_penalty_awarded;
|
||||
} else {
|
||||
last_penalty_awarded = 1.0;
|
||||
if last_ch1 == NULLCHAR && last_ch2 == NULLCHAR {
|
||||
last_penalty_awarded += 1.0;
|
||||
}
|
||||
penalty += last_penalty_awarded;
|
||||
}
|
||||
|
||||
i1 += 1;
|
||||
i2 += 1;
|
||||
last_ch1 = ch1;
|
||||
last_ch2 = ch2;
|
||||
}
|
||||
|
||||
let mut score = match strategy {
|
||||
FuzzyMatchStrategy::ScoreLhs => score1,
|
||||
FuzzyMatchStrategy::ScoreRhs => score2,
|
||||
FuzzyMatchStrategy::MaxScore => f64::max(score1, score2),
|
||||
};
|
||||
if score == 0.0 {
|
||||
// both strings essentially empty, thus they match
|
||||
return 1.0;
|
||||
}
|
||||
score = 1.0 - penalty / score;
|
||||
return f64::max(0.0, score);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn str_match(word1: &[char], word2: &[char]) -> f64 {
|
||||
return str_match_impl(word1, word2, FuzzyMatchStrategy::MaxScore);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn str_match_live(base_word: &[char], curr_user_word: &[char]) -> f64 {
|
||||
return str_match_impl(base_word, curr_user_word, FuzzyMatchStrategy::ScoreRhs);
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::vec;
|
||||
|
||||
use crate::normalization::StringNormalizationHelpers;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn ascii_basic_match() {
|
||||
let abc = "abc".to_nfd_chars();
|
||||
|
||||
let result = str_match(&abc, &abc);
|
||||
assert_eq!(result, 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ascii_basic_mismatch() {
|
||||
let abc = "abc".to_nfd_chars();
|
||||
let def = "def".to_nfd_chars();
|
||||
|
||||
let result = str_match(&abc, &def);
|
||||
assert_eq!(result, 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ascii_casing_diff_one_char() {
|
||||
let a = "a".to_nfd_chars();
|
||||
let A = "A".to_nfd_chars();
|
||||
|
||||
let result = str_match(&a, &A);
|
||||
assert_eq!(result, 0.9);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ascii_casing_diff_multiple_chars() {
|
||||
let abc = "abc".to_nfd_chars();
|
||||
let ABC = "ABC".to_nfd_chars();
|
||||
|
||||
let result = str_match(&abc, &ABC);
|
||||
assert_eq!(result, 0.9);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diacritic_basic_match_lowercase() {
|
||||
let ae = "ä".to_nfd_chars();
|
||||
|
||||
let result = str_match(&ae, &ae);
|
||||
assert_eq!(result, 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diacritic_basic_match_uppercase() {
|
||||
let AE = "Ä".to_nfd_chars();
|
||||
|
||||
let result = str_match(&AE, &AE);
|
||||
assert_eq!(result, 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diacritic_basic_mismatch_lowercase() {
|
||||
let ae = "ä".to_nfd_chars();
|
||||
let oe = "ö".to_nfd_chars();
|
||||
|
||||
let result = str_match(&ae, &oe);
|
||||
assert_eq!(result, 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diacritic_basic_mismatch_uppercase() {
|
||||
let AE = "Ä".to_nfd_chars();
|
||||
let OE = "Ö".to_nfd_chars();
|
||||
|
||||
let result = str_match(&AE, &OE);
|
||||
assert_eq!(result, 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diacritic_casing_and_accent_diff() {
|
||||
let ae = "ä".to_nfd_chars();
|
||||
let AE = "Ä".to_nfd_chars();
|
||||
let a: Vec<char> = "a".to_nfd_chars();
|
||||
let A = "A".to_nfd_chars();
|
||||
|
||||
let result = str_match(&ae, &AE);
|
||||
assert_eq!(result, 0.9);
|
||||
|
||||
let result = str_match(&ae, &A);
|
||||
assert_eq!(result, 0.9);
|
||||
|
||||
let result = str_match(&AE, &a);
|
||||
assert_eq!(result, 0.9);
|
||||
|
||||
let result = str_match(&ae, &a);
|
||||
assert_eq!(result, 1.0);
|
||||
|
||||
let result = str_match(&AE, &A);
|
||||
assert_eq!(result, 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_match() {
|
||||
let empty = "".to_nfd_chars();
|
||||
|
||||
let result = str_match(&empty, &empty);
|
||||
assert_eq!(result, 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transposition_basic_1_start() {
|
||||
let str1 = "abxx".to_nfd_chars();
|
||||
let str2 = "baxx".to_nfd_chars();
|
||||
|
||||
let result = str_match(&str1, &str2);
|
||||
assert_eq!(result, 0.75);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transposition_basic_2_middle() {
|
||||
let str1 = "xabx".to_nfd_chars();
|
||||
let str2 = "xbax".to_nfd_chars();
|
||||
|
||||
let result = str_match(&str1, &str2);
|
||||
assert_eq!(result, 0.875);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transposition_basic_3_end() {
|
||||
let str1 = "xxab".to_nfd_chars();
|
||||
let str2 = "xxba".to_nfd_chars();
|
||||
|
||||
let result = str_match(&str1, &str2);
|
||||
assert_eq!(result, 0.875);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transposition_diactric_1_start() {
|
||||
let str1 = "äbxx".to_nfd_chars();
|
||||
let str2 = "bäxx".to_nfd_chars();
|
||||
|
||||
let result = str_match(&str1, &str2);
|
||||
assert_eq!(result, 0.75);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transposition_diactric_2_middle() {
|
||||
let str1 = "xäbx".to_nfd_chars();
|
||||
let str2 = "xbäx".to_nfd_chars();
|
||||
|
||||
let result = str_match(&str1, &str2);
|
||||
assert_eq!(result, 0.875);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transposition_diactric_3_end() {
|
||||
let str1 = "xxäb".to_nfd_chars();
|
||||
let str2 = "xxbä".to_nfd_chars();
|
||||
|
||||
let result = str_match(&str1, &str2);
|
||||
assert_eq!(result, 0.875);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unicode_normalization_basic_mismatch() {
|
||||
let ae_nfd = "ä".to_nfd_chars();
|
||||
let ae_nfc = "ä".to_nfc_chars();
|
||||
|
||||
let result = str_match(&ae_nfd, &ae_nfc);
|
||||
assert_eq!(result, 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn words_english_many() {
|
||||
let words = vec![
|
||||
("hello", "hello", 1.0),
|
||||
("hello", "hallo", 0.8),
|
||||
("hello", "helo", 0.6),
|
||||
];
|
||||
|
||||
for (word1, word2, expected_score) in words {
|
||||
let result = str_match(&word1.to_nfd_chars(), &word2.to_nfd_chars());
|
||||
assert_eq!(result, expected_score, "Mismatch for words '{}' and '{}'", word1, word2);
|
||||
}
|
||||
}
|
||||
}
|
||||
54
libnative/textutils/src/lib.rs
Normal file
54
libnative/textutils/src/lib.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
pub mod filter;
|
||||
pub mod fuzzy;
|
||||
pub mod normalization;
|
||||
pub mod properties;
|
||||
pub mod segment;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use filter::preprocess_auto;
|
||||
use icu_segmenter::{SentenceSegmenter, WordSegmenter};
|
||||
use segment::{split_sentences, split_words};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn segment_sentences_simple() {
|
||||
let text = "Hello, world! How are you? I'm fine.";
|
||||
let segmenter = SentenceSegmenter::new();
|
||||
let sentences = split_sentences(text, &segmenter);
|
||||
assert_eq!(&sentences, &["Hello, world!", "How are you?", "I'm fine."]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn segment_words_simple() {
|
||||
let text = "Hello, world! How are you? I'm fine.";
|
||||
let segmenter = WordSegmenter::new_auto();
|
||||
let words = split_words(text, &segmenter);
|
||||
assert_eq!(&words, &["Hello", "world", "How", "are", "you", "I'm", "fine"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preprocess_auto_simple() {
|
||||
let text = "Hello, world! How are you? I'm fine. https://example.com and more";
|
||||
let cleaned_text = preprocess_auto(text);
|
||||
assert_eq!(&cleaned_text, "Hello, world! How are you? I'm fine. and more");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preprocess_reddit_ids() {
|
||||
let text = "have a look at r/cats, user u/example posed a cute cat in there";
|
||||
let cleaned_text = preprocess_auto(text);
|
||||
assert_eq!(&cleaned_text, "have a look at , user posed a cute cat in there");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preprocess_url_markdown() {
|
||||
let text = "You can find an example [in the documentation](https://example.com) or on GitHub";
|
||||
let cleaned_text = preprocess_auto(text);
|
||||
assert_eq!(&cleaned_text, "You can find an example [in the documentation]() or on GitHub");
|
||||
let segmenter = WordSegmenter::new_auto();
|
||||
let words = split_words(&cleaned_text, &segmenter);
|
||||
assert_eq!(&words, &["You", "can", "find", "an", "example", "in", "the", "documentation", "or", "on", "GitHub"]);
|
||||
}
|
||||
}
|
||||
61
libnative/textutils/src/normalization.rs
Normal file
61
libnative/textutils/src/normalization.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use unicode_normalization::UnicodeNormalization;
|
||||
|
||||
pub trait StringNormalizationHelpers {
|
||||
fn to_nfd_chars(&self) -> Vec<char>;
|
||||
|
||||
fn to_nfd_string(&self) -> String;
|
||||
|
||||
fn to_nfc_chars(&self) -> Vec<char>;
|
||||
|
||||
fn to_nfc_string(&self) -> String;
|
||||
|
||||
fn to_nfkd_chars(&self) -> Vec<char>;
|
||||
|
||||
fn to_nfkd_string(&self) -> String;
|
||||
|
||||
fn to_nfkc_chars(&self) -> Vec<char>;
|
||||
|
||||
fn to_nfkc_string(&self) -> String;
|
||||
}
|
||||
|
||||
impl StringNormalizationHelpers for str {
|
||||
#[inline]
|
||||
fn to_nfd_chars(&self) -> Vec<char> {
|
||||
self.nfd().collect()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_nfd_string(&self) -> String {
|
||||
self.nfd().collect()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_nfc_chars(&self) -> Vec<char> {
|
||||
self.nfc().collect()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_nfc_string(&self) -> String {
|
||||
self.nfc().collect()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_nfkd_chars(&self) -> Vec<char> {
|
||||
self.nfkd().collect()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_nfkd_string(&self) -> String {
|
||||
self.nfkd().collect()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_nfkc_chars(&self) -> Vec<char> {
|
||||
self.nfkc().collect()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_nfkc_string(&self) -> String {
|
||||
self.nfkc().collect()
|
||||
}
|
||||
}
|
||||
33
libnative/textutils/src/properties.rs
Normal file
33
libnative/textutils/src/properties.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use icu::properties::sets::CodePointSetDataBorrowed;
|
||||
|
||||
pub use icu::properties::sets;
|
||||
|
||||
pub trait CodePointSetDataExt {
|
||||
fn debug_print(&self);
|
||||
|
||||
fn debug_print_based(&self, base: char);
|
||||
}
|
||||
|
||||
impl <'a> CodePointSetDataExt for CodePointSetDataBorrowed<'a> {
|
||||
fn debug_print(&self) {
|
||||
debug_print_impl(&self, None);
|
||||
}
|
||||
|
||||
fn debug_print_based(&self, base: char) {
|
||||
debug_print_impl(&self, Some(base));
|
||||
}
|
||||
}
|
||||
|
||||
fn debug_print_impl(set: &CodePointSetDataBorrowed, base: Option<char>) {
|
||||
for range in set.iter_ranges() {
|
||||
print!("{:#x}..={:#x}", range.start(), range.end());
|
||||
for codepoint in range {
|
||||
if let Some(base) = base {
|
||||
print!(" {}{}", base, char::from_u32(codepoint).unwrap());
|
||||
} else {
|
||||
print!(" {}", char::from_u32(codepoint).unwrap());
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
63
libnative/textutils/src/segment.rs
Normal file
63
libnative/textutils/src/segment.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
use icu_segmenter::{GraphemeClusterSegmenter, SentenceSegmenter, WordSegmenter};
|
||||
use itertools::Itertools;
|
||||
|
||||
pub struct IcuSegmenterCache {
|
||||
sentence_segmenter: SentenceSegmenter,
|
||||
word_segmenter: WordSegmenter,
|
||||
grapheme_cluster_segmenter: GraphemeClusterSegmenter,
|
||||
}
|
||||
|
||||
impl IcuSegmenterCache {
|
||||
pub fn new_auto() -> Self {
|
||||
let sentence_segmenter = SentenceSegmenter::new();
|
||||
let word_segmenter = WordSegmenter::new_auto();
|
||||
let grapheme_cluster_segmenter = GraphemeClusterSegmenter::new();
|
||||
return Self {
|
||||
sentence_segmenter,
|
||||
word_segmenter,
|
||||
grapheme_cluster_segmenter,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn split_sentences<'t>(&self, text: &'t str) -> Vec<&'t str> {
|
||||
return split_sentences(text, &self.sentence_segmenter);
|
||||
}
|
||||
|
||||
pub fn split_words<'t>(&self, text: &'t str) -> Vec<&'t str> {
|
||||
return split_words(text, &self.word_segmenter);
|
||||
}
|
||||
|
||||
pub fn split_grapheme_clusters<'t>(&self, text: &'t str) -> Vec<&'t str> {
|
||||
return split_grapheme_clusters(text, &self.grapheme_cluster_segmenter);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split_sentences<'t>(text: &'t str, segmenter: &SentenceSegmenter) -> Vec<&'t str> {
|
||||
let sentences: Vec<&str> = segmenter
|
||||
.segment_str(text)
|
||||
.tuple_windows()
|
||||
.map(|(i, j)| text[i..j].trim())
|
||||
.filter(|sentence| !sentence.is_empty())
|
||||
.collect();
|
||||
return sentences;
|
||||
}
|
||||
|
||||
pub fn split_words<'t>(text: &'t str, segmenter: &WordSegmenter) -> Vec<&'t str> {
|
||||
let words: Vec<&str> = segmenter
|
||||
.segment_str(text)
|
||||
.iter_with_word_type()
|
||||
.tuple_windows()
|
||||
.filter(|(_, (_, segment_type))| segment_type.is_word_like())
|
||||
.map(|((i, _), (j, _))| &text[i..j])
|
||||
.collect();
|
||||
return words;
|
||||
}
|
||||
|
||||
pub fn split_grapheme_clusters<'t>(text: &'t str, segmenter: &GraphemeClusterSegmenter) -> Vec<&'t str> {
|
||||
let grapheme_clusters: Vec<&str> = segmenter
|
||||
.segment_str(text)
|
||||
.tuple_windows()
|
||||
.map(|(i, j)| &text[i..j])
|
||||
.collect();
|
||||
return grapheme_clusters;
|
||||
}
|
||||
961
utils/flesttools/Cargo.lock
generated
Normal file
961
utils/flesttools/Cargo.lock
generated
Normal file
@@ -0,0 +1,961 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "calendrical_calculations"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cec493b209a1b81fa32312d7ceca1b547d341c7b5f16a3edbf32b1d8b455bbdf"
|
||||
dependencies = [
|
||||
"core_maths",
|
||||
"displaydoc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b9470d453346108f93a59222a9a1a5724db32d0a4727b7ab7ace4b4d822dc9"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core_maths"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3b02505ccb8c50b0aa21ace0fc08c3e53adebd4e58caa18a36152803c7709a3"
|
||||
dependencies = [
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "fixed_decimal"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0febbeb1118a9ecdee6e4520ead6b54882e843dd0592ad233247dbee84c53db8"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"smallvec",
|
||||
"writeable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flest"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"textutils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flesttools"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"flest",
|
||||
"pancurses",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"textutils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff5e3018d703f168b00dcefa540a65f1bbc50754ae32f3f5f0e43fe5ee51502"
|
||||
dependencies = [
|
||||
"icu_calendar",
|
||||
"icu_casemap",
|
||||
"icu_collator",
|
||||
"icu_collections",
|
||||
"icu_datetime",
|
||||
"icu_decimal",
|
||||
"icu_experimental",
|
||||
"icu_list",
|
||||
"icu_locid",
|
||||
"icu_locid_transform",
|
||||
"icu_normalizer",
|
||||
"icu_plurals",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"icu_segmenter",
|
||||
"icu_timezone",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_calendar"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7265b2137f9a36f7634a308d91f984574bbdba8cfd95ceffe1c345552275a8ff"
|
||||
dependencies = [
|
||||
"calendrical_calculations",
|
||||
"displaydoc",
|
||||
"icu_calendar_data",
|
||||
"icu_locid",
|
||||
"icu_locid_transform",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_calendar_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e009b7f0151ee6fb28c40b1283594397e0b7183820793e9ace3dcd13db126d0"
|
||||
|
||||
[[package]]
|
||||
name = "icu_casemap"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ff0c8ae9f8d31b12e27fc385ff9ab1f3cd9b17417c665c49e4ec958c37da75f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_casemap_data",
|
||||
"icu_collections",
|
||||
"icu_locid",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_casemap_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d57966d5ab748f74513be4046867f9a20e801e2775d41f91d04a0f560b61f08"
|
||||
|
||||
[[package]]
|
||||
name = "icu_collator"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d370371887d31d56f361c3eaa15743e54f13bc677059c9191c77e099ed6966b2"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collator_data",
|
||||
"icu_collections",
|
||||
"icu_locid_transform",
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"utf16_iter",
|
||||
"utf8_iter",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_collator_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ee3f88741364b7d6269cce6827a3e6a8a2cf408a78f766c9224ab479d5e4ae5"
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_datetime"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d115efb85e08df3fd77e77f52e7e087545a783fffba8be80bfa2102f306b1780"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"either",
|
||||
"fixed_decimal",
|
||||
"icu_calendar",
|
||||
"icu_datetime_data",
|
||||
"icu_decimal",
|
||||
"icu_locid",
|
||||
"icu_locid_transform",
|
||||
"icu_plurals",
|
||||
"icu_provider",
|
||||
"icu_timezone",
|
||||
"smallvec",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_datetime_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ba7e7f7a01269b9afb0a39eff4f8676f693b55f509b3120e43a0350a9f88bea"
|
||||
|
||||
[[package]]
|
||||
name = "icu_decimal"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb8fd98f86ec0448d85e1edf8884e4e318bb2e121bd733ec929a05c0a5e8b0eb"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"fixed_decimal",
|
||||
"icu_decimal_data",
|
||||
"icu_locid_transform",
|
||||
"icu_provider",
|
||||
"writeable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_decimal_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d424c994071c6f5644f999925fc868c85fec82295326e75ad5017bc94b41523"
|
||||
|
||||
[[package]]
|
||||
name = "icu_experimental"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "844ad7b682a165c758065d694bc4d74ac67f176da1c499a04d85d492c0f193b7"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"fixed_decimal",
|
||||
"icu_collections",
|
||||
"icu_decimal",
|
||||
"icu_experimental_data",
|
||||
"icu_locid",
|
||||
"icu_locid_transform",
|
||||
"icu_normalizer",
|
||||
"icu_pattern",
|
||||
"icu_plurals",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"litemap",
|
||||
"num-bigint",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
"smallvec",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerofrom",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_experimental_data"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c178b9a34083fca5bd70d61f647575335e9c197d0f30c38e8ccd187babc69d0"
|
||||
|
||||
[[package]]
|
||||
name = "icu_list"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbfeda1d7775b6548edd4e8b7562304a559a91ed56ab56e18961a053f367c365"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_list_data",
|
||||
"icu_locid_transform",
|
||||
"icu_provider",
|
||||
"regex-automata 0.2.0",
|
||||
"writeable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_list_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1825170d2c6679cb20dbd96a589d034e49f698aed9a2ef4fafc9a0101ed298f"
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid_transform"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_locid_transform_data",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid_transform_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"utf16_iter",
|
||||
"utf8_iter",
|
||||
"write16",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
|
||||
|
||||
[[package]]
|
||||
name = "icu_pattern"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7f36aafd098d6717de34e668a8120822275c1fba22b936e757b7de8a2fd7e4"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"either",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_plurals"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba5a70e7c025dbd5c501b0a5c188cd11666a424f0dadcd4f0a95b7dafde3b114"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"fixed_decimal",
|
||||
"icu_locid_transform",
|
||||
"icu_plurals_data",
|
||||
"icu_provider",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_plurals_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e3e8f775b215d45838814a090a2227247a7431d74e9156407d9c37f6ef0f208"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locid_transform",
|
||||
"icu_properties_data",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_provider_macros",
|
||||
"stable_deref_trait",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider_macros"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_segmenter"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a717725612346ffc2d7b42c94b820db6908048f39434504cb130e8b46256b0de"
|
||||
dependencies = [
|
||||
"core_maths",
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locid",
|
||||
"icu_provider",
|
||||
"icu_segmenter_data",
|
||||
"utf8_iter",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_segmenter_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f739ee737260d955e330bc83fdeaaf1631f7fb7ed218761d3c04bb13bb7d79df"
|
||||
|
||||
[[package]]
|
||||
name = "icu_timezone"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa91ba6a585939a020c787235daa8aee856d9bceebd6355e283c0c310bc6de96"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_calendar",
|
||||
"icu_provider",
|
||||
"icu_timezone_data",
|
||||
"tinystr",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_timezone_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c588878c508a3e2ace333b3c50296053e6483c6a7541251b546cc59dcd6ced8e"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.161"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
|
||||
|
||||
[[package]]
|
||||
name = "linkify"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1dfa36d52c581e9ec783a7ce2a5e0143da6237be5811a0b3153fedfdbe9f780"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "ncurses"
|
||||
version = "5.101.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e2c5d34d72657dc4b638a1c25d40aae81e4f1c699062f72f467237920752032"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||
dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "pancurses"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0352975c36cbacb9ee99bfb709b9db818bed43af57751797f8633649759d13db"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"ncurses",
|
||||
"pdcurses-sys",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pdcurses-sys"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "084dd22796ff60f1225d4eb6329f33afaf4c85419d51d440ab6b8c6f4529166b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.4.8",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9368763f5a9b804326f3af749e16f9abf378d227bcdee7634b13d8f17793782"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.210"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.210"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.132"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textutils"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"icu",
|
||||
"icu_segmenter",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"linkify",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"unicase",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf16_iter"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a27a759395c1195c4cc5cda607ef6f8f6498f64e78f7900f5de0a127a424704a"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "write16"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb594dd55d87335c5f60177cee24f19457a5ec10a065e0a3014722ad252d0a1f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
11
utils/flesttools/Cargo.toml
Normal file
11
utils/flesttools/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "flesttools"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
flest = { path = "../../libnative/flest" }
|
||||
textutils = { path = "../../libnative/textutils" }
|
||||
pancurses = { version = "0.17.0", features = ["wide"] }
|
||||
serde = "1.0.203"
|
||||
serde_json = "1.0.120"
|
||||
143
utils/flesttools/src/main.rs
Normal file
143
utils/flesttools/src/main.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
use flest::model::NgramModel;
|
||||
use pancurses::Input;
|
||||
use textutils::filter::preprocess_auto;
|
||||
use textutils::segment::IcuSegmenterCache;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::io::BufRead;
|
||||
use std::io::BufReader;
|
||||
|
||||
const TOKEN_SENTENCE_SEPARATOR: &str = "\\sep";
|
||||
|
||||
fn tokenize_text(text: &str) -> Vec<&str> {
|
||||
let segmenters = IcuSegmenterCache::new_auto();
|
||||
let sentences = segmenters.split_sentences(text);
|
||||
let mut tokens: Vec<&str> = Vec::new();
|
||||
|
||||
tokens.push(TOKEN_SENTENCE_SEPARATOR);
|
||||
for sentence in sentences {
|
||||
let words = segmenters.split_words(sentence);
|
||||
for word in words {
|
||||
tokens.push(word);
|
||||
}
|
||||
tokens.push(TOKEN_SENTENCE_SEPARATOR);
|
||||
}
|
||||
|
||||
//println!("Tokens: {:?}", tokens);
|
||||
return tokens;
|
||||
}
|
||||
|
||||
fn train_model(text: &str, model: &mut NgramModel) {
|
||||
let text = preprocess_auto(text);
|
||||
let text = text.trim();
|
||||
if text.is_empty() {
|
||||
return;
|
||||
}
|
||||
let tokens = tokenize_text(&text);
|
||||
//println!("Tokens: {:?}", tokens);
|
||||
model.train_from_tokens(&tokens);
|
||||
}
|
||||
|
||||
fn train_from_plain_text(path: &str, model: &mut NgramModel) {
|
||||
let text = fs::read_to_string(path).expect("Failed to read file");
|
||||
train_model(&text, model);
|
||||
}
|
||||
|
||||
fn train_from_reddit_comments(path: &str, model: &mut NgramModel) {
|
||||
let file = fs::File::open(path).expect("Failed to open file");
|
||||
let reader = BufReader::new(file);
|
||||
let mut line_count = 0;
|
||||
for line in reader.lines() {
|
||||
if let Ok(line) = line {
|
||||
let json: serde_json::Value = serde_json::from_str(&line).expect("Failed to parse JSON");
|
||||
|
||||
if let Some(author) = json.get("author").and_then(|it| it.as_str()) {
|
||||
if author == "AutoModerator" {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if let Some(body) = json.get("body").and_then(|it| it.as_str()) {
|
||||
train_model(body, model);
|
||||
}
|
||||
}
|
||||
line_count += 1;
|
||||
if line_count > 10000 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() != 2 {
|
||||
eprintln!("Usage: {} <file_path>", args[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
let path = &args[1];
|
||||
let mut model = NgramModel::new();
|
||||
model.meta.sentence_token = TOKEN_SENTENCE_SEPARATOR.to_owned();
|
||||
|
||||
if path.ends_with(".reddit.jsonl") {
|
||||
train_from_reddit_comments(path, &mut model);
|
||||
} else {
|
||||
train_from_plain_text(path, &mut model);
|
||||
}
|
||||
//model.trie_root.debug_pretty_print();
|
||||
//return;
|
||||
|
||||
let window = pancurses::initscr();
|
||||
let mut input_text = String::new();
|
||||
|
||||
pancurses::noecho();
|
||||
window.keypad(true);
|
||||
loop {
|
||||
let mut words: Vec<&str> = input_text.split_whitespace().collect();
|
||||
words.insert(0, TOKEN_SENTENCE_SEPARATOR);
|
||||
|
||||
if input_text.ends_with(' ') || words.last() == Some(&TOKEN_SENTENCE_SEPARATOR) {
|
||||
words.push("");
|
||||
}
|
||||
|
||||
let predictions = model.predict(&words);
|
||||
|
||||
window.clear();
|
||||
window.addstr("N-gram model debug frontend\n");
|
||||
window.addstr(" demo tokenizer only supports single-line sentence in input text!\n\n");
|
||||
window.addstr(format!("enter text: {}\n", input_text));
|
||||
window.addstr(format!("detected words: {:?}\n\n", words));
|
||||
window.addstr("predictions:\n");
|
||||
for (i, candidate) in predictions.iter().enumerate() {
|
||||
if i == 0 && candidate.confidence > (0.9 * 255.0) as u8 {
|
||||
window.attron(pancurses::A_BOLD);
|
||||
}
|
||||
window.addstr(format!(" {}. {} (c={:.2})\n", i + 1, candidate.text, candidate.confidence));
|
||||
if i == 0 && candidate.confidence > (0.9 * 255.0) as u8 {
|
||||
window.attroff(pancurses::A_BOLD);
|
||||
}
|
||||
}
|
||||
if predictions.is_empty() {
|
||||
window.addstr(" (none)\n");
|
||||
}
|
||||
window.mv(3, 12 + input_text.len() as i32);
|
||||
window.refresh();
|
||||
|
||||
match window.getch().unwrap() {
|
||||
Input::KeyF10 => {
|
||||
break
|
||||
}
|
||||
Input::KeyBackspace => {
|
||||
input_text.pop();
|
||||
}
|
||||
Input::Character('\n') => {
|
||||
train_model(&input_text, &mut model)
|
||||
}
|
||||
Input::Character(ch) => {
|
||||
input_text.push(ch)
|
||||
}
|
||||
_ => { () }
|
||||
}
|
||||
}
|
||||
|
||||
pancurses::endwin();
|
||||
}
|
||||
27
utils/setup_vscode_dev_env.sh
Executable file
27
utils/setup_vscode_dev_env.sh
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
|
||||
WORKSPACE_ROOT_DIR="$(realpath "$(dirname "$0")/..")"
|
||||
VSCODE_DIR="$WORKSPACE_ROOT_DIR/.vscode"
|
||||
VSCODE_SETTINGS_JSON_PATH="$VSCODE_DIR/settings.json"
|
||||
|
||||
if [ "$WORKSPACE_ROOT_DIR" != "$(pwd)" ]; then
|
||||
echo "Not executing this script from workspace root dir!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$VSCODE_DIR" ]; then
|
||||
mkdir "$VSCODE_DIR"
|
||||
fi
|
||||
|
||||
echo -en "{\n" > "$VSCODE_SETTINGS_JSON_PATH"
|
||||
|
||||
# <rust-analyzer>
|
||||
rust_project_paths="$(find "$WORKSPACE_ROOT_DIR" -type f -name "Cargo.toml")"
|
||||
echo -en " \"rust-analyzer.linkedProjects\": [\n" >> "$VSCODE_SETTINGS_JSON_PATH"
|
||||
for rust_project_path in $rust_project_paths; do
|
||||
echo -en " \"$rust_project_path\",\n" >> "$VSCODE_SETTINGS_JSON_PATH"
|
||||
done
|
||||
echo -en " ],\n" >> "$VSCODE_SETTINGS_JSON_PATH"
|
||||
# </rust-analyzer>
|
||||
|
||||
echo -en "}\n" >> "$VSCODE_SETTINGS_JSON_PATH"
|
||||
Reference in New Issue
Block a user