mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Create a Backing File if The Image File is Not On Disk
https://youtrack.jetbrains.com/issue/IDEA-362022 closes https://github.com/JetBrains/intellij-community/pull/2872 GitOrigin-RevId: e4e5f6072f6fbcbcc932307669901b57f0419ff3
This commit is contained in:
committed by
intellij-monorepo-bot
parent
c568dbf9fa
commit
2efb146b95
@@ -1,4 +1,5 @@
|
||||
error.title.launching.external.editor=Problem Launching External Executable
|
||||
error.cannot.edit.image.external.editor=Can't edit image in external editor
|
||||
|
||||
select.external.executable.title=Select Editor
|
||||
select.external.executable.message=Select external graphics editor
|
||||
|
||||
@@ -5,22 +5,37 @@ import com.intellij.execution.ExecutionException
|
||||
import com.intellij.execution.configurations.GeneralCommandLine
|
||||
import com.intellij.execution.util.ExecUtil
|
||||
import com.intellij.ide.util.PropertiesComponent
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.actionSystem.ActionUpdateThread
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.actionSystem.PlatformCoreDataKeys
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.openapi.project.DumbAwareAction
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.toNioPathOrNull
|
||||
import com.intellij.ui.UIBundle
|
||||
import com.intellij.util.EnvironmentUtil
|
||||
import com.intellij.util.io.delete
|
||||
import kotlinx.coroutines.*
|
||||
import org.intellij.images.ImagesBundle
|
||||
import org.intellij.images.fileTypes.ImageFileTypeManager
|
||||
import org.intellij.images.options.impl.ImagesConfigurable
|
||||
import org.intellij.images.ui.ImageComponentDecorator
|
||||
import java.awt.Desktop
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
|
||||
|
||||
/**
|
||||
* Open image file externally.
|
||||
@@ -31,6 +46,29 @@ import java.io.IOException
|
||||
internal class EditExternallyAction : DumbAwareAction() {
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val imageFile = e.getData(CommonDataKeys.VIRTUAL_FILE) ?: return
|
||||
if (imageFile.toNioPathOrNull() != null) {
|
||||
actionPerformed(e, imageFile)
|
||||
}
|
||||
else {
|
||||
performActionWithBackingFile(e, imageFile)
|
||||
}
|
||||
}
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
val file = e.getData(CommonDataKeys.VIRTUAL_FILE)
|
||||
val enabled = file != null && ImageFileTypeManager.getInstance().isImage(file)
|
||||
if (e.isFromContextMenu) {
|
||||
e.presentation.isVisible = enabled
|
||||
}
|
||||
|
||||
e.presentation.isEnabled = enabled
|
||||
}
|
||||
|
||||
override fun getActionUpdateThread(): ActionUpdateThread {
|
||||
return ActionUpdateThread.BGT
|
||||
}
|
||||
|
||||
private fun actionPerformed(e: AnActionEvent, imageFile: VirtualFile) {
|
||||
var executablePath = PropertiesComponent.getInstance().getValue(EditExternalImageEditorAction.EXT_PATH_KEY, "")
|
||||
if (!StringUtil.isEmpty(executablePath)) {
|
||||
EnvironmentUtil.getEnvironmentMap().forEach { (varName, varValue) ->
|
||||
@@ -75,17 +113,78 @@ internal class EditExternallyAction : DumbAwareAction() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
val file = e.getData(CommonDataKeys.VIRTUAL_FILE)
|
||||
val enabled = file != null && ImageFileTypeManager.getInstance().isImage(file)
|
||||
if (e.isFromContextMenu) {
|
||||
e.presentation.isVisible = enabled
|
||||
// Try to create a temporary backing file for the external editor to use
|
||||
private fun performActionWithBackingFile(e: AnActionEvent, imageFile: VirtualFile) {
|
||||
try {
|
||||
val disposable = e.getDisposable()
|
||||
disposable.launch {
|
||||
try {
|
||||
val backingFile = imageFile.copyToBackingFile(disposable)
|
||||
actionPerformed(e, backingFile)
|
||||
}
|
||||
catch (e: IllegalStateException) {
|
||||
thisLogger().warn("Failed to open external image editor", e)
|
||||
withContext(Dispatchers.EDT) {
|
||||
Messages.showErrorDialog(ImagesBundle.message("error.cannot.edit.image.external.editor"), UIBundle.message("error.dialog.title"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: IllegalStateException) {
|
||||
thisLogger().warn("Failed to open external image editor", e)
|
||||
Messages.showErrorDialog(ImagesBundle.message("error.cannot.edit.image.external.editor"), UIBundle.message("error.dialog.title"))
|
||||
}
|
||||
|
||||
e.presentation.isEnabled = enabled
|
||||
}
|
||||
}
|
||||
|
||||
override fun getActionUpdateThread(): ActionUpdateThread {
|
||||
return ActionUpdateThread.BGT
|
||||
private fun VirtualFile.copyToBackingFile(disposable: Disposable): VirtualFile {
|
||||
val filePath = Files.createTempFile("EditExternallyAction", name)
|
||||
Disposer.register(disposable, Disposable {
|
||||
filePath.safeDelete()
|
||||
})
|
||||
inputStream.use { inputStream ->
|
||||
try {
|
||||
Files.copy(inputStream, filePath, REPLACE_EXISTING)
|
||||
}
|
||||
catch (e: IOException) {
|
||||
filePath.safeDelete()
|
||||
throw IllegalStateException("Failed to create backing file", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
return LocalFileSystem.getInstance().refreshAndFindFileByNioFile(filePath) ?: throw IllegalStateException("Failed to create virtual file")
|
||||
}
|
||||
|
||||
private fun Path.safeDelete() {
|
||||
try {
|
||||
delete()
|
||||
}
|
||||
catch (ignore: IOException) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Disposable.launch(block: suspend CoroutineScope.() -> Unit) {
|
||||
val job = SupervisorJob()
|
||||
@Suppress("SSBasedInspection")
|
||||
CoroutineScope(job + Dispatchers.IO).launch {
|
||||
block()
|
||||
}
|
||||
Disposer.register(this, Disposable {
|
||||
job.cancel()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to get a Disposable from the event
|
||||
*
|
||||
* If `ImageComponentDecorator.DATA_KEY` exists and is a `Disposable`, use it (ImageEditorUI returns a
|
||||
* [org.intellij.images.editor.ImageEditor] which is a `Disposable`)
|
||||
* Otherwise, use PlatformCoreDataKeys.FILE_EDITOR.
|
||||
*/
|
||||
private fun AnActionEvent.getDisposable(): Disposable {
|
||||
val data = getData(ImageComponentDecorator.DATA_KEY)
|
||||
if (data is Disposable) {
|
||||
return data
|
||||
}
|
||||
return getData(PlatformCoreDataKeys.FILE_EDITOR)
|
||||
?: throw IllegalStateException("Component does not provide a Disposable object")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user