diff --git a/plugins/markdown/core/intellij.markdown.iml b/plugins/markdown/core/intellij.markdown.iml
index 2839d1456a9a..7d180994b4e9 100644
--- a/plugins/markdown/core/intellij.markdown.iml
+++ b/plugins/markdown/core/intellij.markdown.iml
@@ -34,5 +34,6 @@
+
\ No newline at end of file
diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/extensions/common/plantuml/PlantUMLCodeGeneratingProvider.kt b/plugins/markdown/core/src/org/intellij/plugins/markdown/extensions/common/plantuml/PlantUMLCodeGeneratingProvider.kt
index 8e2275a30cdb..a9e1af409419 100644
--- a/plugins/markdown/core/src/org/intellij/plugins/markdown/extensions/common/plantuml/PlantUMLCodeGeneratingProvider.kt
+++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/extensions/common/plantuml/PlantUMLCodeGeneratingProvider.kt
@@ -1,25 +1,25 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.intellij.plugins.markdown.extensions.common.plantuml
-import com.intellij.openapi.diagnostic.thisLogger
-import com.intellij.openapi.util.io.FileUtil
+import com.github.benmanes.caffeine.cache.Caffeine
import com.intellij.openapi.util.registry.Registry
import org.intellij.markdown.ast.ASTNode
import org.intellij.plugins.markdown.MarkdownBundle
+import org.intellij.plugins.markdown.extensions.CodeFenceGeneratingProvider
import org.intellij.plugins.markdown.extensions.MarkdownBrowserPreviewExtension
-import org.intellij.plugins.markdown.extensions.MarkdownCodeFenceCacheableProvider
import org.intellij.plugins.markdown.extensions.MarkdownExtensionWithDownloadableFiles
import org.intellij.plugins.markdown.extensions.MarkdownExtensionWithDownloadableFiles.FileEntry
import org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanel
-import org.intellij.plugins.markdown.ui.preview.html.MarkdownCodeFencePluginCacheCollector
+import org.intellij.plugins.markdown.ui.preview.html.MarkdownUtil
import org.jetbrains.annotations.ApiStatus
-import java.io.File
+import java.io.ByteArrayOutputStream
import java.io.IOException
+import java.util.*
@ApiStatus.Internal
-class PlantUMLCodeGeneratingProvider(
- collector: MarkdownCodeFencePluginCacheCollector? = null
-): MarkdownCodeFenceCacheableProvider(collector), MarkdownExtensionWithDownloadableFiles, MarkdownBrowserPreviewExtension.Provider {
+class PlantUMLCodeGeneratingProvider: CodeFenceGeneratingProvider, MarkdownExtensionWithDownloadableFiles, MarkdownBrowserPreviewExtension.Provider {
+ private val cache = Caffeine.newBuilder().softValues().build()
+
override val externalFiles: Iterable
get() = ownFiles
@@ -31,10 +31,21 @@ class PlantUMLCodeGeneratingProvider(
}
override fun generateHtml(language: String, raw: String, node: ASTNode): String {
- val key = getUniqueFile(language.lowercase(), raw, "png").toFile()
- cacheDiagram(key, raw)
- collector?.addAliveCachedFile(this, key)
- return "
"
+ val content = obtainGeneratedContent(raw)
+ val header = "data:image/png;base64,"
+ return """
"""
+ }
+
+ // Not thread safe
+ private fun obtainGeneratedContent(raw: String): String {
+ val key = MarkdownUtil.md5(raw, "salt")
+ val cached = cache.getIfPresent(key)
+ if (cached != null) {
+ return cached
+ }
+ val generated = generateDiagram(raw)
+ cache.put(key, generated)
+ return generated
}
override val displayName: String
@@ -60,36 +71,26 @@ class PlantUMLCodeGeneratingProvider(
return null
}
- private fun cacheDiagram(path: File, text: String) {
- if (!path.exists()) {
- generateDiagram(text, path)
- }
- }
-
@Throws(IOException::class)
- private fun generateDiagram(text: CharSequence, diagramPath: File) {
- var innerText: String = text.toString().trim()
- if (!innerText.startsWith("@startuml")) {
- innerText = "@startuml\n$innerText"
+ private fun generateDiagram(text: CharSequence): String {
+ val content = buildString {
+ if (!text.startsWith("@startuml")) {
+ append("@startuml\n")
+ }
+ append(text)
+ if (!text.endsWith("@enduml")) {
+ append("\n@enduml")
+ }
}
- if (!innerText.endsWith("@enduml")) {
- innerText += "\n@enduml"
- }
- FileUtil.createParentDirs(diagramPath)
- storeDiagram(innerText, diagramPath)
+ val stream = ByteArrayOutputStream()
+ PlantUMLJarManager.getInstance().generateImage(content, stream)
+ val encodedContent = Base64.getEncoder().encode(stream.toByteArray())
+ return encodedContent.toString(Charsets.UTF_8)
}
companion object {
const val jarFilename = "plantuml.jar"
private val ownFiles = listOf(jarFilename)
private val dowloadableFiles = listOf(FileEntry(jarFilename) { Registry.stringValue("markdown.plantuml.download.link") })
-
- private fun storeDiagram(source: String, file: File) {
- try {
- file.outputStream().buffered().use { PlantUMLJarManager.getInstance().generateImage(source, it) }
- } catch (exception: Exception) {
- thisLogger().warn("Cannot save diagram PlantUML diagram. ", exception)
- }
- }
}
}
diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/PreviewStaticServer.kt b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/PreviewStaticServer.kt
index 2a61e1127b7e..f6cf3ab12c1f 100644
--- a/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/PreviewStaticServer.kt
+++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/PreviewStaticServer.kt
@@ -9,7 +9,6 @@ import io.netty.buffer.Unpooled
import io.netty.channel.Channel
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.http.*
-import org.jetbrains.annotations.ApiStatus
import org.jetbrains.ide.BuiltInServerManager.Companion.getInstance
import org.jetbrains.ide.HttpRequestHandler
import org.jetbrains.io.FileResponses.checkCache
@@ -96,7 +95,7 @@ class PreviewStaticServer : HttpRequestHandler() {
default-src 'none';
script-src ${StringUtil.join(scripts.map(::stripQueryParameters), " ")};
style-src https: ${StringUtil.join(styles.map(::stripQueryParameters), " ")} 'unsafe-inline';
- img-src file: *; connect-src 'none'; font-src * data: *;
+ img-src file: * data:; connect-src 'none'; font-src * data: *;
object-src 'none'; media-src 'none'; child-src 'none';
"""
}
diff --git a/plugins/markdown/test/data/extensions/plantuml/plantUML1.md b/plugins/markdown/test/data/extensions/plantuml/plantUML1.md
deleted file mode 100644
index 99c16bf2b7b3..000000000000
--- a/plugins/markdown/test/data/extensions/plantuml/plantUML1.md
+++ /dev/null
@@ -1,5 +0,0 @@
-```plantuml
-@startuml
-Alice -> Bob: test
-@enduml
-```
\ No newline at end of file
diff --git a/plugins/markdown/test/data/extensions/plantuml/plantUML2.md b/plugins/markdown/test/data/extensions/plantuml/plantUML2.md
deleted file mode 100644
index 4a516912078e..000000000000
--- a/plugins/markdown/test/data/extensions/plantuml/plantUML2.md
+++ /dev/null
@@ -1,3 +0,0 @@
-```plantuml
-Alice -> Bob: test
-```
\ No newline at end of file
diff --git a/plugins/markdown/test/data/extensions/plantuml/puml.md b/plugins/markdown/test/data/extensions/plantuml/puml.md
deleted file mode 100644
index da24db2050c1..000000000000
--- a/plugins/markdown/test/data/extensions/plantuml/puml.md
+++ /dev/null
@@ -1,5 +0,0 @@
-```puml
-@startuml
-Alice -> Bob: test
-@enduml
-```
\ No newline at end of file
diff --git a/plugins/markdown/test/src/org/intellij/plugins/markdown/extensions/PlantUmlGenerationTest.kt b/plugins/markdown/test/src/org/intellij/plugins/markdown/extensions/PlantUmlGenerationTest.kt
deleted file mode 100644
index 9119c8be660d..000000000000
--- a/plugins/markdown/test/src/org/intellij/plugins/markdown/extensions/PlantUmlGenerationTest.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
-package org.intellij.plugins.markdown.extensions
-
-import com.intellij.openapi.Disposable
-import com.intellij.openapi.util.Disposer
-import com.intellij.openapi.vfs.VfsUtilCore
-import com.intellij.testFramework.LightPlatformCodeInsightTestCase
-import com.intellij.testFramework.rules.TempDirectory
-import org.intellij.plugins.markdown.MarkdownTestingUtil
-import org.intellij.plugins.markdown.extensions.common.plantuml.PlantUMLCodeGeneratingProvider
-import org.intellij.plugins.markdown.extensions.common.plantuml.PlantUMLJarManager
-import org.intellij.plugins.markdown.ui.preview.html.MarkdownCodeFenceHtmlCache
-import org.intellij.plugins.markdown.ui.preview.html.MarkdownUtil
-import org.junit.Rule
-import org.junit.Test
-import org.junit.rules.ExternalResource
-import org.junit.rules.RuleChain
-import org.junit.rules.TestRule
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import java.util.*
-
-
-@RunWith(JUnit4::class)
-class PlantUmlGenerationTest: LightPlatformCodeInsightTestCase() {
- private val tempDirectory = TempDirectory()
-
- @Rule
- @JvmField
- val rules: TestRule = RuleChain.outerRule(tempDirectory).around(SetupRule(tempDirectory))
-
- class SetupRule(private val tempDirectory: TempDirectory): ExternalResource() {
- private var disposable: Disposable? = null
-
- override fun before() {
- disposable = Disposer.newDisposable()
- ExtensionTestingUtil.mockPathManager(tempDirectory.newDirectoryPath(), disposable!!)
- val extension = requireNotNull(MarkdownExtensionsUtil.findCodeFenceGeneratingProvider())
- PlantUMLJarManager.getInstance().dropCache()
- ExtensionTestingUtil.downloadExtension(extension, project = null)
- val state = Collections.singletonMap(extension.id, true)
- ExtensionTestingUtil.replaceExtensionsState(state, disposable!!)
- }
-
- override fun after() {
- disposable?.let(Disposer::dispose)
- disposable = null
- PlantUMLJarManager.getInstance().dropCache()
- }
- }
-
- @Test
- fun `test plantUML1`() = doTest()
-
- @Test
- fun `test plantUML2`() = doTest()
-
- @Test
- fun `test puml`() = doTest()
-
- private fun doTest() {
- val virtualFile = getVirtualFile("${getTestName(true)}.md")
- val result = MarkdownUtil.generateMarkdownHtml(virtualFile, VfsUtilCore.loadText(virtualFile), project)
- val hash = MarkdownUtil.md5(virtualFile.path, MarkdownCodeFenceHtmlCache.MARKDOWN_FILE_PATH_KEY)
- println(result)
- assertTrue(result.contains(hash))
- }
-
- override fun getTestName(lowercaseFirstLetter: Boolean): String {
- val name = super.getTestName(lowercaseFirstLetter)
- return name.trimStart().replace(' ', '_')
- }
-
- override fun getTestDataPath(): String {
- return "${MarkdownTestingUtil.TEST_DATA_PATH}/extensions/plantuml/"
- }
-}