mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
added simple preview for restructured text
This commit is contained in:
@@ -26,4 +26,8 @@
|
||||
</action>
|
||||
|
||||
</actions>
|
||||
|
||||
<extensions defaultExtensionNs="restructured.text">
|
||||
<html.preview.provider implementation="com.jetbrains.rest.RestPythonPreviewProvider" />
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.rest;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.process.ProcessOutput;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.jetbrains.python.sdk.PySdkUtil;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import com.jetbrains.rest.editor.RestPreviewProvider;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.jetbrains.python.PythonHelper.REST_RUNNER;
|
||||
|
||||
public class RestPythonPreviewProvider extends RestPreviewProvider {
|
||||
|
||||
@Nullable
|
||||
public String toHtml(String text, VirtualFile virtualFile, Project project) {
|
||||
Module module = ProjectFileIndex.SERVICE.getInstance(project).getModuleForFile(virtualFile);
|
||||
if (module == null) return null;
|
||||
|
||||
final Sdk sdk = PythonSdkType.findPythonSdk(module);
|
||||
if (sdk == null) {
|
||||
return null;
|
||||
}
|
||||
final String sdkHomePath = sdk.getHomePath();
|
||||
if (sdkHomePath == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final GeneralCommandLine commandLine = REST_RUNNER.newCommandLine(sdkHomePath, Lists.newArrayList("rst2html"));
|
||||
final ProcessOutput output = PySdkUtil.getProcessOutput(commandLine, virtualFile.getParent().getPath(), null, 5000,
|
||||
text.getBytes(CharsetToolkit.UTF8_CHARSET), true);
|
||||
if (output.isCancelled() || output.isTimeout()) return null;
|
||||
|
||||
return output.getStdout();
|
||||
}
|
||||
}
|
||||
@@ -15,5 +15,6 @@
|
||||
<orderEntry type="library" name="Guava" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.spellchecker" />
|
||||
<orderEntry type="module" module-name="intellij.platform.testFramework" scope="TEST" />
|
||||
<orderEntry type="library" name="com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20180219.1" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -1,4 +1,8 @@
|
||||
<idea-plugin>
|
||||
<extensionPoints>
|
||||
<extensionPoint qualifiedName="restructured.text.html.preview.provider" interface="com.jetbrains.rest.editor.RestPreviewProvider" />
|
||||
</extensionPoints>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
|
||||
<fileTypeFactory implementation="com.jetbrains.rest.RestFileTypeFactory"/>
|
||||
@@ -25,6 +29,8 @@
|
||||
|
||||
<indexPatternBuilder implementation="com.jetbrains.rest.RestIndexPatternBuilder" />
|
||||
<todoIndexer filetype="ReST" implementationClass="com.jetbrains.rest.RestTodoIndexer" />
|
||||
|
||||
<fileEditorProvider implementation="com.jetbrains.rest.editor.RestSplitEditorProvider"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij.spellchecker">
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.rest.editor;
|
||||
|
||||
import com.intellij.codeHighlighting.BackgroundEditorHighlighter;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.event.DocumentListener;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorLocation;
|
||||
import com.intellij.openapi.fileEditor.FileEditorState;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.NotNullLazyValue;
|
||||
import com.intellij.openapi.util.UserDataHolderBase;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.javafx.JavaFxHtmlPanel;
|
||||
import com.intellij.util.Alarm;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.owasp.html.HtmlPolicyBuilder;
|
||||
import org.owasp.html.PolicyFactory;
|
||||
import org.owasp.html.Sanitizers;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.beans.PropertyChangeListener;
|
||||
|
||||
public class RestPreviewFileEditor extends UserDataHolderBase implements FileEditor {
|
||||
private final static long PARSING_CALL_TIMEOUT_MS = 50L;
|
||||
|
||||
private final static long RENDERING_DELAY_MS = 20L;
|
||||
|
||||
final static NotNullLazyValue<PolicyFactory> SANITIZER_VALUE = new NotNullLazyValue<PolicyFactory>() {
|
||||
@NotNull
|
||||
@Override
|
||||
protected PolicyFactory compute() {
|
||||
return Sanitizers.BLOCKS
|
||||
.and(Sanitizers.FORMATTING)
|
||||
.and(new HtmlPolicyBuilder()
|
||||
.allowUrlProtocols("file", "http", "https").allowElements("img")
|
||||
.allowAttributes("alt", "src", "title").onElements("img")
|
||||
.allowAttributes("border", "height", "width").onElements("img")
|
||||
.toFactory())
|
||||
.and(new HtmlPolicyBuilder()
|
||||
.allowUrlProtocols("file", "http", "https", "mailto").allowElements("a")
|
||||
.allowAttributes("href", "title").onElements("a")
|
||||
.toFactory())
|
||||
.and(Sanitizers.TABLES)
|
||||
.and(new HtmlPolicyBuilder()
|
||||
.allowElements("code", "tr")
|
||||
.allowAttributes("class").onElements("code", "tr")
|
||||
.toFactory());
|
||||
}
|
||||
};
|
||||
@NotNull
|
||||
private final JavaFxHtmlPanel myPanel;
|
||||
@NotNull
|
||||
private final VirtualFile myFile;
|
||||
private final Project myProject;
|
||||
@Nullable
|
||||
private final Document myDocument;
|
||||
@NotNull
|
||||
private final Alarm myPooledAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, this);
|
||||
@NotNull
|
||||
private final Alarm mySwingAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, this);
|
||||
|
||||
private final Object REQUESTS_LOCK = new Object();
|
||||
@Nullable
|
||||
private Runnable myLastRequest = null;
|
||||
|
||||
@NotNull
|
||||
private String myLastRenderedHtml = "";
|
||||
|
||||
public RestPreviewFileEditor(@NotNull VirtualFile file, Project project) {
|
||||
myFile = file;
|
||||
myProject = project;
|
||||
myDocument = FileDocumentManager.getInstance().getDocument(myFile);
|
||||
myPanel = new JavaFxHtmlPanel();
|
||||
|
||||
if (myDocument != null) {
|
||||
myDocument.addDocumentListener(new DocumentListener() {
|
||||
|
||||
@Override
|
||||
public void beforeDocumentChange(DocumentEvent e) {
|
||||
myPooledAlarm.cancelAllRequests();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void documentChanged(final DocumentEvent e) {
|
||||
myPooledAlarm.addRequest(() -> updateHtml(), PARSING_CALL_TIMEOUT_MS);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return myPanel.getComponent();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
return myPanel.getComponent();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return "ReStructuredText HTML Preview";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setState(@NotNull FileEditorState state) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModified() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectNotify() {
|
||||
myPooledAlarm.cancelAllRequests();
|
||||
myPooledAlarm.addRequest(() -> updateHtml(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is always run from pooled thread
|
||||
*/
|
||||
private void updateHtml() {
|
||||
if (!myFile.isValid() || myDocument == null || Disposer.isDisposed(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String html = RestPreviewProvider.getProviders()[0].toHtml(myDocument.getText(), myFile, myProject);
|
||||
if (html == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// EA-75860: The lines to the top may be processed slowly; Since we're in pooled thread, we can be disposed already.
|
||||
if (!myFile.isValid() || Disposer.isDisposed(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (REQUESTS_LOCK) {
|
||||
if (myLastRequest != null) {
|
||||
mySwingAlarm.cancelRequest(myLastRequest);
|
||||
}
|
||||
myLastRequest = () -> {
|
||||
final String currentHtml = "<html>" + SANITIZER_VALUE.getValue().sanitize(html) + "</html>";
|
||||
if (!currentHtml.equals(myLastRenderedHtml)) {
|
||||
myLastRenderedHtml = currentHtml;
|
||||
myPanel.setHtml(myLastRenderedHtml);
|
||||
}
|
||||
|
||||
myPanel.render();
|
||||
synchronized (REQUESTS_LOCK) {
|
||||
myLastRequest = null;
|
||||
}
|
||||
};
|
||||
mySwingAlarm.addRequest(myLastRequest, RENDERING_DELAY_MS, ModalityState.stateForComponent(getComponent()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deselectNotify() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPropertyChangeListener(@NotNull PropertyChangeListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePropertyChangeListener(@NotNull PropertyChangeListener listener) {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BackgroundEditorHighlighter getBackgroundHighlighter() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public FileEditorLocation getCurrentLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
Disposer.dispose(myPanel);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.rest.editor;
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class RestPreviewProvider {
|
||||
public static final ExtensionPointName<RestPreviewProvider> EP_NAME =
|
||||
ExtensionPointName.create("restructured.text.html.preview.provider");
|
||||
|
||||
@Nullable
|
||||
public abstract String toHtml(String text, VirtualFile virtualFile, Project project);
|
||||
|
||||
@NotNull
|
||||
public static RestPreviewProvider[] getProviders() {
|
||||
return EP_NAME.getExtensions();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.rest.editor;
|
||||
|
||||
import com.intellij.openapi.fileEditor.*;
|
||||
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.jetbrains.rest.RestFileType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class RestSplitEditorProvider implements FileEditorProvider, DumbAware {
|
||||
public RestSplitEditorProvider() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(@NotNull Project project, @NotNull VirtualFile file) {
|
||||
return file.getFileType() == RestFileType.INSTANCE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public FileEditor createEditor(@NotNull Project project, @NotNull VirtualFile file) {
|
||||
TextEditor editor = (TextEditor)TextEditorProvider.getInstance().createEditor(project, file);
|
||||
return new TextEditorWithPreview(editor, new RestPreviewFileEditor(file, project));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getEditorTypeId() {
|
||||
return "restructured-text-editor";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public FileEditorPolicy getPolicy() {
|
||||
return FileEditorPolicy.HIDE_DEFAULT_EDITOR;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user