| 14 | 14 | import com.keenwrite.processors.markdown.extensions.tex.TeXExtension; |
| 15 | 15 | import com.keenwrite.processors.r.RInlineEvaluator; |
| 16 | import com.keenwrite.processors.r.RVariableProcessor; | |
| 16 | 17 | import com.vladsch.flexmark.util.misc.Extension; |
| 17 | 18 | |
| ... | ||
| 64 | 65 | |
| 65 | 66 | if( mediaType == TEXT_R_MARKDOWN ) { |
| 66 | result.add( RInlineExtension.create( context ) ); | |
| 67 | processor = IDENTITY; | |
| 68 | evaluator = new RInlineEvaluator( context ); | |
| 67 | final var rVarProcessor = new RVariableProcessor( IDENTITY, context ); | |
| 68 | final var rInlineEvaluator = new RInlineEvaluator( rVarProcessor ); | |
| 69 | result.add( RInlineExtension.create( rInlineEvaluator, context ) ); | |
| 70 | processor = rVarProcessor; | |
| 71 | evaluator = rInlineEvaluator; | |
| 69 | 72 | } |
| 70 | 73 | else { |
| ... | ||
| 77 | 80 | |
| 78 | 81 | result.add( ImageLinkExtension.create( context ) ); |
| 79 | result.add( TeXExtension.create( processor, context ) ); | |
| 82 | result.add( TeXExtension.create( evaluator, context ) ); | |
| 80 | 83 | result.add( FencedBlockExtension.create( processor, evaluator, context ) ); |
| 81 | 84 | |
| 34 | 34 | private final BaseMarkdownProcessor mMarkdownProcessor; |
| 35 | 35 | |
| 36 | private RInlineExtension( final ProcessorContext context ) { | |
| 37 | mEvaluator = new RInlineEvaluator( context ); | |
| 36 | private RInlineExtension( | |
| 37 | final RInlineEvaluator evaluator, | |
| 38 | final ProcessorContext context ) { | |
| 39 | mEvaluator = evaluator; | |
| 38 | 40 | mMarkdownProcessor = new BaseMarkdownProcessor( IDENTITY, context ); |
| 39 | 41 | } |
| 40 | 42 | |
| 41 | 43 | /** |
| 42 | 44 | * Creates an extension capable of intercepting R code blocks and preventing |
| 43 | 45 | * them from being converted into HTML {@code <code>} elements. |
| 44 | 46 | */ |
| 45 | public static RInlineExtension create( final ProcessorContext context ) { | |
| 46 | return new RInlineExtension( context ); | |
| 47 | public static RInlineExtension create( | |
| 48 | final RInlineEvaluator evaluator, | |
| 49 | final ProcessorContext context ) { | |
| 50 | return new RInlineExtension( evaluator, context ); | |
| 47 | 51 | } |
| 48 | 52 |
| 3 | 3 | |
| 4 | 4 | import com.keenwrite.ExportFormat; |
| 5 | import com.keenwrite.processors.Processor; | |
| 6 | 5 | import com.keenwrite.processors.ProcessorContext; |
| 7 | 6 | import com.keenwrite.processors.markdown.extensions.HtmlRendererAdapter; |
| 8 | 7 | import com.keenwrite.processors.markdown.extensions.tex.TexNodeRenderer.Factory; |
| 9 | 8 | import com.vladsch.flexmark.html.HtmlRenderer; |
| 10 | 9 | import com.vladsch.flexmark.parser.Parser; |
| 11 | 10 | import com.vladsch.flexmark.util.data.MutableDataHolder; |
| 12 | 11 | import org.jetbrains.annotations.NotNull; |
| 12 | ||
| 13 | import java.util.function.Function; | |
| 13 | 14 | |
| 14 | 15 | import static com.vladsch.flexmark.parser.Parser.ParserExtension; |
| ... | ||
| 29 | 30 | * Responsible for pre-parsing the input. |
| 30 | 31 | */ |
| 31 | private final Processor<String> mProcessor; | |
| 32 | private final Function<String, String> mEvaluator; | |
| 32 | 33 | |
| 33 | 34 | /** |
| 34 | 35 | * Controls how the node renderer produces TeX code within HTML output. |
| 35 | 36 | */ |
| 36 | 37 | private final ExportFormat mExportFormat; |
| 37 | 38 | |
| 38 | 39 | private TeXExtension( |
| 39 | final Processor<String> processor, final ProcessorContext context ) { | |
| 40 | mProcessor = processor; | |
| 40 | final Function<String, String> evaluator, final ProcessorContext context ) { | |
| 41 | mEvaluator = evaluator; | |
| 41 | 42 | mExportFormat = context.getExportFormat(); |
| 42 | 43 | } |
| 43 | 44 | |
| 44 | 45 | /** |
| 45 | 46 | * Creates an extension capable of handling delimited TeX code in Markdown. |
| 46 | 47 | * |
| 47 | 48 | * @return The new {@link TeXExtension}, never {@code null}. |
| 48 | 49 | */ |
| 49 | 50 | public static TeXExtension create( |
| 50 | final Processor<String> processor, final ProcessorContext context ) { | |
| 51 | return new TeXExtension( processor, context ); | |
| 51 | final Function<String, String> evaluator, final ProcessorContext context ) { | |
| 52 | return new TeXExtension( evaluator, context ); | |
| 52 | 53 | } |
| 53 | 54 | |
| ... | ||
| 62 | 63 | @NotNull final String rendererType ) { |
| 63 | 64 | if( "HTML".equalsIgnoreCase( rendererType ) ) { |
| 64 | builder.nodeRendererFactory( new Factory( mExportFormat, mProcessor ) ); | |
| 65 | builder.nodeRendererFactory( new Factory( mExportFormat, mEvaluator ) ); | |
| 65 | 66 | } |
| 66 | 67 | } |
| 4 | 4 | import com.keenwrite.ExportFormat; |
| 5 | 5 | import com.keenwrite.preview.SvgRasterizer; |
| 6 | import com.keenwrite.processors.Processor; | |
| 7 | 6 | import com.vladsch.flexmark.html.HtmlWriter; |
| 8 | 7 | import com.vladsch.flexmark.html.renderer.NodeRenderer; |
| ... | ||
| 17 | 16 | import java.util.Map; |
| 18 | 17 | import java.util.Set; |
| 18 | import java.util.function.Function; | |
| 19 | 19 | |
| 20 | 20 | import static com.keenwrite.ExportFormat.*; |
| ... | ||
| 39 | 39 | |
| 40 | 40 | public Factory( |
| 41 | final ExportFormat exportFormat, final Processor<String> processor ) { | |
| 41 | final ExportFormat exportFormat, | |
| 42 | final Function<String, String> evaluator ) { | |
| 42 | 43 | mNodeRenderer = EXPORT_RENDERERS.getOrDefault( exportFormat, RENDERER ); |
| 43 | mNodeRenderer.setProcessor( processor ); | |
| 44 | mNodeRenderer.setEvaluator( evaluator ); | |
| 44 | 45 | } |
| 45 | 46 | |
| ... | ||
| 53 | 54 | private static abstract class RendererFacade |
| 54 | 55 | implements NodeRenderer { |
| 55 | private Processor<String> mProcessor; | |
| 56 | private Function<String, String> mEvaluator; | |
| 56 | 57 | |
| 57 | 58 | @Override |
| ... | ||
| 74 | 75 | final HtmlWriter html ); |
| 75 | 76 | |
| 76 | private void setProcessor( final Processor<String> processor ) { | |
| 77 | mProcessor = processor; | |
| 77 | private void setEvaluator( final Function<String, String> evaluator ) { | |
| 78 | mEvaluator = evaluator; | |
| 78 | 79 | } |
| 79 | 80 | |
| 80 | Processor<String> getProcessor() { | |
| 81 | return mProcessor; | |
| 81 | Function<String, String> getEvaluator() { | |
| 82 | return mEvaluator; | |
| 82 | 83 | } |
| 83 | 84 | } |
| ... | ||
| 97 | 98 | final NodeRendererContext context, |
| 98 | 99 | final HtmlWriter html ) { |
| 99 | final var text = getProcessor().apply( node.getText().toString() ); | |
| 100 | final var text = getEvaluator().apply( node.getText().toString() ); | |
| 100 | 101 | final var content = |
| 101 | 102 | mIncludeDelimiter |
| ... | ||
| 118 | 119 | final var tex = node.getText().toStringOrNull(); |
| 119 | 120 | final var doc = MATH_RENDERER.render( |
| 120 | tex == null ? "" : getProcessor().apply( tex ) ); | |
| 121 | tex == null ? "" : getEvaluator().apply( tex ) ); | |
| 121 | 122 | final var svg = SvgRasterizer.toSvg( doc.getDocumentElement() ); |
| 122 | 123 | html.raw( svg ); |
| ... | ||
| 132 | 133 | final HtmlWriter html ) { |
| 133 | 134 | html.raw( TOKEN_OPEN ); |
| 134 | html.raw( getProcessor().apply( node.getText().toString() ) ); | |
| 135 | html.raw( getEvaluator().apply( node.getText().toString() ) ); | |
| 135 | 136 | html.raw( TOKEN_CLOSE ); |
| 136 | 137 | } |
| 3 | 3 | |
| 4 | 4 | import com.keenwrite.processors.Processor; |
| 5 | import com.keenwrite.processors.ProcessorContext; | |
| 6 | 5 | |
| 7 | 6 | import java.util.function.Function; |
| 8 | 7 | import java.util.function.Predicate; |
| 9 | 8 | |
| 10 | 9 | import static com.keenwrite.constants.Constants.STATUS_PARSE_ERROR; |
| 11 | 10 | import static com.keenwrite.events.StatusEvent.clue; |
| 12 | import static com.keenwrite.processors.IdentityProcessor.IDENTITY; | |
| 13 | 11 | |
| 14 | 12 | /** |
| ... | ||
| 27 | 25 | * Constructs an evaluator capable of executing R statements. |
| 28 | 26 | */ |
| 29 | public RInlineEvaluator( final ProcessorContext context ) { | |
| 30 | mProcessor = new RVariableProcessor( IDENTITY, context ); | |
| 27 | public RInlineEvaluator( final RVariableProcessor processor ) { | |
| 28 | mProcessor = processor; | |
| 31 | 29 | } |
| 32 | 30 | |
| 2 | 2 | package com.keenwrite.sigils; |
| 3 | 3 | |
| 4 | import com.keenwrite.collections.BoundedCache; | |
| 5 | ||
| 4 | 6 | import java.util.function.UnaryOperator; |
| 5 | 7 | |
| ... | ||
| 12 | 14 | private static final char KEY_SEPARATOR_R = '$'; |
| 13 | 15 | |
| 16 | /** Minor optimization to avoid recreating an object. */ | |
| 14 | 17 | private final StringBuilder mVarName = new StringBuilder( 128 ); |
| 18 | ||
| 19 | /** Optimization to avoid re-converting variable names into R format. */ | |
| 20 | private final BoundedCache<String, String> mVariables = new BoundedCache<>( | |
| 21 | 2048 | |
| 22 | ); | |
| 15 | 23 | |
| 16 | 24 | /** |
| ... | ||
| 39 | 47 | assert !key.isBlank(); |
| 40 | 48 | |
| 41 | mVarName.setLength( 0 ); | |
| 49 | return mVariables.computeIfAbsent( key, this::convert ); | |
| 50 | } | |
| 42 | 51 | |
| 43 | //final var rVarName = new StringBuilder( key.length() + 3 ); | |
| 52 | private String convert( final String key ) { | |
| 53 | mVarName.setLength( 0 ); | |
| 44 | 54 | mVarName.append( "v" ); |
| 45 | 55 | mVarName.append( KEY_SEPARATOR_R ); |