@@ -28,7 +28,6 @@ import androidx.appcompat.view.menu.MenuBuilder
2828import androidx.collection.MutableIntObjectMap
2929import androidx.core.view.GravityCompat
3030import com.blankj.utilcode.util.ImageUtils
31- import com.blankj.utilcode.util.ThreadUtils
3231import com.itsaky.androidide.R.string
3332import com.itsaky.androidide.actions.ActionData
3433import com.itsaky.androidide.actions.ActionItem.Location.EDITOR_TOOLBAR
@@ -51,7 +50,6 @@ import com.itsaky.androidide.models.Range
5150import com.itsaky.androidide.models.SaveResult
5251import com.itsaky.androidide.projects.ProjectManagerImpl
5352import com.itsaky.androidide.tasks.executeAsync
54- import com.itsaky.androidide.tasks.executeAsyncProvideError
5553import com.itsaky.androidide.ui.CodeEditorView
5654import com.itsaky.androidide.utils.DialogUtils.newYesNoDialog
5755import 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 }
0 commit comments