From 8fa367e1474343d84f5d0f7e41d7be0c0810fee5 Mon Sep 17 00:00:00 2001 From: samgst-amazon Date: Tue, 6 May 2025 12:25:03 -0700 Subject: [PATCH 1/7] clicking q chat diff opens diff view in editor --- .../amazonq/lsp/AmazonQLanguageClient.kt | 5 ++ .../amazonq/lsp/AmazonQLanguageClientImpl.kt | 46 +++++++++++++++++++ .../lsp/model/aws/chat/FlareChatCommands.kt | 1 + .../lsp/model/aws/chat/OpenFileDiffParams.kt | 11 +++++ .../resources/MessagesBundle.properties | 1 + 5 files changed, 64 insertions(+) create mode 100644 plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/OpenFileDiffParams.kt diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClient.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClient.kt index a7dad74b179..5cb296926f4 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClient.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClient.kt @@ -11,6 +11,8 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ChatU import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.GET_SERIALIZED_CHAT_REQUEST_METHOD import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.GetSerializedChatParams import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.GetSerializedChatResult +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.OPEN_FILE_DIFF +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.OpenFileDiffParams import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.OpenTabParams import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.OpenTabResult import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SHOW_SAVE_FILE_DIALOG_REQUEST_METHOD @@ -38,4 +40,7 @@ interface AmazonQLanguageClient : LanguageClient { @JsonNotification(CHAT_SEND_UPDATE) fun sendChatUpdate(params: ChatUpdateParams): CompletableFuture + + @JsonNotification(OPEN_FILE_DIFF) + fun openFileDiff(params: OpenFileDiffParams): CompletableFuture } diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt index f389e8b9b77..97745cabe20 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt @@ -4,6 +4,9 @@ package software.aws.toolkits.jetbrains.services.amazonq.lsp import com.google.gson.Gson +import com.intellij.diff.DiffContentFactory +import com.intellij.diff.DiffManager +import com.intellij.diff.requests.SimpleDiffRequest import com.intellij.notification.NotificationType import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.fileChooser.FileChooserFactory @@ -34,6 +37,7 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ChatU import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.GET_SERIALIZED_CHAT_REQUEST_METHOD import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.GetSerializedChatParams import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.GetSerializedChatResult +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.OpenFileDiffParams import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.OpenTabParams import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.OpenTabResult import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ShowSaveFileDialogParams @@ -42,6 +46,9 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credential import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials.SsoProfileData import software.aws.toolkits.jetbrains.services.codewhisperer.customization.CodeWhispererModelConfigurator import software.aws.toolkits.jetbrains.settings.CodeWhispererSettings +import software.aws.toolkits.resources.message +import java.io.File +import java.nio.file.Paths import java.util.UUID import java.util.concurrent.CompletableFuture import java.util.concurrent.TimeUnit @@ -262,6 +269,45 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC return CompletableFuture.completedFuture(Unit) } + override fun openFileDiff(params: OpenFileDiffParams): CompletableFuture = + CompletableFuture.supplyAsync({ + ApplicationManager.getApplication().invokeLater { + try { + val contentFactory = DiffContentFactory.getInstance() + val fileName = Paths.get(params.originalFileUri).fileName.toString() + + val originalContent = params.originalFileContent ?: run { + val file = File(params.originalFileUri) + if (file.exists()) file.readText() else "" + } + val (leftContent, rightContent) = when { + params.isDeleted -> { + // For deleted files, show original on left, empty on right + contentFactory.create(originalContent) to + contentFactory.createEmpty() + } + else -> { + // For new or modified files + val newContent = params.fileContent.orEmpty() + contentFactory.create(originalContent) to + contentFactory.create(newContent) + } + } + val diffRequest = SimpleDiffRequest( + "$fileName ${message("aws.q.lsp.client.diff_message")}", + leftContent, + rightContent, + "Original", + if (params.isDeleted) "Deleted" else "Modified" + ) + DiffManager.getInstance().showDiff(project, diffRequest) + } catch (e: Exception) { + LOG.warn { "Failed to open file diff: ${e.message}" } + } + } + Unit + }) + companion object { private val LOG = getLogger() } diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/FlareChatCommands.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/FlareChatCommands.kt index 02934b87db1..ef044e51875 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/FlareChatCommands.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/FlareChatCommands.kt @@ -31,3 +31,4 @@ const val CHAT_SEND_UPDATE = "aws/chat/sendChatUpdate" const val CHAT_CREATE_PROMPT = "aws/chat/createPrompt" const val SHOW_SAVE_FILE_DIALOG_REQUEST_METHOD = "aws/showSaveFileDialog" const val GET_SERIALIZED_CHAT_REQUEST_METHOD = "aws/chat/getSerializedChat" +const val OPEN_FILE_DIFF = "aws/openFileDiff" diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/OpenFileDiffParams.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/OpenFileDiffParams.kt new file mode 100644 index 00000000000..e0eb4870bf4 --- /dev/null +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/OpenFileDiffParams.kt @@ -0,0 +1,11 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat + +data class OpenFileDiffParams( + val originalFileUri: String, + val originalFileContent: String? = null, + val isDeleted: Boolean, + val fileContent: String? = null, +) diff --git a/plugins/core/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties b/plugins/core/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties index 81a1e774b75..da293b986ee 100644 --- a/plugins/core/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties +++ b/plugins/core/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties @@ -252,6 +252,7 @@ aws.onboarding.getstarted.panel.login_with_iam=Use Professional License aws.onboarding.getstarted.panel.share_feedback=Share Feedback aws.onboarding.getstarted.panel.signup_iam_text=Sign up for free aws.onboarding.getstarted.panel.title=Authenticate with AWS Toolkit +aws.q.lsp.client.diff_message=(Generated by Amazon Q) aws.q.migration.action.install.text=Install aws.q.migration.action.manage_plugins.text=Manage plugins aws.q.migration.action.read_more.text=Read more From 1d04d50578b0f1590ff8470040bfdb92d81482c7 Mon Sep 17 00:00:00 2001 From: samgst-amazon Date: Tue, 6 May 2025 14:45:16 -0700 Subject: [PATCH 2/7] feedback --- .../services/amazonq/lsp/AmazonQLanguageClientImpl.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt index 97745cabe20..5d891c1e379 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt @@ -271,7 +271,6 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC override fun openFileDiff(params: OpenFileDiffParams): CompletableFuture = CompletableFuture.supplyAsync({ - ApplicationManager.getApplication().invokeLater { try { val contentFactory = DiffContentFactory.getInstance() val fileName = Paths.get(params.originalFileUri).fileName.toString() @@ -304,9 +303,9 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC } catch (e: Exception) { LOG.warn { "Failed to open file diff: ${e.message}" } } - } - Unit - }) + }, + ApplicationManager.getApplication()::invokeLater + ) companion object { private val LOG = getLogger() From 1ab34894a776433bb2c597f6b6cb56843f203dab Mon Sep 17 00:00:00 2001 From: samgst-amazon Date: Tue, 6 May 2025 14:47:39 -0700 Subject: [PATCH 3/7] detekt --- .../services/amazonq/lsp/AmazonQLanguageClientImpl.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt index 5d891c1e379..f9ba4b44bd1 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt @@ -270,7 +270,8 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC } override fun openFileDiff(params: OpenFileDiffParams): CompletableFuture = - CompletableFuture.supplyAsync({ + CompletableFuture.supplyAsync( + { try { val contentFactory = DiffContentFactory.getInstance() val fileName = Paths.get(params.originalFileUri).fileName.toString() From b20850b65d2cdede1e5c86a8623b24808412eaeb Mon Sep 17 00:00:00 2001 From: samgst-amazon Date: Tue, 6 May 2025 15:11:07 -0700 Subject: [PATCH 4/7] file diff syntax highlighting --- .../amazonq/lsp/AmazonQLanguageClientImpl.kt | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt index f9ba4b44bd1..c56c37ff3e4 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt @@ -6,6 +6,7 @@ package software.aws.toolkits.jetbrains.services.amazonq.lsp import com.google.gson.Gson import com.intellij.diff.DiffContentFactory import com.intellij.diff.DiffManager +import com.intellij.diff.DiffManagerEx import com.intellij.diff.requests.SimpleDiffRequest import com.intellij.notification.NotificationType import com.intellij.openapi.application.ApplicationManager @@ -13,6 +14,7 @@ import com.intellij.openapi.fileChooser.FileChooserFactory import com.intellij.openapi.fileChooser.FileSaverDescriptor import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VirtualFileManager import migration.software.aws.toolkits.jetbrains.settings.AwsSettings import org.eclipse.lsp4j.ConfigurationParams @@ -48,6 +50,7 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.customization.Code import software.aws.toolkits.jetbrains.settings.CodeWhispererSettings import software.aws.toolkits.resources.message import java.io.File +import java.nio.file.Files import java.nio.file.Paths import java.util.UUID import java.util.concurrent.CompletableFuture @@ -269,30 +272,40 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC return CompletableFuture.completedFuture(Unit) } + private fun File.toVirtualFile() = LocalFileSystem.getInstance().findFileByIoFile(this) + override fun openFileDiff(params: OpenFileDiffParams): CompletableFuture = CompletableFuture.supplyAsync( { + var tempPath: java.nio.file.Path? = null try { val contentFactory = DiffContentFactory.getInstance() val fileName = Paths.get(params.originalFileUri).fileName.toString() + // Create a temporary virtual file for syntax highlighting + val fileExtension = fileName.substringAfterLast('.', "") + tempPath = Files.createTempFile(null, ".$fileExtension") + val virtualFile = tempPath.toFile().toVirtualFile() + val originalContent = params.originalFileContent ?: run { val file = File(params.originalFileUri) if (file.exists()) file.readText() else "" } + val (leftContent, rightContent) = when { params.isDeleted -> { // For deleted files, show original on left, empty on right - contentFactory.create(originalContent) to + contentFactory.create(originalContent, virtualFile) to contentFactory.createEmpty() } else -> { // For new or modified files val newContent = params.fileContent.orEmpty() - contentFactory.create(originalContent) to - contentFactory.create(newContent) + contentFactory.create(originalContent, virtualFile) to + contentFactory.create(newContent, virtualFile) } } + val diffRequest = SimpleDiffRequest( "$fileName ${message("aws.q.lsp.client.diff_message")}", leftContent, @@ -300,9 +313,17 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC "Original", if (params.isDeleted) "Deleted" else "Modified" ) - DiffManager.getInstance().showDiff(project, diffRequest) + + (DiffManager.getInstance() as DiffManagerEx).showDiffBuiltin(project, diffRequest) } catch (e: Exception) { LOG.warn { "Failed to open file diff: ${e.message}" } + } finally { + // Clean up the temporary file + try { + tempPath?.let { Files.deleteIfExists(it) } + } catch (e: Exception) { + LOG.warn { "Failed to delete temporary file: ${e.message}" } + } } }, ApplicationManager.getApplication()::invokeLater From 07780c494df1ab7c888a61a14a82849115ac2189 Mon Sep 17 00:00:00 2001 From: samgst-amazon Date: Wed, 7 May 2025 15:27:18 -0700 Subject: [PATCH 5/7] unified view for created/deleted files --- .../amazonq/lsp/AmazonQLanguageClientImpl.kt | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt index c56c37ff3e4..d80bf16696b 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt @@ -279,9 +279,7 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC { var tempPath: java.nio.file.Path? = null try { - val contentFactory = DiffContentFactory.getInstance() val fileName = Paths.get(params.originalFileUri).fileName.toString() - // Create a temporary virtual file for syntax highlighting val fileExtension = fileName.substringAfterLast('.', "") tempPath = Files.createTempFile(null, ".$fileExtension") @@ -292,28 +290,39 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC if (file.exists()) file.readText() else "" } + val contentFactory = DiffContentFactory.getInstance() + var isNewFile = false val (leftContent, rightContent) = when { params.isDeleted -> { - // For deleted files, show original on left, empty on right - contentFactory.create(originalContent, virtualFile) to + contentFactory.create(project, originalContent, virtualFile) to contentFactory.createEmpty() } else -> { - // For new or modified files val newContent = params.fileContent.orEmpty() - contentFactory.create(originalContent, virtualFile) to - contentFactory.create(newContent, virtualFile) + isNewFile = newContent == originalContent + when { + isNewFile -> { + contentFactory.createEmpty() to + contentFactory.create(project, newContent, virtualFile) + } + else -> { + contentFactory.create(project, originalContent, virtualFile) to + contentFactory.create(project, newContent, virtualFile) + } + } } } - val diffRequest = SimpleDiffRequest( "$fileName ${message("aws.q.lsp.client.diff_message")}", leftContent, rightContent, "Original", - if (params.isDeleted) "Deleted" else "Modified" + when { + params.isDeleted -> "Deleted" + isNewFile -> "Created" + else -> "Modified" + } ) - (DiffManager.getInstance() as DiffManagerEx).showDiffBuiltin(project, diffRequest) } catch (e: Exception) { LOG.warn { "Failed to open file diff: ${e.message}" } From e84d0c943f774827006a92e794107d5f7da3ca27 Mon Sep 17 00:00:00 2001 From: samgst-amazon Date: Wed, 7 May 2025 17:56:19 -0700 Subject: [PATCH 6/7] restrict temp file permissions --- .../services/amazonq/lsp/AmazonQLanguageClientImpl.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt index d80bf16696b..daf30ab1759 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt @@ -283,7 +283,9 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC // Create a temporary virtual file for syntax highlighting val fileExtension = fileName.substringAfterLast('.', "") tempPath = Files.createTempFile(null, ".$fileExtension") - val virtualFile = tempPath.toFile().toVirtualFile() + val file = tempPath.toFile() + file.setReadOnly() + val virtualFile = file.toVirtualFile() val originalContent = params.originalFileContent ?: run { val file = File(params.originalFileUri) From 938b1e5ab178bb73f7c0a3e6d211da8eb2e3b2f2 Mon Sep 17 00:00:00 2001 From: samgst-amazon Date: Wed, 7 May 2025 22:37:24 -0700 Subject: [PATCH 7/7] detekt --- .../services/amazonq/lsp/AmazonQLanguageClientImpl.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt index daf30ab1759..45bd77b8749 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt @@ -283,13 +283,13 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC // Create a temporary virtual file for syntax highlighting val fileExtension = fileName.substringAfterLast('.', "") tempPath = Files.createTempFile(null, ".$fileExtension") - val file = tempPath.toFile() - file.setReadOnly() - val virtualFile = file.toVirtualFile() + val virtualFile = tempPath.toFile() + .also { it.setReadOnly() } + .toVirtualFile() val originalContent = params.originalFileContent ?: run { - val file = File(params.originalFileUri) - if (file.exists()) file.readText() else "" + val sourceFile = File(params.originalFileUri) + if (sourceFile.exists()) sourceFile.readText() else "" } val contentFactory = DiffContentFactory.getInstance()