Update editor: build using temp files and save directly to source

This commit is contained in:
Hasinjato 2026-05-19 12:21:26 +03:00
parent d61ec9e1f2
commit 1223416923
5 changed files with 116 additions and 18 deletions

View file

@ -0,0 +1,22 @@
package mg.dot.feufaro
class AndroidPathValidator : PathValidator {
override fun isRestrictedPath(path: String): Boolean {
if (path.isEmpty()) return true
val lowerPath = path.lowercase()
val isAndroidAsset = lowerPath.contains("android_asset") ||
lowerPath.startsWith("assets/")
val isAndroidCacheOrTmp = lowerPath.contains("/cache/") ||
lowerPath.contains("/code_cache/") ||
lowerPath.contains("/tmp/") ||
lowerPath.contains("_tmp")
return isAndroidAsset || isAndroidCacheOrTmp
}
}
actual fun getPathValidator(): PathValidator = AndroidPathValidator()

View file

@ -0,0 +1,5 @@
package mg.dot.feufaro
interface PathValidator {
fun isRestrictedPath(path: String): Boolean
}

View file

@ -1,7 +1,10 @@
package mg.dot.feufaro
interface Platform {
val name: String
}
expect fun getPlatform(): Platform
expect fun getPathValidator(): PathValidator

View file

@ -15,7 +15,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBackIos
import androidx.compose.material.icons.automirrored.filled.Undo
import androidx.compose.material.icons.filled.Build
import androidx.compose.material.icons.filled.Close
@ -51,6 +50,7 @@ import mg.dot.feufaro.ContextualMenu
import mg.dot.feufaro.FileRepository
import mg.dot.feufaro.TUODetailDialog
import mg.dot.feufaro.data.GridTUOData
import mg.dot.feufaro.getPathValidator
import mg.dot.feufaro.ui.rememberFileSaveLauncher
import mg.dot.feufaro.viewmodel.MidiMarkers
import mg.dot.feufaro.viewmodel.SolfaScreenModel
@ -1039,10 +1039,15 @@ private suspend fun expandInclusions(
val lastSlash = currentFilePath.lastIndexOf('/')
val directory = if (lastSlash != -1) currentFilePath.substring(0, lastSlash + 1) else ""
val inclusionRegex = Regex("^I([0-9]):(.*)")
lines.forEach { line ->
if (line.startsWith("I0:")) {
val match = inclusionRegex.find(line)
if (match != null) {
try {
val parts = line.substring(3).split(":")
val body = match.groupValues[2]
val parts = body.split(":")
val fileName = parts[0]
val ignorePattern = if (parts.size > 1) parts[1] else ""
@ -1057,7 +1062,7 @@ private suspend fun expandInclusions(
}
}
} catch (e: Exception) {
result.append("// Erreur inclusion: ${e.message}\n")
result.append("// Erreur inclusion sur ${line}: ${e.message}\n")
}
} else {
result.append(line).append("\n")
@ -1092,8 +1097,20 @@ fun EditSourceCompose(
var currentTempFile by remember { mutableStateOf<File?>(null) }
var codeContent by remember { mutableStateOf(sourceContent?: "") }
val pathValidator = remember { getPathValidator() }
// restricted path pour les fichiers interne de l'app
val isRestrictedPath = remember(sourcePath) {
pathValidator.isRestrictedPath(sourcePath)
}
val isModified = remember(codeContent, sourceContent) { codeContent != sourceContent }
var previousCodeContentBeforeBuild by remember { mutableStateOf<String?>(null) }
LaunchedEffect(sourceContent) {
codeContent = sourceContent
previousCodeContentBeforeBuild = null
}
var textLayoutResult by remember { mutableStateOf<TextLayoutResult?>(null) }
@ -1140,6 +1157,8 @@ fun EditSourceCompose(
Spacer(modifier = Modifier.weight(1f))
EditorActionButtons(
isSaveVisible = isModified || isRestrictedPath,
isUndoVisible = isModified,
onUndo = {
scope.launch {
codeContent = sourceContent
@ -1162,19 +1181,20 @@ fun EditSourceCompose(
if (currentTempFile == null || !currentTempFile!!.exists()) {
val tempDir = System.getProperty("java.io.tmpdir")
currentTempFile = File(tempDir, fileName)
currentTempFile = File(tempDir, fileName.ifEmpty { "preview_solfa.txt" })
currentTempFile?.deleteOnExit()
}
withContext(Dispatchers.IO) {
currentTempFile?.writeText(expandedContent)
}
currentTempFile?.let { file ->
withContext(Dispatchers.Main) {
solfaScreenModel.justeCompile(file.absolutePath)
}
}
solfaScreenModel.justeCompile(currentTempFile!!.absolutePath)
println("Compilation sur le fichier : ${currentTempFile?.absolutePath}")
println("Compilation temporaire réussie : ${currentTempFile?.absolutePath}")
currentTempFile?.deleteOnExit()
}
} catch (e: Exception) {
e.printStackTrace()
@ -1184,8 +1204,23 @@ fun EditSourceCompose(
onSave = {
scope.launch {
try {
if (!isRestrictedPath) {
val externalFile = File(sourcePath)
withContext(Dispatchers.IO) {
externalFile.writeText(codeContent)
}
withContext(Dispatchers.Main) {
sharedScreenModel.setFileContent(codeContent, sourcePath)
solfaScreenModel.loadExternalFile(sourcePath)
}
println("Vraie source écrasée avec succès : $sourcePath")
} else {
val initialDir = solfaScreenModel.fileRepository.getAppPublicFolder().absolutePath
saveLauncher.launch(fileName, initialDir)
}
currentTempFile?.delete()
currentTempFile = null
} catch (e: Exception) {
e.printStackTrace()
}
@ -1221,11 +1256,15 @@ fun EditSourceCompose(
}
@Composable
fun EditorActionButtons(onUndo: () -> Unit, onBuild: () -> Unit, onSave: () -> Unit, onClose: () -> Unit) {
fun EditorActionButtons(isSaveVisible: Boolean, isUndoVisible: Boolean, onUndo: () -> Unit, onBuild: () -> Unit, onSave: () -> Unit, onClose: () -> Unit) {
Row(horizontalArrangement = Arrangement.SpaceBetween) {
if(isUndoVisible) {
IconButton(onClick = onUndo) { Icon(Icons.AutoMirrored.Filled.Undo, null, tint = Color.Gray) }
}
IconButton(onClick = onBuild) { Icon(Icons.Default.Build, null, tint = Color(0xFF2196F3)) }
if(isSaveVisible) {
IconButton(onClick = onSave) { Icon(Icons.Default.Save, null, tint = Color(0xFF4CAF50)) }
}
IconButton(onClick = onClose) { Icon(Icons.Default.Close, null, tint = Color.Red) }
}
}

View file

@ -0,0 +1,29 @@
package mg.dot.feufaro
import java.io.File
class DesktopPathValidator : PathValidator {
private val systemTmpDir: String by lazy {
try {
File(System.getProperty("java.io.tmpdir")).absolutePath.lowercase()
} catch (e: Exception) {
"/tmp"
}
}
override fun isRestrictedPath(path: String): Boolean {
if (path.isEmpty()) return true
val lowerPath = path.lowercase()
val isInSystemTmp = lowerPath.contains(systemTmpDir)
val isOtherRestricted = lowerPath.contains("/tmp/") ||
lowerPath.contains("_tmp") ||
lowerPath.contains("assets")
return isInSystemTmp || isOtherRestricted
}
}
actual fun getPathValidator(): PathValidator = DesktopPathValidator()