mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[Markdown][IJPL-90975] Replace file system cache for plantUML extension
GitOrigin-RevId: f1c843d6060e9158f7245592f0b8730732cd68b5
This commit is contained in:
committed by
intellij-monorepo-bot
parent
129419aa70
commit
5a5c335cf6
@@ -34,5 +34,6 @@
|
||||
<orderEntry type="library" name="kotlinx-coroutines-core" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.diagnostic" />
|
||||
<orderEntry type="module" module-name="intellij.platform.util.coroutines" />
|
||||
<orderEntry type="library" name="caffeine" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
+36
-35
@@ -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<String, String>()
|
||||
|
||||
override val externalFiles: Iterable<String>
|
||||
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 "<img src=\"${key.toURI()}\" from-extension=true/>"
|
||||
val content = obtainGeneratedContent(raw)
|
||||
val header = "data:image/png;base64,"
|
||||
return """<img src="$header$content" from-extension=true/>"""
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -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';
|
||||
"""
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
```plantuml
|
||||
@startuml
|
||||
Alice -> Bob: test
|
||||
@enduml
|
||||
```
|
||||
@@ -1,3 +0,0 @@
|
||||
```plantuml
|
||||
Alice -> Bob: test
|
||||
```
|
||||
@@ -1,5 +0,0 @@
|
||||
```puml
|
||||
@startuml
|
||||
Alice -> Bob: test
|
||||
@enduml
|
||||
```
|
||||
-77
@@ -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<PlantUMLCodeGeneratingProvider>())
|
||||
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/"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user