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/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/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/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/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/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" />
|
<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")
|
.apache("https://github.com/JetBrains/jewel/blob/master/LICENSE")
|
||||||
.suppliedByOrganizations(Suppliers.JETBRAINS),
|
.suppliedByOrganizations(Suppliers.JETBRAINS),
|
||||||
LibraryLicense(name = "Jetbrains Jewel Markdown LaF Standalone",
|
LibraryLicense(name = "Jetbrains Jewel Markdown IDE LaF Bridge",
|
||||||
url = "https://github.com/JetBrains/jewel",
|
url = "https://github.com/JetBrains/jewel",
|
||||||
libraryName= "jetbrains-jewel-markdown-laf-bridge-styling",
|
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")
|
@Suppress("FunctionName")
|
||||||
@OptIn(ExperimentalComposeUiApi::class, ExperimentalJewelApi::class)
|
@OptIn(ExperimentalComposeUiApi::class, ExperimentalJewelApi::class)
|
||||||
@Experimental
|
@Experimental
|
||||||
internal fun JBComposePanel(
|
fun JBComposePanel(
|
||||||
content: @Composable () -> Unit
|
content: @Composable () -> Unit
|
||||||
): JComponent {
|
): JComponent {
|
||||||
if (ApplicationManager.getApplication().isInternal) {
|
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>
|
||||||
@@ -9,4 +9,15 @@
|
|||||||
- name: intellij.markdown.images
|
- name: intellij.markdown.images
|
||||||
- name: intellij.markdown.xml
|
- name: intellij.markdown.xml
|
||||||
- name: intellij.markdown.model
|
- name: intellij.markdown.model
|
||||||
- name: intellij.markdown.spellchecker
|
- 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.xml"/>
|
||||||
<module name="intellij.markdown.model"/>
|
<module name="intellij.markdown.model"/>
|
||||||
<module name="intellij.markdown.spellchecker"/>
|
<module name="intellij.markdown.spellchecker"/>
|
||||||
|
<module name="intellij.markdown.compose.preview"/>
|
||||||
</content>
|
</content>
|
||||||
|
|
||||||
<description><![CDATA[
|
<description><![CDATA[
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import org.jetbrains.annotations.ApiStatus
|
|||||||
@ApiStatus.Experimental
|
@ApiStatus.Experimental
|
||||||
@Service(Service.Level.APP)
|
@Service(Service.Level.APP)
|
||||||
@State(name = "MarkdownSettings", storages = [(Storage("markdown.xml"))])
|
@State(name = "MarkdownSettings", storages = [(Storage("markdown.xml"))])
|
||||||
internal class MarkdownPreviewSettings: SimplePersistentStateComponent<MarkdownPreviewSettings.State>(State()) {
|
class MarkdownPreviewSettings: SimplePersistentStateComponent<MarkdownPreviewSettings.State>(State()) {
|
||||||
class State: BaseState() {
|
class State: BaseState() {
|
||||||
var fontSize by property(defaultFontSize)
|
var fontSize by property(defaultFontSize)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ public abstract class MarkdownHtmlPanelProvider {
|
|||||||
|
|
||||||
public abstract @NotNull ProviderInfo getProviderInfo();
|
public abstract @NotNull ProviderInfo getProviderInfo();
|
||||||
|
|
||||||
|
public @NotNull SourceTextPreprocessor getSourceTextPreprocessor() {
|
||||||
|
return SourceTextPreprocessor.getDefault();
|
||||||
|
}
|
||||||
|
|
||||||
public static @NotNull List<MarkdownHtmlPanelProvider> getProviders() {
|
public static @NotNull List<MarkdownHtmlPanelProvider> getProviders() {
|
||||||
return EP_NAME.getExtensionList();
|
return EP_NAME.getExtensionList();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ import kotlinx.coroutines.launch
|
|||||||
import org.intellij.plugins.markdown.MarkdownBundle
|
import org.intellij.plugins.markdown.MarkdownBundle
|
||||||
import org.intellij.plugins.markdown.settings.MarkdownExtensionsSettings
|
import org.intellij.plugins.markdown.settings.MarkdownExtensionsSettings
|
||||||
import org.intellij.plugins.markdown.settings.MarkdownSettings
|
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.ui.preview.jcef.MarkdownJCEFHtmlPanel
|
||||||
import org.intellij.plugins.markdown.util.MarkdownPluginScope
|
import org.intellij.plugins.markdown.util.MarkdownPluginScope
|
||||||
import org.jetbrains.annotations.ApiStatus.Internal
|
import org.jetbrains.annotations.ApiStatus.Internal
|
||||||
@@ -204,10 +203,12 @@ class MarkdownPreviewFileEditor(
|
|||||||
return
|
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
|
val editor = mainEditor.firstOrNull() ?: return
|
||||||
writeIntentReadAction {
|
writeIntentReadAction {
|
||||||
val offset = editor.caretModel.offset
|
val offset = editor.caretModel.offset
|
||||||
@@ -229,7 +230,8 @@ class MarkdownPreviewFileEditor(
|
|||||||
@RequiresEdt
|
@RequiresEdt
|
||||||
private suspend fun attachHtmlPanel() {
|
private suspend fun attachHtmlPanel() {
|
||||||
val settings = MarkdownSettings.getInstance(project)
|
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
|
this.panel = panel
|
||||||
htmlPanelWrapper.add(panel.component, BorderLayout.CENTER)
|
htmlPanelWrapper.add(panel.component, BorderLayout.CENTER)
|
||||||
if (htmlPanelWrapper.isShowing) htmlPanelWrapper.validate()
|
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 com.intellij.ui.jcef.JBCefApp;
|
||||||
import org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanel;
|
import org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanel;
|
||||||
import org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanelProvider;
|
import org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanelProvider;
|
||||||
|
import org.intellij.plugins.markdown.ui.preview.SourceTextPreprocessor;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
public final class JCEFHtmlPanelProvider extends MarkdownHtmlPanelProvider {
|
public final class JCEFHtmlPanelProvider extends MarkdownHtmlPanelProvider {
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -39,6 +38,11 @@ public final class JCEFHtmlPanelProvider extends MarkdownHtmlPanelProvider {
|
|||||||
return new ProviderInfo("JCEF Browser", JCEFHtmlPanelProvider.class.getName());
|
return new ProviderInfo("JCEF Browser", JCEFHtmlPanelProvider.class.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull SourceTextPreprocessor getSourceTextPreprocessor() {
|
||||||
|
return new HtmlSourceTextPreprocessor();
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean canBeUsed() {
|
public static boolean canBeUsed() {
|
||||||
return !AppMode.isRemoteDevHost() && JBCefApp.isSupported();
|
return !AppMode.isRemoteDevHost() && JBCefApp.isSupported();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,10 +24,7 @@ import com.intellij.ui.jcef.JCEFHtmlPanel
|
|||||||
import com.intellij.util.application
|
import com.intellij.util.application
|
||||||
import com.intellij.util.net.NetUtils
|
import com.intellij.util.net.NetUtils
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.channels.BufferOverflow
|
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.debounce
|
|
||||||
import org.cef.browser.CefBrowser
|
import org.cef.browser.CefBrowser
|
||||||
import org.cef.browser.CefFrame
|
import org.cef.browser.CefFrame
|
||||||
import org.cef.handler.CefRequestHandlerAdapter
|
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.MarkdownPreviewSettings
|
||||||
import org.intellij.plugins.markdown.settings.MarkdownSettingsConfigurable.Companion.fontSizeOptions
|
import org.intellij.plugins.markdown.settings.MarkdownSettingsConfigurable.Companion.fontSizeOptions
|
||||||
import org.intellij.plugins.markdown.ui.preview.*
|
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.impl.*
|
||||||
import org.intellij.plugins.markdown.ui.preview.jcef.zoomIndicator.PreviewZoomIndicatorManager
|
import org.intellij.plugins.markdown.ui.preview.jcef.zoomIndicator.PreviewZoomIndicatorManager
|
||||||
import org.intellij.plugins.markdown.util.MarkdownApplicationScope
|
import org.intellij.plugins.markdown.util.MarkdownApplicationScope
|
||||||
@@ -101,6 +99,8 @@ class MarkdownJCEFHtmlPanel(
|
|||||||
styles.map { PreviewStaticServer.getStaticUrl(resourceProvider, it) }
|
styles.map { PreviewStaticServer.getStaticUrl(resourceProvider, it) }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val updateHandler = MarkdownUpdateHandler.Debounced()
|
||||||
|
|
||||||
private fun buildIndexContent(): String {
|
private fun buildIndexContent(): String {
|
||||||
// language=HTML
|
// language=HTML
|
||||||
return """
|
return """
|
||||||
@@ -126,18 +126,6 @@ class MarkdownJCEFHtmlPanel(
|
|||||||
|
|
||||||
private val coroutineScope = project?.let(MarkdownPluginScope::createChildScope) ?: MarkdownApplicationScope.createChildScope()
|
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) {
|
private val projectRoot = coroutineScope.async(context = Dispatchers.Default) {
|
||||||
if (virtualFile != null && project != null) {
|
if (virtualFile != null && project != null) {
|
||||||
BaseProjectDirectories.getInstance(project).getBaseDirectoryFor(virtualFile)
|
BaseProjectDirectories.getInstance(project).getBaseDirectoryFor(virtualFile)
|
||||||
@@ -186,7 +174,7 @@ class MarkdownJCEFHtmlPanel(
|
|||||||
val fileSchemeResourcesProcessor = createFileSchemeResourcesProcessor(projectRoot)
|
val fileSchemeResourcesProcessor = createFileSchemeResourcesProcessor(projectRoot)
|
||||||
|
|
||||||
loadIndexContent()
|
loadIndexContent()
|
||||||
updateViewRequests.debounce(20.milliseconds).collectLatest { request ->
|
updateHandler.requests.collectLatest { request ->
|
||||||
when (request) {
|
when (request) {
|
||||||
is PreviewRequest.Update -> {
|
is PreviewRequest.Update -> {
|
||||||
val (html, initialScrollOffset, document) = request
|
val (html, initialScrollOffset, document) = request
|
||||||
@@ -239,7 +227,7 @@ class MarkdownJCEFHtmlPanel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun setHtml(html: String, initialScrollOffset: Int, document: VirtualFile?) {
|
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
|
@ApiStatus.Internal
|
||||||
@@ -253,7 +241,7 @@ class MarkdownJCEFHtmlPanel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun reloadWithOffset(offset: Int) {
|
override fun reloadWithOffset(offset: Int) {
|
||||||
check(updateViewRequests.tryEmit(PreviewRequest.ReloadWithOffset(offset)))
|
updateHandler.reloadWithOffset(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
|
|||||||
@@ -12,5 +12,6 @@
|
|||||||
<orderEntry type="module" module-name="intellij.markdown.model" />
|
<orderEntry type="module" module-name="intellij.markdown.model" />
|
||||||
<orderEntry type="module" module-name="intellij.markdown.spellchecker" />
|
<orderEntry type="module" module-name="intellij.markdown.spellchecker" />
|
||||||
<orderEntry type="module" module-name="intellij.markdown.xml" />
|
<orderEntry type="module" module-name="intellij.markdown.xml" />
|
||||||
|
<orderEntry type="module" module-name="intellij.markdown.compose.preview" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
||||||
Reference in New Issue
Block a user