mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-13 15:52:01 +07:00
[markdown] IJPL-157064 initial compose-based preview implementation
Now it's hidden under a registry key ("markdown.experimental.use.compose.for.preview") and a checkbox in Markdown settings. It's a very early stage of development, lots of inlines are not unsupported yet, and styling is far from the JCEF-based renderer.
Merge-request: IJ-MR-140719
Merged-by: Alexander Kuznetsov <Aleksander.Kuznetsov@jetbrains.com>
GitOrigin-RevId: b4ad8a44e17aee1cfff32d20a3d2adc52f9fa9c5
This commit is contained in:
committed by
intellij-monorepo-bot
parent
a9860a447e
commit
ca86a0a4cc
1
.idea/modules.xml
generated
1
.idea/modules.xml
generated
@@ -526,6 +526,7 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/lombok/intellij.lombok.iml" filepath="$PROJECT_DIR$/plugins/lombok/intellij.lombok.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/lombok/generated/intellij.lombok.generated.iml" filepath="$PROJECT_DIR$/plugins/lombok/generated/intellij.lombok.generated.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/markdown/core/intellij.markdown.iml" filepath="$PROJECT_DIR$/plugins/markdown/core/intellij.markdown.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/markdown/compose/intellij.markdown.compose.preview.iml" filepath="$PROJECT_DIR$/plugins/markdown/compose/intellij.markdown.compose.preview.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/markdown/fenceInjection/intellij.markdown.fenceInjection.iml" filepath="$PROJECT_DIR$/plugins/markdown/fenceInjection/intellij.markdown.fenceInjection.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/markdown/frontmatter/intellij.markdown.frontmatter.iml" filepath="$PROJECT_DIR$/plugins/markdown/frontmatter/intellij.markdown.frontmatter.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/markdown/frontmatter/toml/intellij.markdown.frontmatter.toml.iml" filepath="$PROJECT_DIR$/plugins/markdown/frontmatter/toml/intellij.markdown.frontmatter.toml.iml" />
|
||||
|
||||
@@ -583,7 +583,7 @@ object CommunityLibraryLicenses {
|
||||
)
|
||||
.apache("https://github.com/JetBrains/jewel/blob/master/LICENSE")
|
||||
.suppliedByOrganizations(Suppliers.JETBRAINS),
|
||||
LibraryLicense(name = "Jetbrains Jewel Markdown LaF Standalone",
|
||||
LibraryLicense(name = "Jetbrains Jewel Markdown IDE LaF Bridge",
|
||||
url = "https://github.com/JetBrains/jewel",
|
||||
libraryName= "jetbrains-jewel-markdown-laf-bridge-styling",
|
||||
)
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
f:com.intellij.platform.compose.JBComposePanelKt
|
||||
- *sf:JBComposePanel(kotlin.jvm.functions.Function2):javax.swing.JComponent
|
||||
|
||||
@@ -22,7 +22,7 @@ import javax.swing.JComponent
|
||||
@Suppress("FunctionName")
|
||||
@OptIn(ExperimentalComposeUiApi::class, ExperimentalJewelApi::class)
|
||||
@Experimental
|
||||
internal fun JBComposePanel(
|
||||
fun JBComposePanel(
|
||||
content: @Composable () -> Unit
|
||||
): JComponent {
|
||||
if (ApplicationManager.getApplication().isInternal) {
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="kotlin-language" name="Kotlin">
|
||||
<configuration version="5" platform="JVM 17" allPlatforms="JVM [17]" useProjectSettings="false">
|
||||
<compilerSettings>
|
||||
<option name="additionalArguments" value="-Xjvm-default=all" />
|
||||
</compilerSettings>
|
||||
<compilerArguments>
|
||||
<stringArguments>
|
||||
<stringArg name="jvmTarget" arg="17" />
|
||||
<stringArg name="apiVersion" arg="1.9" />
|
||||
<stringArg name="languageVersion" arg="1.9" />
|
||||
</stringArguments>
|
||||
<arrayArguments>
|
||||
<arrayArg name="pluginClasspaths">
|
||||
<args>$MAVEN_REPOSITORY$/org/jetbrains/compose/compiler/compiler-hosted/1.5.14/compiler-hosted-1.5.14.jar</args>
|
||||
</arrayArg>
|
||||
<arrayArg name="pluginOptions">
|
||||
<args>plugin:androidx.compose.compiler.plugins.kotlin:suppressKotlinVersionCompatibilityCheck=1.9.25</args>
|
||||
</arrayArg>
|
||||
</arrayArguments>
|
||||
</compilerArguments>
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/kotlin" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="kotlin-stdlib" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.markdown" />
|
||||
<orderEntry type="module" module-name="intellij.platform.compose" />
|
||||
<orderEntry type="module-library">
|
||||
<library name="jetbrains-jewel-markdown-laf-bridge-styling" type="repository">
|
||||
<properties maven-id="org.jetbrains.jewel:jewel-markdown-ide-laf-bridge-styling-241:0.19.7">
|
||||
<verification>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/jetbrains/jewel/jewel-markdown-ide-laf-bridge-styling-241/0.19.7/jewel-markdown-ide-laf-bridge-styling-241-0.19.7.jar">
|
||||
<sha256sum>44f90aace7a3f8b9c811294f7705a4591d169f13b5d2299eed9f922be30dafce</sha256sum>
|
||||
</artifact>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/jetbrains/jewel/jewel-markdown-core-241/0.19.7/jewel-markdown-core-241-0.19.7.jar">
|
||||
<sha256sum>422b42dab33648dcb2b413a06707e100c6e70470918a42a1d760e4b02a1401a3</sha256sum>
|
||||
</artifact>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/commonmark/commonmark/0.22.0/commonmark-0.22.0.jar">
|
||||
<sha256sum>3417909f2997bc8c61d90d64c6af29f4a3f5b6729caceaef97221ceac93df814</sha256sum>
|
||||
</artifact>
|
||||
</verification>
|
||||
<exclude>
|
||||
<dependency maven-id="org.jetbrains.jewel:jewel-ui-241" />
|
||||
<dependency maven-id="org.jetbrains.jewel:jewel-ide-laf-bridge-241" />
|
||||
</exclude>
|
||||
</properties>
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/jewel/jewel-markdown-ide-laf-bridge-styling-241/0.19.7/jewel-markdown-ide-laf-bridge-styling-241-0.19.7.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/jewel/jewel-markdown-core-241/0.19.7/jewel-markdown-core-241-0.19.7.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/commonmark/commonmark/0.22.0/commonmark-0.22.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/jewel/jewel-markdown-ide-laf-bridge-styling-241/0.19.7/jewel-markdown-ide-laf-bridge-styling-241-0.19.7-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/jewel/jewel-markdown-core-241/0.19.7/jewel-markdown-core-241-0.19.7-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/commonmark/commonmark/0.22.0/commonmark-0.22.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</orderEntry>
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.markdown.compose.preview
|
||||
|
||||
import com.intellij.idea.AppMode
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanel
|
||||
import org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanelProvider
|
||||
import org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanelProvider.AvailabilityInfo
|
||||
import org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanelProvider.ProviderInfo
|
||||
import org.jetbrains.jewel.foundation.ExperimentalJewelApi
|
||||
|
||||
@OptIn(ExperimentalJewelApi::class)
|
||||
private class ComposePanelProvider : MarkdownHtmlPanelProvider() {
|
||||
|
||||
override fun createHtmlPanel(): MarkdownHtmlPanel {
|
||||
return MarkdownComposePanel()
|
||||
}
|
||||
|
||||
override fun createHtmlPanel(project: Project, virtualFile: VirtualFile): MarkdownHtmlPanel {
|
||||
return MarkdownComposePanel(project, virtualFile)
|
||||
}
|
||||
|
||||
override fun isAvailable(): AvailabilityInfo {
|
||||
if (Registry.`is`("markdown.experimental.use.compose.for.preview", false) && !AppMode.isRemoteDevHost()) {
|
||||
return AvailabilityInfo.AVAILABLE
|
||||
}
|
||||
return AvailabilityInfo.UNAVAILABLE
|
||||
}
|
||||
|
||||
override fun getProviderInfo() = ProviderInfo("Compose-based", ComposePanelProvider::class.java.name)
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.markdown.compose.preview
|
||||
|
||||
import androidx.compose.foundation.ScrollState
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.rememberScrollbarAdapter
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.intellij.ide.BrowserUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.UserDataHolder
|
||||
import com.intellij.openapi.util.UserDataHolderBase
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.platform.compose.JBComposePanel
|
||||
import org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanel
|
||||
import org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanelEx
|
||||
import org.intellij.plugins.markdown.ui.preview.MarkdownUpdateHandler
|
||||
import org.intellij.plugins.markdown.ui.preview.MarkdownUpdateHandler.PreviewRequest
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.jewel.foundation.ExperimentalJewelApi
|
||||
import org.jetbrains.jewel.intui.markdown.bridge.ProvideMarkdownStyling
|
||||
import org.jetbrains.jewel.markdown.Markdown
|
||||
import org.jetbrains.jewel.ui.component.VerticalScrollbar
|
||||
import javax.swing.JComponent
|
||||
|
||||
@ExperimentalJewelApi
|
||||
class MarkdownComposePanel(
|
||||
private val project: Project?,
|
||||
private val virtualFile: VirtualFile?,
|
||||
private val updateHandler: MarkdownUpdateHandler = MarkdownUpdateHandler.Debounced()
|
||||
) : MarkdownHtmlPanelEx, UserDataHolder by UserDataHolderBase() {
|
||||
|
||||
constructor() : this(null, null)
|
||||
|
||||
private val panelComponent by lazy {
|
||||
JBComposePanel {
|
||||
// TODO temporary styling, we will likely need our own in the future for JCEF-like rendering
|
||||
ProvideMarkdownStyling {
|
||||
MarkdownPanel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
@Composable
|
||||
private fun MarkdownPanel() {
|
||||
Box {
|
||||
val scrollState = rememberScrollState(0)
|
||||
MarkdownPreviewPanel(scrollState)
|
||||
VerticalScrollbar(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterEnd),
|
||||
adapter = rememberScrollbarAdapter(scrollState),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
@Composable
|
||||
private fun MarkdownPreviewPanel(scrollState: ScrollState) {
|
||||
val request by updateHandler.requests.collectAsState(null)
|
||||
(request as? PreviewRequest.Update)?.let {
|
||||
Markdown(
|
||||
it.content,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
.verticalScroll(scrollState),
|
||||
enabled = true,
|
||||
onUrlClick = { url -> BrowserUtil.open(url) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setHtml(html: String, initialScrollOffset: Int, document: VirtualFile?) {
|
||||
updateHandler.setContent(html, initialScrollOffset, document)
|
||||
}
|
||||
|
||||
override fun reloadWithOffset(offset: Int) {
|
||||
updateHandler.reloadWithOffset(offset)
|
||||
}
|
||||
|
||||
override fun getComponent(): JComponent {
|
||||
return panelComponent
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
override fun getProject(): Project? = project
|
||||
|
||||
@ApiStatus.Experimental
|
||||
override fun getVirtualFile(): VirtualFile? = virtualFile
|
||||
|
||||
override fun addScrollListener(listener: MarkdownHtmlPanel.ScrollListener) {
|
||||
}
|
||||
|
||||
override fun removeScrollListener(listener: MarkdownHtmlPanel.ScrollListener) {
|
||||
}
|
||||
|
||||
override fun scrollToMarkdownSrcOffset(offset: Int, smooth: Boolean) {
|
||||
}
|
||||
|
||||
override fun scrollBy(horizontalUnits: Int, verticalUnits: Int) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<idea-plugin package="com.intellij.markdown.compose.preview">
|
||||
<dependencies>
|
||||
<module name="intellij.platform.compose"/>
|
||||
</dependencies>
|
||||
|
||||
<extensions defaultExtensionNs="org.intellij.markdown">
|
||||
<html.panel.provider implementation="com.intellij.markdown.compose.preview.ComposePanelProvider"/>
|
||||
</extensions>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<registryKey key="markdown.experimental.use.compose.for.preview"
|
||||
defaultValue="false"
|
||||
description="Renders Markdown Preview using Compose instead of JCEF (experimental feature, many inlines are unsupported yet)."/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
@@ -10,3 +10,14 @@
|
||||
- name: intellij.markdown.xml
|
||||
- name: intellij.markdown.model
|
||||
- name: intellij.markdown.spellchecker
|
||||
- name: lib/modules/intellij.markdown.compose.preview.jar
|
||||
contentModules:
|
||||
- name: intellij.markdown.compose.preview
|
||||
libraries:
|
||||
jetbrains-jewel-markdown-laf-bridge-styling:
|
||||
- name: $MAVEN_REPOSITORY$/org/jetbrains/jewel/jewel-markdown-ide-laf-bridge-styling-241/0.19.7/jewel-markdown-ide-laf-bridge-styling-241-0.19.7.jar
|
||||
size: 28320
|
||||
- name: $MAVEN_REPOSITORY$/org/jetbrains/jewel/jewel-markdown-core-241/0.19.7/jewel-markdown-core-241-0.19.7.jar
|
||||
size: 294476
|
||||
- name: $MAVEN_REPOSITORY$/org/commonmark/commonmark/0.22.0/commonmark-0.22.0.jar
|
||||
size: 193845
|
||||
@@ -17,6 +17,7 @@
|
||||
<module name="intellij.markdown.xml"/>
|
||||
<module name="intellij.markdown.model"/>
|
||||
<module name="intellij.markdown.spellchecker"/>
|
||||
<module name="intellij.markdown.compose.preview"/>
|
||||
</content>
|
||||
|
||||
<description><![CDATA[
|
||||
|
||||
@@ -9,7 +9,7 @@ import org.jetbrains.annotations.ApiStatus
|
||||
@ApiStatus.Experimental
|
||||
@Service(Service.Level.APP)
|
||||
@State(name = "MarkdownSettings", storages = [(Storage("markdown.xml"))])
|
||||
internal class MarkdownPreviewSettings: SimplePersistentStateComponent<MarkdownPreviewSettings.State>(State()) {
|
||||
class MarkdownPreviewSettings: SimplePersistentStateComponent<MarkdownPreviewSettings.State>(State()) {
|
||||
class State: BaseState() {
|
||||
var fontSize by property(defaultFontSize)
|
||||
}
|
||||
|
||||
@@ -40,6 +40,10 @@ public abstract class MarkdownHtmlPanelProvider {
|
||||
|
||||
public abstract @NotNull ProviderInfo getProviderInfo();
|
||||
|
||||
public @NotNull SourceTextPreprocessor getSourceTextPreprocessor() {
|
||||
return SourceTextPreprocessor.getDefault();
|
||||
}
|
||||
|
||||
public static @NotNull List<MarkdownHtmlPanelProvider> getProviders() {
|
||||
return EP_NAME.getExtensionList();
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ import kotlinx.coroutines.launch
|
||||
import org.intellij.plugins.markdown.MarkdownBundle
|
||||
import org.intellij.plugins.markdown.settings.MarkdownExtensionsSettings
|
||||
import org.intellij.plugins.markdown.settings.MarkdownSettings
|
||||
import org.intellij.plugins.markdown.ui.preview.html.MarkdownUtil.generateMarkdownHtml
|
||||
import org.intellij.plugins.markdown.ui.preview.jcef.MarkdownJCEFHtmlPanel
|
||||
import org.intellij.plugins.markdown.util.MarkdownPluginScope
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
@@ -204,10 +203,12 @@ class MarkdownPreviewFileEditor(
|
||||
return
|
||||
}
|
||||
|
||||
val html = readAction { generateMarkdownHtml(file, document.text, project) }
|
||||
val settings = MarkdownSettings.getInstance(project)
|
||||
val textPreprocessor = retrievePanelProvider(settings).sourceTextPreprocessor
|
||||
lastRenderedHtml = readAction {
|
||||
textPreprocessor.preprocessText(project, document, file)
|
||||
}
|
||||
|
||||
val currentHtml = "<html><head></head>$html</html>"
|
||||
lastRenderedHtml = currentHtml
|
||||
val editor = mainEditor.firstOrNull() ?: return
|
||||
writeIntentReadAction {
|
||||
val offset = editor.caretModel.offset
|
||||
@@ -229,7 +230,8 @@ class MarkdownPreviewFileEditor(
|
||||
@RequiresEdt
|
||||
private suspend fun attachHtmlPanel() {
|
||||
val settings = MarkdownSettings.getInstance(project)
|
||||
val panel = retrievePanelProvider(settings).createHtmlPanel(project, file)
|
||||
val panelProvider = retrievePanelProvider(settings)
|
||||
val panel = panelProvider.createHtmlPanel(project, file)
|
||||
this.panel = panel
|
||||
htmlPanelWrapper.add(panel.component, BorderLayout.CENTER)
|
||||
if (htmlPanelWrapper.isShowing) htmlPanelWrapper.validate()
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.intellij.plugins.markdown.ui.preview
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
abstract class MarkdownUpdateHandler {
|
||||
|
||||
abstract val requests: Flow<PreviewRequest>
|
||||
protected abstract fun addRequest(request: PreviewRequest): Boolean
|
||||
|
||||
fun setContent(content: String, initialScrollOffset: Int, document: VirtualFile?) {
|
||||
doRequest(PreviewRequest.Update(content, initialScrollOffset, document))
|
||||
}
|
||||
|
||||
fun reloadWithOffset(offset: Int) {
|
||||
doRequest(PreviewRequest.ReloadWithOffset(offset))
|
||||
}
|
||||
|
||||
private fun doRequest(request: PreviewRequest) {
|
||||
check(addRequest(request))
|
||||
}
|
||||
|
||||
sealed interface PreviewRequest {
|
||||
data class Update(
|
||||
val content: String,
|
||||
val initialScrollOffset: Int,
|
||||
val document: VirtualFile?
|
||||
) : PreviewRequest
|
||||
|
||||
data class ReloadWithOffset(val offset: Int) : PreviewRequest
|
||||
}
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
class Debounced(private val debounceTimeout: Duration = 20.milliseconds) : MarkdownUpdateHandler() {
|
||||
|
||||
private val _updateViewRequests = MutableSharedFlow<PreviewRequest>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||
|
||||
override val requests: Flow<PreviewRequest>
|
||||
get() = _updateViewRequests.debounce(debounceTimeout)
|
||||
|
||||
override fun addRequest(request: PreviewRequest): Boolean = _updateViewRequests.tryEmit(request)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.intellij.plugins.markdown.ui.preview
|
||||
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
|
||||
interface SourceTextPreprocessor {
|
||||
fun preprocessText(project: Project, document: Document, file: VirtualFile): String
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
val default: SourceTextPreprocessor = object : SourceTextPreprocessor {
|
||||
override fun preprocessText(project: Project, document: Document, file: VirtualFile): String {
|
||||
return document.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.intellij.plugins.markdown.ui.preview.jcef
|
||||
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.intellij.plugins.markdown.ui.preview.SourceTextPreprocessor
|
||||
import org.intellij.plugins.markdown.ui.preview.html.MarkdownUtil.generateMarkdownHtml
|
||||
|
||||
class HtmlSourceTextPreprocessor : SourceTextPreprocessor {
|
||||
override fun preprocessText(project: Project, document: Document, file: VirtualFile): String {
|
||||
val html = generateMarkdownHtml(file, document.text, project)
|
||||
return "<html><head></head>$html</html>"
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,9 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.jcef.JBCefApp;
|
||||
import org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanel;
|
||||
import org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanelProvider;
|
||||
import org.intellij.plugins.markdown.ui.preview.SourceTextPreprocessor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public final class JCEFHtmlPanelProvider extends MarkdownHtmlPanelProvider {
|
||||
|
||||
@NotNull
|
||||
@@ -39,6 +38,11 @@ public final class JCEFHtmlPanelProvider extends MarkdownHtmlPanelProvider {
|
||||
return new ProviderInfo("JCEF Browser", JCEFHtmlPanelProvider.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull SourceTextPreprocessor getSourceTextPreprocessor() {
|
||||
return new HtmlSourceTextPreprocessor();
|
||||
}
|
||||
|
||||
public static boolean canBeUsed() {
|
||||
return !AppMode.isRemoteDevHost() && JBCefApp.isSupported();
|
||||
}
|
||||
|
||||
@@ -24,10 +24,7 @@ import com.intellij.ui.jcef.JCEFHtmlPanel
|
||||
import com.intellij.util.application
|
||||
import com.intellij.util.net.NetUtils
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import org.cef.browser.CefBrowser
|
||||
import org.cef.browser.CefFrame
|
||||
import org.cef.handler.CefRequestHandlerAdapter
|
||||
@@ -41,6 +38,7 @@ import org.intellij.plugins.markdown.extensions.MarkdownConfigurableExtension
|
||||
import org.intellij.plugins.markdown.settings.MarkdownPreviewSettings
|
||||
import org.intellij.plugins.markdown.settings.MarkdownSettingsConfigurable.Companion.fontSizeOptions
|
||||
import org.intellij.plugins.markdown.ui.preview.*
|
||||
import org.intellij.plugins.markdown.ui.preview.MarkdownUpdateHandler.PreviewRequest
|
||||
import org.intellij.plugins.markdown.ui.preview.jcef.impl.*
|
||||
import org.intellij.plugins.markdown.ui.preview.jcef.zoomIndicator.PreviewZoomIndicatorManager
|
||||
import org.intellij.plugins.markdown.util.MarkdownApplicationScope
|
||||
@@ -101,6 +99,8 @@ class MarkdownJCEFHtmlPanel(
|
||||
styles.map { PreviewStaticServer.getStaticUrl(resourceProvider, it) }
|
||||
)
|
||||
|
||||
private val updateHandler = MarkdownUpdateHandler.Debounced()
|
||||
|
||||
private fun buildIndexContent(): String {
|
||||
// language=HTML
|
||||
return """
|
||||
@@ -126,18 +126,6 @@ class MarkdownJCEFHtmlPanel(
|
||||
|
||||
private val coroutineScope = project?.let(MarkdownPluginScope::createChildScope) ?: MarkdownApplicationScope.createChildScope()
|
||||
|
||||
private sealed interface PreviewRequest {
|
||||
data class Update(
|
||||
val content: String,
|
||||
val initialScrollOffset: Int,
|
||||
val document: VirtualFile?,
|
||||
) : PreviewRequest
|
||||
|
||||
data class ReloadWithOffset(val offset: Int) : PreviewRequest
|
||||
}
|
||||
|
||||
private val updateViewRequests = MutableSharedFlow<PreviewRequest>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||
|
||||
private val projectRoot = coroutineScope.async(context = Dispatchers.Default) {
|
||||
if (virtualFile != null && project != null) {
|
||||
BaseProjectDirectories.getInstance(project).getBaseDirectoryFor(virtualFile)
|
||||
@@ -186,7 +174,7 @@ class MarkdownJCEFHtmlPanel(
|
||||
val fileSchemeResourcesProcessor = createFileSchemeResourcesProcessor(projectRoot)
|
||||
|
||||
loadIndexContent()
|
||||
updateViewRequests.debounce(20.milliseconds).collectLatest { request ->
|
||||
updateHandler.requests.collectLatest { request ->
|
||||
when (request) {
|
||||
is PreviewRequest.Update -> {
|
||||
val (html, initialScrollOffset, document) = request
|
||||
@@ -239,7 +227,7 @@ class MarkdownJCEFHtmlPanel(
|
||||
}
|
||||
|
||||
override fun setHtml(html: String, initialScrollOffset: Int, document: VirtualFile?) {
|
||||
check(updateViewRequests.tryEmit(PreviewRequest.Update(content = html, initialScrollOffset, document)))
|
||||
updateHandler.setContent(html, initialScrollOffset, document)
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@@ -253,7 +241,7 @@ class MarkdownJCEFHtmlPanel(
|
||||
}
|
||||
|
||||
override fun reloadWithOffset(offset: Int) {
|
||||
check(updateViewRequests.tryEmit(PreviewRequest.ReloadWithOffset(offset)))
|
||||
updateHandler.reloadWithOffset(offset)
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
|
||||
@@ -12,5 +12,6 @@
|
||||
<orderEntry type="module" module-name="intellij.markdown.model" />
|
||||
<orderEntry type="module" module-name="intellij.markdown.spellchecker" />
|
||||
<orderEntry type="module" module-name="intellij.markdown.xml" />
|
||||
<orderEntry type="module" module-name="intellij.markdown.compose.preview" />
|
||||
</component>
|
||||
</module>
|
||||
Reference in New Issue
Block a user