Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/keenwrite.git

Continued to replace pegdown with common mark for HTML preview.

Authordjarvis <email>
Date2016-11-17 23:42:14 GMT-0800
Commit2ea3163b7ba9c55a084afde9c9082b93a3800d6a
Parent2ac480c
Delta398 lines added, 397 lines removed, 1-line increase
src/main/java/com/scrivendor/preview/MarkdownPreviewPane.java
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
-import javafx.scene.Node;
import javafx.scene.control.ScrollPane;
import static javafx.scene.control.ScrollPane.ScrollBarPolicy.ALWAYS;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
-import org.commonmark.renderer.html.HtmlWriter;
+import org.commonmark.node.Node;
+import org.commonmark.parser.Parser;
+import org.commonmark.renderer.html.HtmlRenderer;
/**
* Markdown preview pane.
*
* @author Karl Tauber and White Magic Software, Ltd.
*/
-public final class MarkdownPreviewPane extends ScrollPane implements ChangeListener {
+public final class MarkdownPreviewPane extends ScrollPane implements ChangeListener<String> {
private final ObjectProperty<Path> path = new SimpleObjectProperty<>();
public MarkdownPreviewPane() {
- setVbarPolicy(ALWAYS);
+ setVbarPolicy( ALWAYS );
- pathProperty().addListener((observable, oldValue, newValue) -> {
+ pathProperty().addListener( (observable, oldValue, newValue) -> {
update();
- });
+ } );
- scrollYProperty().addListener((observable, oldValue, newValue) -> {
+ scrollYProperty().addListener( (observable, oldValue, newValue) -> {
scrollY();
- });
+ } );
}
@Override
- public void changed(ObservableValue observable, Object oldValue, Object newValue) {
- final StringBuilder sb = new StringBuilder();
- final HtmlWriter writer = new HtmlWriter(sb);
- writer.text(newValue == null ? "" : newValue.toString());
- setHtml(sb.toString());
+ public void changed(
+ final ObservableValue<? extends String> observable,
+ final String oldValue,
+ final String newValue ) {
+ final String markdown = newValue == null ? "" : newValue;
+ final HtmlRenderer renderer = HtmlRenderer.builder().build();
+ final Parser parser = Parser.builder().build();
+ final Node document = parser.parse( markdown );
+
+ setHtml( renderer.render( document ) );
update();
}
private void update() {
- if (!getEngine().getLoadWorker().isRunning()) {
+ if( !getEngine().getLoadWorker().isRunning() ) {
setScrollXY();
}
getEngine().loadContent(
"<!DOCTYPE html>"
+ "<html>"
+ "<head>"
- + "<link rel='stylesheet' href='" + getClass().getResource("markdownpad-github.css") + "'>"
+ + "<link rel='stylesheet' href='" + getClass().getResource( "markdownpad-github.css" ) + "'>"
+ getBase()
+ "</head>"
+ "<body" + getScrollScript() + ">"
+ getHtml()
+ "</body>"
- + "</html>");
+ + "</html>" );
}
/**
* Obtain the window.scrollX and window.scrollY from web engine, but only no
* worker is running (in this case the result would be zero).
*/
private void setScrollXY() {
- lastScrollX = getNumber(execute("window.scrollX"));
- lastScrollY = getNumber(execute("window.scrollY"));
+ lastScrollX = getNumber( execute( "window.scrollX" ) );
+ lastScrollY = getNumber( execute( "window.scrollY" ) );
}
- private int getNumber(final Object number) {
- return (number instanceof Number) ? ((Number) number).intValue() : 0;
+ private int getNumber( final Object number ) {
+ return (number instanceof Number) ? ((Number)number).intValue() : 0;
}
*/
private void scrollY() {
- if (!delayScroll) {
+ if( !delayScroll ) {
delayScroll = true;
- Platform.runLater(() -> {
+ Platform.runLater( () -> {
delayScroll = false;
- scrollY(getScrollY());
- });
+ scrollY( getScrollY() );
+ } );
}
}
- private void scrollY(final double value) {
+ private void scrollY( final double value ) {
execute(
"window.scrollTo(0, (document.body.scrollHeight - window.innerHeight) * "
+ value
- + ");");
+ + ");" );
}
public Path getPath() {
return pathProperty().get();
}
- public void setPath(final Path path) {
- pathProperty().set(path);
+ public void setPath( final Path path ) {
+ pathProperty().set( path );
}
}
- public void setScrollY(final double value) {
- scrollYProperty().set(value);
+ public void setScrollY( final double value ) {
+ scrollYProperty().set( value );
}
public DoubleProperty scrollYProperty() {
return this.scrollY;
- }
-
- public Node getNode() {
- return getWebView();
}
- private Object execute(final String script) {
- return getEngine().executeScript(script);
+ private Object execute( final String script ) {
+ return getEngine().executeScript( script );
}
private WebEngine getEngine() {
return getWebView().getEngine();
}
- private WebView getWebView() {
+ public WebView getWebView() {
return this.webView;
}
private String getHtml() {
return this.html == null ? "" : this.html;
}
- private void setHtml(final String html) {
+ private void setHtml( final String html ) {
this.html = html;
}
src/main/java/com/scrivendor/editor/MarkdownSyntaxHighlighter.java
* @author Karl Tauber, White Magic Software, Ltd.
*/
-class MarkdownSyntaxHighlighter
- implements Visitor {
-
- private enum StyleClass {
- // headers
- h1,
- h2,
- h3,
- h4,
- h5,
- h6,
- // inlines
- strong,
- em,
- del,
- a,
- img,
- code,
- // blocks
- pre,
- blockquote,
- // lists
- ul,
- ol,
- li,
- dl,
- dt,
- dd,
- // tables
- table,
- thead,
- tbody,
- caption,
- th,
- tr,
- td,
- // misc
- html,
- monospace,
- };
-
- /**
- * Style bits (1 &lt;&lt; StyleClass.ordinal()) for each character to simplify
- * overlapping styles implementation.
- */
- private int[] styleClassBits;
- private boolean inTableHeader;
-
- static void highlight( StyleClassedTextArea textArea, RootNode astRoot ) {
- assert StyleClass.values().length <= 32;
- assert Platform.isFxApplicationThread();
-
- textArea.setStyleSpans( 0, new MarkdownSyntaxHighlighter()
- .computeHighlighting( astRoot, textArea.getLength() ) );
- }
-
- private MarkdownSyntaxHighlighter() {
- }
-
- private StyleSpans<Collection<String>> computeHighlighting( RootNode astRoot, int textLength ) {
- styleClassBits = new int[ textLength ];
-
- // visit all nodes
- astRoot.accept( this );
-
- // build style spans
- StyleSpansBuilder<Collection<String>> spansBuilder = new StyleSpansBuilder<>();
- if( styleClassBits.length > 0 ) {
- int spanStart = 0;
- int previousBits = styleClassBits[ 0 ];
-
- for( int i = 1; i < styleClassBits.length; i++ ) {
- int bits = styleClassBits[ i ];
- if( bits == previousBits ) {
- continue;
- }
-
- spansBuilder.add( toStyleClasses( previousBits ), i - spanStart );
-
- spanStart = i;
- previousBits = bits;
- }
- spansBuilder.add( toStyleClasses( previousBits ), styleClassBits.length - spanStart );
- } else {
- spansBuilder.add( Collections.emptyList(), 0 );
- }
- return spansBuilder.create();
- }
-
- private Collection<String> toStyleClasses( int bits ) {
- if( bits == 0 ) {
- return Collections.emptyList();
- }
-
- Collection<String> styleClasses = new ArrayList<>( 1 );
- for( StyleClass styleClass : StyleClass.values() ) {
- if( (bits & (1 << styleClass.ordinal())) != 0 ) {
- styleClasses.add( styleClass.name() );
- }
- }
- return styleClasses;
- }
-
- @Override
- public void visit( AbbreviationNode node ) {
- // noting to do here
- }
-
- @Override
- public void visit( AnchorLinkNode node ) {
- // noting to do here
- }
-
- @Override
- public void visit( AutoLinkNode node ) {
- setStyleClass( node, StyleClass.a );
- }
-
- @Override
- public void visit( BlockQuoteNode node ) {
- setStyleClass( node, StyleClass.blockquote );
- visitChildren( node );
- }
-
- @Override
- public void visit( BulletListNode node ) {
- setStyleClass( node, StyleClass.ul );
- visitChildren( node );
- }
-
- @Override
- public void visit( CodeNode node ) {
- setStyleClass( node, StyleClass.code );
- }
-
- @Override
- public void visit( DefinitionListNode node ) {
- setStyleClass( node, StyleClass.dl );
- visitChildren( node );
- }
-
- @Override
- public void visit( DefinitionNode node ) {
- setStyleClass( node, StyleClass.dd );
- visitChildren( node );
- }
-
- @Override
- public void visit( DefinitionTermNode node ) {
- setStyleClass( node, StyleClass.dt );
- visitChildren( node );
- }
-
- @Override
- public void visit( ExpImageNode node ) {
- setStyleClass( node, StyleClass.img );
- visitChildren( node );
- }
-
- @Override
- public void visit( ExpLinkNode node ) {
- setStyleClass( node, StyleClass.a );
- visitChildren( node );
- }
-
- @Override
- public void visit( HeaderNode node ) {
- StyleClass styleClass;
- switch( node.getLevel() ) {
- case 1:
- styleClass = StyleClass.h1;
- break;
- case 2:
- styleClass = StyleClass.h2;
- break;
- case 3:
- styleClass = StyleClass.h3;
- break;
- case 4:
- styleClass = StyleClass.h4;
- break;
- case 5:
- styleClass = StyleClass.h5;
- break;
- case 6:
- styleClass = StyleClass.h6;
- break;
- default:
- return;
- }
- setStyleClass( node, styleClass );
-
- // use monospace font for underlined headers
- if( !node.getChildren().isEmpty()
- && node.getChildren().get( 0 ).getStartIndex() == node.getStartIndex() ) {
- setStyleClass( node, StyleClass.monospace );
- }
-
- visitChildren( node );
- }
-
- @Override
- public void visit( HtmlBlockNode node ) {
- setStyleClass( node, StyleClass.html );
- }
-
- @Override
- public void visit( InlineHtmlNode node ) {
- setStyleClass( node, StyleClass.html );
- }
-
- @Override
- public void visit( ListItemNode node ) {
- setStyleClass( node, StyleClass.li );
- visitChildren( node );
- }
-
- @Override
- public void visit( MailLinkNode node ) {
- setStyleClass( node, StyleClass.a );
- }
-
- @Override
- public void visit( OrderedListNode node ) {
- setStyleClass( node, StyleClass.ol );
- visitChildren( node );
- }
-
- @Override
- public void visit( ParaNode node ) {
- visitChildren( node );
- }
-
- @Override
- public void visit( QuotedNode node ) {
- // noting to do here
- }
-
- @Override
- public void visit( ReferenceNode node ) {
- // noting to do here
- }
-
- @Override
- public void visit( RefImageNode node ) {
- setStyleClass( node, StyleClass.img );
- visitChildren( node );
- }
-
- @Override
- public void visit( RefLinkNode node ) {
- setStyleClass( node, StyleClass.a );
- visitChildren( node );
- }
-
- @Override
- public void visit( RootNode node ) {
- visitChildren( node );
- }
-
- @Override
- public void visit( SimpleNode node ) {
- // noting to do here
- }
-
- @Override
- public void visit( SpecialTextNode node ) {
- // noting to do here
- }
-
- @Override
- public void visit( StrikeNode node ) {
- setStyleClass( node, StyleClass.del );
- visitChildren( node );
- }
-
- @Override
- public void visit( StrongEmphSuperNode node ) {
- if( node.isClosed() ) {
- setStyleClass( node, node.isStrong() ? StyleClass.strong : StyleClass.em );
- }
- // else sequence was not closed, treat open chars as ordinary chars
-
- visitChildren( node );
- }
-
- @Override
- public void visit( TableBodyNode node ) {
- setStyleClass( node, StyleClass.tbody );
- visitChildren( node );
- }
-
- @Override
- public void visit( TableCaptionNode node ) {
- setStyleClass( node, StyleClass.caption );
- visitChildren( node );
- }
-
- @Override
- public void visit( TableCellNode node ) {
- setStyleClass( node, inTableHeader ? StyleClass.th : StyleClass.td );
- visitChildren( node );
- }
-
- @Override
- public void visit( TableColumnNode node ) {
- // noting to do here
- }
-
- @Override
- public void visit( TableHeaderNode node ) {
- setStyleClass( node, StyleClass.thead );
-
- inTableHeader = true;
- visitChildren( node );
- inTableHeader = false;
- }
-
- @Override
- public void visit( TableNode node ) {
- setStyleClass( node, StyleClass.table );
- visitChildren( node );
- }
-
- @Override
- public void visit( TableRowNode node ) {
- setStyleClass( node, StyleClass.tr );
- visitChildren( node );
- }
-
- @Override
- public void visit( VerbatimNode node ) {
- setStyleClass( node, StyleClass.pre );
- }
-
- @Override
- public void visit( WikiLinkNode node ) {
- setStyleClass( node, StyleClass.a );
- }
-
- @Override
- public void visit( TextNode node ) {
- // noting to do here
- }
-
- @Override
- public void visit( SuperNode node ) {
- visitChildren( node );
- }
-
- @Override
- public void visit( Node node ) {
- // ignore custom Node implementations
- }
-
- private void visitChildren( SuperNode node ) {
- node.getChildren().stream().forEach((child) -> {
- child.accept( this );
- });
+class MarkdownSyntaxHighlighter implements Visitor {
+
+ private enum StyleClass {
+ // headers
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6,
+ // inlines
+ strong,
+ em,
+ del,
+ a,
+ img,
+ code,
+ // blocks
+ pre,
+ blockquote,
+ // lists
+ ul,
+ ol,
+ li,
+ dl,
+ dt,
+ dd,
+ // tables
+ table,
+ thead,
+ tbody,
+ caption,
+ th,
+ tr,
+ td,
+ // misc
+ html,
+ monospace,
+ };
+
+ /**
+ * Style bits (1 &lt;&lt; StyleClass.ordinal()) for each character to simplify
+ * overlapping styles implementation.
+ */
+ private int[] styleClassBits;
+ private boolean inTableHeader;
+
+ static void highlight( StyleClassedTextArea textArea, RootNode astRoot ) {
+ assert StyleClass.values().length <= 32;
+ assert Platform.isFxApplicationThread();
+
+ textArea.setStyleSpans( 0, new MarkdownSyntaxHighlighter()
+ .computeHighlighting( astRoot, textArea.getLength() ) );
+ }
+
+ private MarkdownSyntaxHighlighter() {
+ }
+
+ private StyleSpans<Collection<String>> computeHighlighting( RootNode astRoot, int textLength ) {
+ styleClassBits = new int[ textLength ];
+
+ // visit all nodes
+ astRoot.accept( this );
+
+ // build style spans
+ StyleSpansBuilder<Collection<String>> spansBuilder = new StyleSpansBuilder<>();
+ if( styleClassBits.length > 0 ) {
+ int spanStart = 0;
+ int previousBits = styleClassBits[ 0 ];
+
+ for( int i = 1; i < styleClassBits.length; i++ ) {
+ int bits = styleClassBits[ i ];
+ if( bits == previousBits ) {
+ continue;
+ }
+
+ spansBuilder.add( toStyleClasses( previousBits ), i - spanStart );
+
+ spanStart = i;
+ previousBits = bits;
+ }
+ spansBuilder.add( toStyleClasses( previousBits ), styleClassBits.length - spanStart );
+ } else {
+ spansBuilder.add( Collections.emptyList(), 0 );
+ }
+ return spansBuilder.create();
+ }
+
+ private Collection<String> toStyleClasses( int bits ) {
+ if( bits == 0 ) {
+ return Collections.emptyList();
+ }
+
+ Collection<String> styleClasses = new ArrayList<>( 1 );
+ for( StyleClass styleClass : StyleClass.values() ) {
+ if( (bits & (1 << styleClass.ordinal())) != 0 ) {
+ styleClasses.add( styleClass.name() );
+ }
+ }
+ return styleClasses;
+ }
+
+ @Override
+ public void visit( AbbreviationNode node ) {
+ // noting to do here
+ }
+
+ @Override
+ public void visit( AnchorLinkNode node ) {
+ // noting to do here
+ }
+
+ @Override
+ public void visit( AutoLinkNode node ) {
+ setStyleClass( node, StyleClass.a );
+ }
+
+ @Override
+ public void visit( BlockQuoteNode node ) {
+ setStyleClass( node, StyleClass.blockquote );
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( BulletListNode node ) {
+ setStyleClass( node, StyleClass.ul );
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( CodeNode node ) {
+ setStyleClass( node, StyleClass.code );
+ }
+
+ @Override
+ public void visit( DefinitionListNode node ) {
+ setStyleClass( node, StyleClass.dl );
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( DefinitionNode node ) {
+ setStyleClass( node, StyleClass.dd );
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( DefinitionTermNode node ) {
+ setStyleClass( node, StyleClass.dt );
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( ExpImageNode node ) {
+ setStyleClass( node, StyleClass.img );
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( ExpLinkNode node ) {
+ setStyleClass( node, StyleClass.a );
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( HeaderNode node ) {
+ StyleClass styleClass;
+ switch( node.getLevel() ) {
+ case 1:
+ styleClass = StyleClass.h1;
+ break;
+ case 2:
+ styleClass = StyleClass.h2;
+ break;
+ case 3:
+ styleClass = StyleClass.h3;
+ break;
+ case 4:
+ styleClass = StyleClass.h4;
+ break;
+ case 5:
+ styleClass = StyleClass.h5;
+ break;
+ case 6:
+ styleClass = StyleClass.h6;
+ break;
+ default:
+ return;
+ }
+ setStyleClass( node, styleClass );
+
+ // use monospace font for underlined headers
+ if( !node.getChildren().isEmpty()
+ && node.getChildren().get( 0 ).getStartIndex() == node.getStartIndex() ) {
+ setStyleClass( node, StyleClass.monospace );
+ }
+
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( HtmlBlockNode node ) {
+ setStyleClass( node, StyleClass.html );
+ }
+
+ @Override
+ public void visit( InlineHtmlNode node ) {
+ setStyleClass( node, StyleClass.html );
+ }
+
+ @Override
+ public void visit( ListItemNode node ) {
+ setStyleClass( node, StyleClass.li );
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( MailLinkNode node ) {
+ setStyleClass( node, StyleClass.a );
+ }
+
+ @Override
+ public void visit( OrderedListNode node ) {
+ setStyleClass( node, StyleClass.ol );
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( ParaNode node ) {
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( QuotedNode node ) {
+ // noting to do here
+ }
+
+ @Override
+ public void visit( ReferenceNode node ) {
+ // noting to do here
+ }
+
+ @Override
+ public void visit( RefImageNode node ) {
+ setStyleClass( node, StyleClass.img );
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( RefLinkNode node ) {
+ setStyleClass( node, StyleClass.a );
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( RootNode node ) {
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( SimpleNode node ) {
+ // noting to do here
+ }
+
+ @Override
+ public void visit( SpecialTextNode node ) {
+ // noting to do here
+ }
+
+ @Override
+ public void visit( StrikeNode node ) {
+ setStyleClass( node, StyleClass.del );
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( StrongEmphSuperNode node ) {
+ if( node.isClosed() ) {
+ setStyleClass( node, node.isStrong() ? StyleClass.strong : StyleClass.em );
+ }
+ // else sequence was not closed, treat open chars as ordinary chars
+
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( TableBodyNode node ) {
+ setStyleClass( node, StyleClass.tbody );
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( TableCaptionNode node ) {
+ setStyleClass( node, StyleClass.caption );
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( TableCellNode node ) {
+ setStyleClass( node, inTableHeader ? StyleClass.th : StyleClass.td );
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( TableColumnNode node ) {
+ // noting to do here
+ }
+
+ @Override
+ public void visit( TableHeaderNode node ) {
+ setStyleClass( node, StyleClass.thead );
+
+ inTableHeader = true;
+ visitChildren( node );
+ inTableHeader = false;
+ }
+
+ @Override
+ public void visit( TableNode node ) {
+ setStyleClass( node, StyleClass.table );
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( TableRowNode node ) {
+ setStyleClass( node, StyleClass.tr );
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( VerbatimNode node ) {
+ setStyleClass( node, StyleClass.pre );
+ }
+
+ @Override
+ public void visit( WikiLinkNode node ) {
+ setStyleClass( node, StyleClass.a );
+ }
+
+ @Override
+ public void visit( TextNode node ) {
+ // noting to do here
+ }
+
+ @Override
+ public void visit( SuperNode node ) {
+ visitChildren( node );
+ }
+
+ @Override
+ public void visit( Node node ) {
+ // ignore custom Node implementations
+ }
+
+ private void visitChildren( SuperNode node ) {
+ node.getChildren().stream().forEach( (child) -> {
+ child.accept( this );
+ } );
}