Update editor: build using temp files and save directly to source
This commit is contained in:
parent
d61ec9e1f2
commit
1223416923
5 changed files with 116 additions and 18 deletions
|
|
@ -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()
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package mg.dot.feufaro
|
||||||
|
|
||||||
|
interface PathValidator {
|
||||||
|
fun isRestrictedPath(path: String): Boolean
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
package mg.dot.feufaro
|
package mg.dot.feufaro
|
||||||
|
|
||||||
|
|
||||||
interface Platform {
|
interface Platform {
|
||||||
val name: String
|
val name: String
|
||||||
}
|
}
|
||||||
|
|
||||||
expect fun getPlatform(): Platform
|
expect fun getPlatform(): Platform
|
||||||
|
|
||||||
|
expect fun getPathValidator(): PathValidator
|
||||||
|
|
@ -15,7 +15,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.BasicTextField
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material.icons.Icons
|
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.automirrored.filled.Undo
|
||||||
import androidx.compose.material.icons.filled.Build
|
import androidx.compose.material.icons.filled.Build
|
||||||
import androidx.compose.material.icons.filled.Close
|
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.FileRepository
|
||||||
import mg.dot.feufaro.TUODetailDialog
|
import mg.dot.feufaro.TUODetailDialog
|
||||||
import mg.dot.feufaro.data.GridTUOData
|
import mg.dot.feufaro.data.GridTUOData
|
||||||
|
import mg.dot.feufaro.getPathValidator
|
||||||
import mg.dot.feufaro.ui.rememberFileSaveLauncher
|
import mg.dot.feufaro.ui.rememberFileSaveLauncher
|
||||||
import mg.dot.feufaro.viewmodel.MidiMarkers
|
import mg.dot.feufaro.viewmodel.MidiMarkers
|
||||||
import mg.dot.feufaro.viewmodel.SolfaScreenModel
|
import mg.dot.feufaro.viewmodel.SolfaScreenModel
|
||||||
|
|
@ -1039,10 +1039,15 @@ private suspend fun expandInclusions(
|
||||||
val lastSlash = currentFilePath.lastIndexOf('/')
|
val lastSlash = currentFilePath.lastIndexOf('/')
|
||||||
val directory = if (lastSlash != -1) currentFilePath.substring(0, lastSlash + 1) else ""
|
val directory = if (lastSlash != -1) currentFilePath.substring(0, lastSlash + 1) else ""
|
||||||
|
|
||||||
|
val inclusionRegex = Regex("^I([0-9]):(.*)")
|
||||||
|
|
||||||
lines.forEach { line ->
|
lines.forEach { line ->
|
||||||
if (line.startsWith("I0:")) {
|
val match = inclusionRegex.find(line)
|
||||||
|
|
||||||
|
if (match != null) {
|
||||||
try {
|
try {
|
||||||
val parts = line.substring(3).split(":")
|
val body = match.groupValues[2]
|
||||||
|
val parts = body.split(":")
|
||||||
val fileName = parts[0]
|
val fileName = parts[0]
|
||||||
val ignorePattern = if (parts.size > 1) parts[1] else ""
|
val ignorePattern = if (parts.size > 1) parts[1] else ""
|
||||||
|
|
||||||
|
|
@ -1057,7 +1062,7 @@ private suspend fun expandInclusions(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
result.append("// Erreur inclusion: ${e.message}\n")
|
result.append("// Erreur inclusion sur ${line}: ${e.message}\n")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result.append(line).append("\n")
|
result.append(line).append("\n")
|
||||||
|
|
@ -1092,8 +1097,20 @@ fun EditSourceCompose(
|
||||||
var currentTempFile by remember { mutableStateOf<File?>(null) }
|
var currentTempFile by remember { mutableStateOf<File?>(null) }
|
||||||
|
|
||||||
var codeContent by remember { mutableStateOf(sourceContent?: "") }
|
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) {
|
LaunchedEffect(sourceContent) {
|
||||||
codeContent = sourceContent
|
codeContent = sourceContent
|
||||||
|
previousCodeContentBeforeBuild = null
|
||||||
}
|
}
|
||||||
var textLayoutResult by remember { mutableStateOf<TextLayoutResult?>(null) }
|
var textLayoutResult by remember { mutableStateOf<TextLayoutResult?>(null) }
|
||||||
|
|
||||||
|
|
@ -1140,6 +1157,8 @@ fun EditSourceCompose(
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
|
||||||
EditorActionButtons(
|
EditorActionButtons(
|
||||||
|
isSaveVisible = isModified || isRestrictedPath,
|
||||||
|
isUndoVisible = isModified,
|
||||||
onUndo = {
|
onUndo = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
codeContent = sourceContent
|
codeContent = sourceContent
|
||||||
|
|
@ -1162,19 +1181,20 @@ fun EditSourceCompose(
|
||||||
|
|
||||||
if (currentTempFile == null || !currentTempFile!!.exists()) {
|
if (currentTempFile == null || !currentTempFile!!.exists()) {
|
||||||
val tempDir = System.getProperty("java.io.tmpdir")
|
val tempDir = System.getProperty("java.io.tmpdir")
|
||||||
currentTempFile = File(tempDir, fileName)
|
currentTempFile = File(tempDir, fileName.ifEmpty { "preview_solfa.txt" })
|
||||||
currentTempFile?.deleteOnExit()
|
currentTempFile?.deleteOnExit()
|
||||||
}
|
}
|
||||||
|
|
||||||
currentTempFile?.writeText(expandedContent)
|
withContext(Dispatchers.IO) {
|
||||||
|
currentTempFile?.writeText(expandedContent)
|
||||||
currentTempFile?.let { file ->
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
solfaScreenModel.justeCompile(file.absolutePath)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
println("Compilation sur le fichier : ${currentTempFile?.absolutePath}")
|
withContext(Dispatchers.Main) {
|
||||||
|
solfaScreenModel.justeCompile(currentTempFile!!.absolutePath)
|
||||||
|
|
||||||
|
println("Compilation temporaire réussie : ${currentTempFile?.absolutePath}")
|
||||||
|
currentTempFile?.deleteOnExit()
|
||||||
|
}
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
|
|
@ -1184,8 +1204,23 @@ fun EditSourceCompose(
|
||||||
onSave = {
|
onSave = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
try {
|
try {
|
||||||
val initialDir = solfaScreenModel.fileRepository.getAppPublicFolder().absolutePath
|
if (!isRestrictedPath) {
|
||||||
saveLauncher.launch(fileName, initialDir)
|
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) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|
@ -1221,11 +1256,15 @@ fun EditSourceCompose(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@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) {
|
Row(horizontalArrangement = Arrangement.SpaceBetween) {
|
||||||
IconButton(onClick = onUndo) { Icon(Icons.AutoMirrored.Filled.Undo, null, tint = Color.Gray) }
|
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)) }
|
IconButton(onClick = onBuild) { Icon(Icons.Default.Build, null, tint = Color(0xFF2196F3)) }
|
||||||
IconButton(onClick = onSave) { Icon(Icons.Default.Save, null, tint = Color(0xFF4CAF50)) }
|
if(isSaveVisible) {
|
||||||
|
IconButton(onClick = onSave) { Icon(Icons.Default.Save, null, tint = Color(0xFF4CAF50)) }
|
||||||
|
}
|
||||||
IconButton(onClick = onClose) { Icon(Icons.Default.Close, null, tint = Color.Red) }
|
IconButton(onClick = onClose) { Icon(Icons.Default.Close, null, tint = Color.Red) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
Loading…
Add table
Reference in a new issue