| Author | DaveJarvis <email> |
|---|---|
| Date | 2023-11-20 17:31:23 GMT-0800 |
| Commit | 420e1e207fbb29c3271809692392b29f2160604f |
| Parent | 2fd2028 |
| Delta | 179 lines added, 89 lines removed, 90-line increase |
| $method = isset( $_SERVER[ 'REQUEST_METHOD' ] ) | ||
| - ? $_SERVER[ 'REQUEST_METHOD ' ] | ||
| + ? $_SERVER[ 'REQUEST_METHOD' ] | ||
| : 'GET'; | ||
| -/* Copyright 2020-2021 White Magic Software, Ltd. -- All rights reserved. */ | ||
| -package com.keenwrite.processors.markdown.extensions.tex; | ||
| - | ||
| -import com.vladsch.flexmark.parser.InlineParser; | ||
| -import com.vladsch.flexmark.parser.core.delimiter.Delimiter; | ||
| -import com.vladsch.flexmark.parser.delimiter.DelimiterProcessor; | ||
| -import com.vladsch.flexmark.parser.delimiter.DelimiterRun; | ||
| -import com.vladsch.flexmark.util.ast.Node; | ||
| - | ||
| -public class TeXInlineDelimiterProcessor implements DelimiterProcessor { | ||
| - | ||
| - @Override | ||
| - public void process( final Delimiter opener, | ||
| - final Delimiter closer, | ||
| - final int delimitersUsed ) { | ||
| - final var node = new TexNode( opener, closer ); | ||
| - opener.moveNodesBetweenDelimitersTo( node, closer ); | ||
| - } | ||
| - | ||
| - @Override | ||
| - public char getOpeningCharacter() { | ||
| - return '$'; | ||
| - } | ||
| - | ||
| - @Override | ||
| - public char getClosingCharacter() { | ||
| - return '$'; | ||
| - } | ||
| - | ||
| - @Override | ||
| - public int getMinLength() { | ||
| - return 1; | ||
| - } | ||
| - | ||
| - /** | ||
| - * Allow for $ or $$. | ||
| - * | ||
| - * @param opener One or more opening delimiter characters. | ||
| - * @param closer One or more closing delimiter characters. | ||
| - * @return The number of delimiters to use to determine whether a valid | ||
| - * opening delimiter expression is found. | ||
| - */ | ||
| - @Override | ||
| - public int getDelimiterUse( | ||
| - final DelimiterRun opener, final DelimiterRun closer ) { | ||
| - return 1; | ||
| - } | ||
| - | ||
| - @Override | ||
| - public boolean canBeOpener( final String before, | ||
| - final String after, | ||
| - final boolean leftFlanking, | ||
| - final boolean rightFlanking, | ||
| - final boolean beforeIsPunctuation, | ||
| - final boolean afterIsPunctuation, | ||
| - final boolean beforeIsWhitespace, | ||
| - final boolean afterIsWhiteSpace ) { | ||
| - return leftFlanking; | ||
| - } | ||
| - | ||
| - @Override | ||
| - public boolean canBeCloser( final String before, | ||
| - final String after, | ||
| - final boolean leftFlanking, | ||
| - final boolean rightFlanking, | ||
| - final boolean beforeIsPunctuation, | ||
| - final boolean afterIsPunctuation, | ||
| - final boolean beforeIsWhitespace, | ||
| - final boolean afterIsWhiteSpace ) { | ||
| - return rightFlanking; | ||
| - } | ||
| - | ||
| - @Override | ||
| - public Node unmatchedDelimiterNode( | ||
| - final InlineParser inlineParser, final DelimiterRun delimiter ) { | ||
| - return null; | ||
| - } | ||
| - | ||
| - @Override | ||
| - public boolean skipNonOpenerCloser() { | ||
| - return false; | ||
| - } | ||
| -} | ||
| public class TexNode extends DelimitedNodeImpl { | ||
| /** | ||
| - * TeX expression wrapped in a {@code <tex>} element. | ||
| + * A TeX expression wrapped in a {@code <tex>} element. | ||
| */ | ||
| public static final String HTML_TEX = "tex"; | ||
| - public static final String TOKEN_OPEN = "$"; | ||
| - public static final String TOKEN_CLOSE = "$"; | ||
| + static final String TOKEN_OPEN = "$"; | ||
| + static final String TOKEN_CLOSE = "$"; | ||
| private final String mOpener; | ||
| * @return Either '$' or '$$'. | ||
| */ | ||
| - public String getOpeningDelimiter() { return mOpener; } | ||
| + public String getOpeningDelimiter() { | ||
| + return mOpener; | ||
| + } | ||
| /** | ||
| * @return Either '$' or '$$'. | ||
| */ | ||
| - public String getClosingDelimiter() { return mCloser; } | ||
| + public String getClosingDelimiter() { | ||
| + return mCloser; | ||
| + } | ||
| private String getDelimiter( final Delimiter delimiter ) { | ||
| +/* Copyright 2023 White Magic Software, Ltd. -- All rights reserved. | ||
| + * | ||
| + * SPDX-License-Identifier: MIT | ||
| + */ | ||
| +package com.keenwrite.processors.markdown.extensions.references; | ||
| + | ||
| +import com.vladsch.flexmark.util.ast.Node; | ||
| +import com.vladsch.flexmark.util.sequence.BasedSequence; | ||
| +import org.jetbrains.annotations.NotNull; | ||
| + | ||
| +/** | ||
| + * Responsible for writing HTML anchor cross-references in the form | ||
| + * {@code <a data-type="..." href="#name" />} where {@code name} refers | ||
| + * to an anchor name. | ||
| + * | ||
| + * @see AnchorNameNode | ||
| + */ | ||
| +class AnchorXrefNode extends Node implements CrossReferenceNode { | ||
| + private final String mTypeName; | ||
| + private final String mIdName; | ||
| + | ||
| + AnchorXrefNode( final String type, final String id ) { | ||
| + mTypeName = type; | ||
| + mIdName = STR. "#\{ id }" ; | ||
| + } | ||
| + | ||
| + @Override | ||
| + public String getTypeName() { | ||
| + return mTypeName; | ||
| + } | ||
| + | ||
| + @Override | ||
| + public String getIdName() { | ||
| + return mIdName; | ||
| + } | ||
| + | ||
| + @Override | ||
| + public String getRefAttrName() { | ||
| + return "href"; | ||
| + } | ||
| + | ||
| + @NotNull | ||
| + @Override | ||
| + public BasedSequence[] getSegments() { | ||
| + return BasedSequence.EMPTY_SEGMENTS; | ||
| + } | ||
| +} | ||
| +/* Copyright 2023 White Magic Software, Ltd. -- All rights reserved. | ||
| + * | ||
| + * SPDX-License-Identifier: MIT | ||
| + */ | ||
| +package com.keenwrite.processors.markdown.extensions.references; | ||
| + | ||
| +import com.keenwrite.processors.markdown.extensions.HtmlRendererAdapter; | ||
| +import com.vladsch.flexmark.html.HtmlRenderer; | ||
| +import com.vladsch.flexmark.parser.Parser; | ||
| +import com.vladsch.flexmark.parser.Parser.ParserExtension; | ||
| +import com.vladsch.flexmark.util.data.MutableDataHolder; | ||
| +import org.jetbrains.annotations.NotNull; | ||
| + | ||
| +/** | ||
| + * Responsible for processing {@code {@type:id}} anchors and their corresponding | ||
| + * {@code [@type:id]} cross-references. | ||
| + */ | ||
| +public class CrossReferenceExtension extends HtmlRendererAdapter | ||
| + implements ParserExtension { | ||
| + | ||
| + @Override | ||
| + public void extend( final Parser.Builder builder ) { | ||
| + builder.customInlineParserFactory( AnchorXrefInlineParser::new ); | ||
| + builder.customDelimiterProcessor( new AnchorNameDelimiterProcessor() ); | ||
| + } | ||
| + | ||
| + @Override | ||
| + public void extend( @NotNull final HtmlRenderer.Builder builder, | ||
| + @NotNull final String rendererType ) { | ||
| + if( "HTML".equalsIgnoreCase( rendererType ) ) { | ||
| + builder.nodeRendererFactory( new CrossReferencesNodeRenderer.Factory() ); | ||
| + } | ||
| + } | ||
| + | ||
| + @Override | ||
| + public void parserOptions( final MutableDataHolder options ) {} | ||
| +} | ||
| +/* Copyright 2023 White Magic Software, Ltd. -- All rights reserved. | ||
| + * | ||
| + * SPDX-License-Identifier: MIT | ||
| + */ | ||
| +package com.keenwrite.processors.markdown.extensions.references; | ||
| + | ||
| +import com.vladsch.flexmark.html.HtmlWriter; | ||
| + | ||
| +/** | ||
| + * Responsible for generating anchor links, either named or cross-referenced. | ||
| + */ | ||
| +interface CrossReferenceNode { | ||
| + String getTypeName(); | ||
| + | ||
| + String getIdName(); | ||
| + | ||
| + String getRefAttrName(); | ||
| + | ||
| + default String toHtml() { | ||
| + final String typeName = getTypeName(); | ||
| + final String idName = getIdName(); | ||
| + | ||
| + return toHtml( typeName, idName ); | ||
| + } | ||
| + | ||
| + default String toHtml( final String type, final String id ) { | ||
| + return STR. | ||
| + "<a data-type=\"\{ type }\" \{ getRefAttrName() }=\"\{ id }\" />" ; | ||
| + } | ||
| + | ||
| + default void write( final HtmlWriter html ) { | ||
| + html.raw( toHtml() ); | ||
| + } | ||
| +} | ||
| +/* Copyright 2023 White Magic Software, Ltd. -- All rights reserved. | ||
| + * | ||
| + * SPDX-License-Identifier: MIT | ||
| + */ | ||
| +package com.keenwrite.processors.markdown.extensions.references; | ||
| + | ||
| +import com.vladsch.flexmark.html.HtmlWriter; | ||
| +import com.vladsch.flexmark.html.renderer.NodeRenderer; | ||
| +import com.vladsch.flexmark.html.renderer.NodeRendererContext; | ||
| +import com.vladsch.flexmark.html.renderer.NodeRendererFactory; | ||
| +import com.vladsch.flexmark.html.renderer.NodeRenderingHandler; | ||
| +import com.vladsch.flexmark.util.data.DataHolder; | ||
| +import org.jetbrains.annotations.NotNull; | ||
| + | ||
| +import java.util.Arrays; | ||
| +import java.util.HashSet; | ||
| +import java.util.Set; | ||
| + | ||
| +/** | ||
| + * Responsible for rendering HTML elements that correspond to cross-references. | ||
| + */ | ||
| +class CrossReferencesNodeRenderer implements NodeRenderer { | ||
| + | ||
| + @Override | ||
| + public Set<NodeRenderingHandler<?>> getNodeRenderingHandlers() { | ||
| + return new HashSet<>( Arrays.asList( | ||
| + new NodeRenderingHandler<>( AnchorNameNode.class, this::render ), | ||
| + new NodeRenderingHandler<>( AnchorXrefNode.class, this::render ) | ||
| + ) ); | ||
| + } | ||
| + | ||
| + private void render( final CrossReferenceNode node, | ||
| + final NodeRendererContext context, | ||
| + final HtmlWriter html ) { | ||
| + node.write( html ); | ||
| + } | ||
| + | ||
| + public static class Factory implements NodeRendererFactory { | ||
| + private final NodeRenderer mNodeRenderer; | ||
| + | ||
| + public Factory() { | ||
| + mNodeRenderer = new CrossReferencesNodeRenderer(); | ||
| + } | ||
| + | ||
| + @NotNull | ||
| + @Override | ||
| + public NodeRenderer apply( @NotNull final DataHolder options ) { | ||
| + return mNodeRenderer; | ||
| + } | ||
| + } | ||
| +} | ||