Skip to content
This repository was archived by the owner on Oct 18, 2024. It is now read-only.

Commit e1382f1

Browse files
committed
fix(editor): file modification status is not updated while closing files (fixes #1485)
1 parent b2ff610 commit e1382f1

File tree

5 files changed

+138
-128
lines changed

5 files changed

+138
-128
lines changed

.idea/misc.xml

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/src/main/java/com/itsaky/androidide/activities/editor/BaseEditorActivity.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,8 @@ abstract class BaseEditorActivity :
465465
viewContainer.displayedChild = 0
466466
}
467467
}
468+
469+
invalidateOptionsMenu()
468470
}
469471

470472
setupNoEditorView()

app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt

Lines changed: 96 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import androidx.appcompat.view.menu.MenuBuilder
2828
import androidx.collection.MutableIntObjectMap
2929
import androidx.core.view.GravityCompat
3030
import com.blankj.utilcode.util.ImageUtils
31-
import com.blankj.utilcode.util.ThreadUtils
3231
import com.itsaky.androidide.R.string
3332
import com.itsaky.androidide.actions.ActionData
3433
import com.itsaky.androidide.actions.ActionItem.Location.EDITOR_TOOLBAR
@@ -51,7 +50,6 @@ import com.itsaky.androidide.models.Range
5150
import com.itsaky.androidide.models.SaveResult
5251
import com.itsaky.androidide.projects.ProjectManagerImpl
5352
import com.itsaky.androidide.tasks.executeAsync
54-
import com.itsaky.androidide.tasks.executeAsyncProvideError
5553
import com.itsaky.androidide.ui.CodeEditorView
5654
import com.itsaky.androidide.utils.DialogUtils.newYesNoDialog
5755
import com.itsaky.androidide.utils.IntentUtils.openImage
@@ -109,6 +107,9 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
109107
}
110108
}
111109

110+
editorViewModel._filesModified.observe(this) { invalidateOptionsMenu() }
111+
editorViewModel._filesSaving.observe(this) { invalidateOptionsMenu() }
112+
112113
editorViewModel.observeFiles(this) {
113114
// rewrite the cached files index if there are any opened files
114115
val currentFile =
@@ -413,41 +414,40 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
413414
}
414415

415416
override suspend fun saveAllResult(progressConsumer: ((Int, Int) -> Unit)?): SaveResult {
416-
setFilesSaving(true)
417-
val result = SaveResult()
418-
for (i in 0 until editorViewModel.getOpenedFileCount()) {
419-
saveResultInternal(i, result, false)
420-
progressConsumer?.invoke(i + 1, editorViewModel.getOpenedFileCount())
417+
return performFileSave {
418+
val result = SaveResult()
419+
for (i in 0 until editorViewModel.getOpenedFileCount()) {
420+
saveResultInternal(i, result)
421+
progressConsumer?.invoke(i + 1, editorViewModel.getOpenedFileCount())
422+
}
423+
424+
return@performFileSave result
421425
}
422-
setFilesSaving(false)
423-
return result
424426
}
425427

426428
override suspend fun saveResult(index: Int, result: SaveResult) {
427-
saveResultInternal(index, result, true)
429+
performFileSave {
430+
saveResultInternal(index, result)
431+
}
428432
}
429433

430-
private suspend fun saveResultInternal(index: Int, result: SaveResult, updateSaving: Boolean) {
434+
private suspend fun saveResultInternal(
435+
index: Int,
436+
result: SaveResult
437+
) : Boolean {
431438
if (index < 0 || index >= editorViewModel.getOpenedFileCount()) {
432-
return
439+
return false
433440
}
434441

435-
val frag = getEditorAtIndex(index) ?: return
436-
val fileName = frag.file?.name ?: return
437-
438-
if (updateSaving) {
439-
setFilesSaving(true)
440-
}
442+
val frag = getEditorAtIndex(index) ?: return false
443+
val fileName = frag.file?.name ?: return false
441444

442445
run {
443446
// Must be called before frag.save()
444447
// Otherwise, it'll always return false
445448
val modified = frag.isModified
446449
if (!frag.save()) {
447-
if (updateSaving) {
448-
setFilesSaving(false)
449-
}
450-
return
450+
return false
451451
}
452452

453453
val isGradle = fileName.endsWith(".gradle") || fileName.endsWith(".gradle.kts")
@@ -461,108 +461,117 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
461461
}
462462
}
463463

464-
var modified = false
465-
for (file in editorViewModel.getOpenedFiles()) {
466-
val editor = getEditorForFile(file) ?: continue
467-
modified = modified || editor.isModified
468-
}
469-
470-
val finalModified = modified
464+
val hasUnsaved = hasUnsavedFiles()
471465

472466
withContext(Dispatchers.Main) {
473-
editorViewModel.apply {
474-
setFilesModified(finalModified)
475-
}
476467

477-
if (updateSaving) {
478-
setFilesSaving(false)
479-
}
468+
editorViewModel.areFilesModified = hasUnsaved
480469

481470
// set tab as unmodified
482471
val tab = binding.tabs.getTabAt(index) ?: return@withContext
483472
if (tab.text!!.startsWith('*')) {
484473
tab.text = tab.text!!.substring(startIndex = 1)
485474
}
486475
}
476+
477+
return true
487478
}
488479

489-
private suspend fun setFilesSaving(saving: Boolean) {
490-
withContext(Dispatchers.Main.immediate) {
491-
invalidateOptionsMenu()
492-
editorViewModel.setFilesSaving(saving)
480+
private fun hasUnsavedFiles() = editorViewModel.getOpenedFiles().any { file ->
481+
getEditorForFile(file)?.isModified == true
482+
}
483+
484+
private suspend inline fun <T : Any?> performFileSave(crossinline action: suspend () -> T) : T {
485+
setFilesSaving(true)
486+
try {
487+
return action()
488+
} finally {
489+
setFilesSaving(false)
493490
}
494491
}
495492

496-
private fun onEditorContentChanged() {
497-
editorViewModel.setFilesModified(true)
498-
invalidateOptionsMenu()
493+
private suspend fun setFilesSaving(saving: Boolean) {
494+
withContext(Dispatchers.Main.immediate) {
495+
editorViewModel.areFilesSaving = saving
496+
}
499497
}
500498

501499
override fun areFilesModified(): Boolean {
502-
return editorViewModel.areFilesModified()
500+
return editorViewModel.areFilesModified
503501
}
504502

505503
override fun areFilesSaving(): Boolean {
506-
return editorViewModel.areFilesSaving()
504+
return editorViewModel.areFilesSaving
507505
}
508506

509507
override fun closeFile(index: Int, runAfter: () -> Unit) {
510-
if (index >= 0 && index < editorViewModel.getOpenedFileCount()) {
511-
val opened: File = editorViewModel.getOpenedFile(index)
512-
log.info("Closing file:", opened)
513-
val editor = getEditorAtIndex(index)
514-
if (editor != null && editor.isModified) {
515-
notifyFilesUnsaved(listOf(editor)) { closeFile(index, runAfter) }
516-
return
517-
}
508+
if (index < 0 || index >= editorViewModel.getOpenedFileCount()) {
509+
log.error("Invalid file index. Cannot close.")
510+
return
511+
}
518512

519-
editor?.close() ?: run {
520-
log.error("Cannot save file before close. Editor instance is null")
521-
}
513+
val opened = editorViewModel.getOpenedFile(index)
514+
log.info("Closing file:", opened)
522515

523-
editorViewModel.removeFile(index)
524-
binding.apply {
525-
tabs.removeTabAt(index)
526-
editorContainer.removeViewAt(index)
516+
val editor = getEditorAtIndex(index)
517+
if (editor?.isModified == true) {
518+
log.info("File has been modified:", opened)
519+
notifyFilesUnsaved(listOf(editor)) {
520+
closeFile(index, runAfter)
527521
}
528-
529-
updateTabs()
530-
} else {
531-
log.error("Invalid file index. Cannot close.")
532522
return
533523
}
534524

525+
editor?.close() ?: run {
526+
log.error("Cannot save file before close. Editor instance is null")
527+
}
528+
529+
editorViewModel.removeFile(index)
530+
binding.apply {
531+
tabs.removeTabAt(index)
532+
editorContainer.removeViewAt(index)
533+
}
534+
535+
editorViewModel.areFilesModified = hasUnsavedFiles()
536+
537+
updateTabs()
535538
runAfter()
536539
}
537540

538541
override fun closeOthers() {
542+
if (editorViewModel.getOpenedFileCount() == 0) {
543+
return
544+
}
545+
539546
val unsavedFiles =
540547
editorViewModel.getOpenedFiles().map(::getEditorForFile)
541548
.filter { it != null && it.isModified }
542549

543-
if (unsavedFiles.isEmpty()) {
544-
val file = editorViewModel.getCurrentFile()
545-
var index = 0
550+
if (unsavedFiles.isNotEmpty()) {
551+
notifyFilesUnsaved(unsavedFiles) { closeOthers() }
552+
return
553+
}
554+
555+
val file = editorViewModel.getCurrentFile()
556+
var index = 0
546557

547-
// keep closing the file at index 0
548-
// if openedFiles[0] == file, then keep closing files at index 1
549-
while (editorViewModel.getOpenedFileCount() != 1) {
550-
val editor = getEditorAtIndex(index)
558+
// keep closing the file at index 0
559+
// if openedFiles[0] == file, then keep closing files at index 1
560+
while (editorViewModel.getOpenedFileCount() != 1) {
561+
val editor = getEditorAtIndex(index)
551562

552-
// Index of files changes as we keep close files
553-
// So we compare the files instead of index
554-
if (editor != null) {
555-
if (file != editor.file) {
556-
closeFile(index)
557-
} else {
558-
index = 1
559-
}
560-
} else {
561-
log.error("Unable to save file at index:", index)
562-
}
563+
if (editor == null) {
564+
log.error("Unable to save file at index:", index)
565+
continue
566+
}
567+
568+
// Index of files changes as we keep close files
569+
// So we compare the files instead of index
570+
if (file != editor.file) {
571+
closeFile(index)
572+
} else {
573+
index = 1
563574
}
564-
} else {
565-
notifyFilesUnsaved(unsavedFiles) { closeOthers() }
566575
}
567576
}
568577

@@ -650,7 +659,7 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
650659
@Subscribe(threadMode = ThreadMode.MAIN)
651660
fun onDocumentChange(event: DocumentChangeEvent) {
652661
// update content modification status
653-
onEditorContentChanged()
662+
editorViewModel.areFilesModified = true
654663

655664
val index = findIndexOfEditorByFile(event.file.toFile())
656665
if (index == -1) {
@@ -667,7 +676,7 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
667676
}
668677

669678
private fun updateTabs() {
670-
executeAsyncProvideError({
679+
editorActivityScope.launch {
671680
val files = editorViewModel.getOpenedFiles()
672681
val dupliCount = mutableMapOf<String, Int>()
673682
val names = MutableIntObjectMap<String>()
@@ -690,15 +699,8 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
690699
names[i] = name
691700
}
692701

693-
names
694-
}) { result, error ->
695-
if (result == null || error != null) {
696-
log.error("Failed to compute names for file tabs", error)
697-
return@executeAsyncProvideError
698-
}
699-
700-
ThreadUtils.runOnUiThread {
701-
result.forEach { index, name ->
702+
withContext(Dispatchers.Main) {
703+
names.forEach { index, name ->
702704
binding.tabs.getTabAt(index)?.text = name
703705
}
704706
}

app/src/main/java/com/itsaky/androidide/ui/CodeEditorView.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ class CodeEditorView(
208208
* Mark this files as saved. Even if it not saved.
209209
*/
210210
fun markAsSaved() {
211-
notifySaved()
211+
editor?.markUnmodified()
212212
}
213213

214214
/**
@@ -245,6 +245,7 @@ class CodeEditorView(
245245
_binding?.rwProgress?.isVisible = false
246246
}
247247

248+
markUnmodified()
248249
notifySaved()
249250

250251
return true

0 commit comments

Comments
 (0)