Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/keenwrite.git
build.gradle
implementation "io.sf.carte:echosvg-gvt:${v_echosvg}"
implementation "io.sf.carte:echosvg-parser:${v_echosvg}"
- implementation "io.sf.carte:echosvg-script:${v_echosvg}"
implementation "io.sf.carte:echosvg-svg-dom:${v_echosvg}"
implementation "io.sf.carte:echosvg-svggen:${v_echosvg}"
implementation "io.sf.carte:echosvg-transcoder:${v_echosvg}"
implementation "io.sf.carte:echosvg-util:${v_echosvg}"
implementation "io.sf.carte:echosvg-xml:${v_echosvg}"
// Misc.
implementation 'org.ahocorasick:ahocorasick:0.6.3'
- implementation 'org.apache.commons:commons-configuration2:2.9.0'
+ implementation 'org.apache.commons:commons-lang3:3.14.0'
implementation 'com.github.albfernandez:juniversalchardet:2.4.0'
implementation 'jakarta.validation:jakarta.validation-api:3.0.2'
testImplementation 'org.assertj:assertj-core:3.24.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
+}
+
+configurations {
+ all*.exclude group: 'org.mozilla', module: 'rhino'
}
useJUnitPlatform()
- doFirst { jvmArgs = moduleSecurity }
+ doFirst { jvmArgs += moduleSecurity }
testLogging { exceptionFormat = 'full' }
}
installer.sh
readonly OPT_JAVA=$(cat << END_OF_ARGS
+-Dprism.order=sw \
+--enable-preview \
--add-opens=javafx.controls/javafx.scene.control=ALL-UNNAMED \
--add-opens=javafx.controls/javafx.scene.control.skin=ALL-UNNAMED \
keenwrite.sh
java \
+ -Dprism.order=sw \
+ --enable-preview \
--add-opens=javafx.controls/javafx.scene.control=ALL-UNNAMED \
--add-opens=javafx.controls/javafx.scene.control.skin=ALL-UNNAMED \
src/main/java/com/keenwrite/collections/InterpolatingMap.java
package com.keenwrite.collections;
+import com.keenwrite.sigils.PropertyKeyOperator;
import com.keenwrite.sigils.SigilKeyOperator;
private transient final SigilKeyOperator mOperator;
+
+ /**
+ * Creates a new interpolating map using the {@link PropertyKeyOperator}.
+ */
+ public InterpolatingMap() {
+ this( new PropertyKeyOperator() );
+ }
/**
@Override
public boolean equals( final Object o ) {
- if( this == o ) { return true; }
- if( o == null || getClass() != o.getClass() ) { return false; }
- if( !super.equals( o ) ) { return false; }
+ if( this == o ) {
+ return true;
+ }
+
+ if( o == null || getClass() != o.getClass() ) {
+ return false;
+ }
+
+ if( !super.equals( o ) ) {
+ return false;
+ }
+
final InterpolatingMap that = (InterpolatingMap) o;
return Objects.equals( mOperator, that.mOperator );
src/main/java/com/keenwrite/config/PropertiesConfiguration.java
+/* Copyright 2023 White Magic Software, Ltd. -- All rights reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ */
+package com.keenwrite.config;
+
+import com.keenwrite.collections.InterpolatingMap;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.*;
+
+import static java.util.Arrays.*;
+
+/**
+ * Responsible for reading and interpolating properties files.
+ */
+public class PropertiesConfiguration {
+ private static final String VALUE_SEPARATOR = ",";
+
+ private final InterpolatingMap mMap = new InterpolatingMap();
+
+ public PropertiesConfiguration() {}
+
+ public void read( final Reader reader ) throws IOException {
+ final var properties = new Properties();
+ properties.load( reader );
+
+ for( final var name : properties.stringPropertyNames() ) {
+ mMap.put( name, properties.getProperty( name ) );
+ }
+
+ mMap.interpolate();
+ }
+
+ /**
+ * Returns the value of a string property.
+ *
+ * @param property The property key.
+ * @param defaultValue The value to return if no property key has been set.
+ * @return The property key value, or defaultValue when no key found.
+ */
+ public String getString( final String property, final String defaultValue ) {
+ assert property != null;
+
+ return mMap.getOrDefault( property, defaultValue );
+ }
+
+ /**
+ * Returns the value of a string property.
+ *
+ * @param property The property key.
+ * @param defaultValue The value to return if no property key has been set.
+ * @return The property key value, or defaultValue when no key found.
+ */
+ public int getInt( final String property, final int defaultValue ) {
+ assert property != null;
+
+ return parse( mMap.get( property ), defaultValue );
+ }
+
+ /**
+ * Convert the generic list of property objects into strings.
+ *
+ * @param property The property value to coerce.
+ * @param defaults The values to use should the property be unset.
+ * @return The list of properties coerced from objects to strings.
+ */
+ public List<String> getList(
+ final String property, final List<String> defaults ) {
+ assert property != null;
+
+ final var value = mMap.get( property );
+
+ return value == null
+ ? defaults
+ : asList( value.split( VALUE_SEPARATOR ) );
+ }
+
+ /**
+ * Returns a list of property names that begin with the given prefix.
+ * Note that the prefix must be separated from other values with a
+ * period.
+ *
+ * @param prefix The prefix to compare against each property name. When
+ * comparing, the prefix value will have a period appended.
+ * @return The list of property names that have the given prefix.
+ */
+ public Iterator<String> getKeys( final String prefix ) {
+ assert prefix != null;
+
+ final var result = new HashMap<String, String>();
+ final var prefixDotted = prefix + '.';
+
+ for( final var entry : mMap.entrySet() ) {
+ final var key = entry.getKey();
+
+ if( key.startsWith( prefixDotted ) ) {
+ final var value = entry.getValue();
+ result.put( key, value );
+ }
+ }
+
+ return result.keySet().iterator();
+ }
+
+ private static int parse( final String s, final int defaultValue ) {
+ try {
+ return s == null || s.isBlank() ? defaultValue : Integer.parseInt( s );
+ } catch( final NumberFormatException e ) {
+ return defaultValue;
+ }
+ }
+}
src/main/java/com/keenwrite/preview/FlyingSaucerPanel.java
if( !box.getStyle().isInline() ) {
final var margin = box.getMargin( getLayoutContext() );
- y += margin.top();
- x += margin.left();
+ y += (int) margin.top();
+ x += (int) margin.left();
}
src/main/java/com/keenwrite/preview/HtmlPreview.java
mScrollLockButton.setMargin( new Insets( 1, 0, 0, 0 ) );
mScrollLockButton.addActionListener(
- e -> fireScrollLockEvent( !mScrollLocked )
+ _ -> fireScrollLockEvent( !mScrollLocked )
);
src/main/java/com/keenwrite/processors/markdown/extensions/captions/CaptionBlock.java
*/
void opening( final HtmlWriter writer ) {
- writer.raw( "<p><span class=\"caption\">" );
+ writer.raw( "<span class=\"caption\">" );
}
/**
* Closes the caption.
*
* @param writer Where to write the closing tags.
*/
void closing( final HtmlWriter writer ) {
- writer.raw( "</span></p>" );
+ writer.raw( "</span>" );
}
src/main/java/com/keenwrite/processors/markdown/extensions/captions/CaptionNodeRenderer.java
package com.keenwrite.processors.markdown.extensions.captions;
+import com.keenwrite.processors.markdown.extensions.references.CrossReferenceNode;
import com.vladsch.flexmark.html.HtmlWriter;
import com.vladsch.flexmark.html.renderer.CoreNodeRenderer;
import com.vladsch.flexmark.html.renderer.NodeRendererContext;
import com.vladsch.flexmark.html.renderer.NodeRenderingHandler;
+import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.data.DataHolder;
import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Set;
final NodeRendererContext context,
final HtmlWriter html ) {
+ final var anchors = new LinkedList<Node>();
+
+ html.raw( "<p>" );
node.opening( html );
if( node.hasChildren() ) {
- context.renderChildren( node );
+ for( final var child : node.getChildren() ) {
+ if( !child.isOrDescendantOfType( CrossReferenceNode.class ) ) {
+ context.render( child );
+ }
+ else {
+ anchors.add( child );
+ }
+ }
}
node.closing( html );
+
+ for( final var anchor : anchors ) {
+ context.render( anchor );
+ }
+
+ html.raw( "</p>" );
}
}
src/main/java/com/keenwrite/processors/markdown/extensions/references/CrossReferenceNode.java
* Responsible for generating anchor links, either named or cross-referenced.
*/
-interface CrossReferenceNode {
+public interface CrossReferenceNode {
String getTypeName();
final var attr = getRefAttrName();
- html.raw( STR. "<a data-type=\"\{ type }\" \{ attr }=\"\{ id }\" />" );
+ final var clazz = STR. "class=\"\{ attr }\"" ;
+ final var dataType = STR. "data-type=\"\{ type }\"" ;
+ final var refId = STR. "\{ attr }=\"\{ id }\"" ;
+
+ html.raw( STR. "<a \{ clazz } \{ dataType } \{ refId } />" );
}
}
src/main/java/com/keenwrite/service/impl/DefaultSettings.java
package com.keenwrite.service.impl;
+import com.keenwrite.config.PropertiesConfiguration;
import com.keenwrite.service.Settings;
-import org.apache.commons.configuration2.PropertiesConfiguration;
-import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
-import org.apache.commons.configuration2.convert.ListDelimiterHandler;
import java.io.InputStreamReader;
*/
public final class DefaultSettings implements Settings {
-
- private static final char VALUE_SEPARATOR = ',';
private final PropertiesConfiguration mProperties = loadProperties();
- public DefaultSettings() {
- }
+ public DefaultSettings() {}
/**
*
* @param property The property value to coerce.
- * @param defaults The defaults values to use should the property be unset.
+ * @param defaults The values to use should the property be unset.
* @return The list of properties coerced from objects to strings.
*/
@Override
public List<String> getStringSettingList(
- final String property, final List<String> defaults ) {
- return getSettings().getList( String.class, property, defaults );
+ final String property, final List<String> defaults ) {
+ return getSettings().getList( property, defaults );
}
final var url = getPropertySource();
final var configuration = new PropertiesConfiguration();
+ final var encoding = getDefaultEncoding();
if( url != null ) {
try( final var reader = new InputStreamReader(
- url.openStream(), getDefaultEncoding() ) ) {
- configuration.setListDelimiterHandler( createListDelimiterHandler() );
+ url.openStream(), encoding ) ) {
configuration.read( reader );
} catch( final Exception ex ) {
private Charset getDefaultEncoding() {
return Charset.defaultCharset();
- }
-
- private ListDelimiterHandler createListDelimiterHandler() {
- return new DefaultListDelimiterHandler( VALUE_SEPARATOR );
}
src/main/resources/com/keenwrite/preview/webview.css
}
-div.bubblerx:after, div.bubbletx:after {
+div.bubblerx::after, div.bubbletx::after {
content: "";
position: absolute;
}
-div.bubbletx:after {
+div.bubbletx::after {
right: -1em;
border-left: 1em solid #ccc;
}
+/* TYPEWRITER ***/
div.typewritten {
font-family: monospace;
font-size: 16px;
font-weight: bold;
}
-
src/test/java/com/keenwrite/processors/markdown/extensions/references/CaptionsAndCrossReferencesExtensionTest.java
""",
"""
- <p><a data-type="fig" name="cats" /> <a data-type="fig" href="#cats" />
- <a data-type="table" name="dogs" /> <a data-type="table" href="#dogs" />
- <a data-type="ocean" name="whale-01" /> <a data-type="ocean" href="#whale-02" /></p>
+ <p><a class="name" data-type="fig" name="cats" /> <a class="href" data-type="fig" href="#cats" />
+ <a class="name" data-type="table" name="dogs" /> <a class="href" data-type="table" href="#dogs" />
+ <a class="name" data-type="ocean" name="whale-01" /> <a class="href" data-type="ocean" href="#whale-02" /></p>
"""
),
args(
"""
{#日本:w0mbatß}
[@日本:w0mbatß]
""",
"""
- <p><a data-type="日本" name="w0mbatß" />
- <a data-type="日本" href="#w0mbatß" /></p>
+ <p><a class="name" data-type="日本" name="w0mbatß" />
+ <a class="href" data-type="日本" href="#w0mbatß" /></p>
"""
),
"""
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.
- <a data-type="fig" name="cats" /> Sed do eiusmod tempor incididunt ut
+ <a class="name" data-type="fig" name="cats" /> Sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip
- ex ea commodo consequat. <a data-type="fig" href="#cats" /></p>
+ ex ea commodo consequat. <a class="href" data-type="fig" href="#cats" /></p>
"""
),
""",
"""
- <p><a data-type="note" name="advancement" /> Advancement isn't
+ <p><a class="name" data-type="note" name="advancement" /> Advancement isn't
measured by the ingenuity of inventions, but by humanity's ability
to anticipate and forfend dire aftermaths <em>before</em> using them.</p>
- <p><a data-type="note" href="#advancement" /></p>
+ <p><a class="href" data-type="note" href="#advancement" /></p>
<p>To what end?</p>
"""
),
args(
"""
$E=mc^2$ {#eq:label}
""",
"""
- <p><tex>$E=mc^2$</tex> <a data-type="eq" name="label" /></p>
+ <p><tex>$E=mc^2$</tex> <a class="name" data-type="eq" name="label" /></p>
"""
),
args(
"""
$$E=mc^2$$ {#eq:label}
""",
"""
- <p><tex>$$E=mc^2$$</tex> <a data-type="eq" name="label" /></p>
+ <p><tex>$$E=mc^2$$</tex> <a class="name" data-type="eq" name="label" /></p>
"""
),
args(
"""
$$E=mc^2$$
:: Caption {#eqn:energy}
""",
"""
- <p><span class="caption">Caption <a data-type="eqn" name="energy" /></span></p>
+ <p><span class="caption">Caption </span><a class="name" data-type="eqn" name="energy" /></p>
<p><tex>$$E=mc^2$$</tex></p>
"""
""",
"""
- <p><span class="caption">Source code caption <a data-type="listing" name="haskell1" /></span></p>
+ <p><span class="caption">Source code caption </span><a class="name" data-type="listing" name="haskell1" /></p>
<pre><code class="language-haskell">main :: IO ()
</code></pre>
""",
"""
- <p><span class="caption">Caption <a data-type="warning" name="sugar" /></span></p><div class="warning">
+ <p><span class="caption">Caption </span><a class="name" data-type="warning" name="sugar" /></p><div class="warning">
<p>Do not eat processed <strong>sugar</strong>.</p>
<p>Seriously.</p>
""",
"""
- <p><span class="caption">Caption <a data-type="fig" name="label" /></span></p>
+ <p><span class="caption">Caption </span><a class="name" data-type="fig" name="label" /></p>
<p><img src="tunnel" alt="alt text" /></p>
"""
""",
"""
- <p><span class="caption">Caption <strong>bold</strong> <a data-type="fig" name="label" /> <em>italics</em></span></p>
+ <p><span class="caption">Caption <strong>bold</strong> <em>italics</em></span><a class="name" data-type="fig" name="label" /></p>
<p><img src="placekitten" alt="kitteh" /></p>
"""
""",
"""
- <p><span class="caption">Meschiya Lake - Lucky Devil <a data-type="lyrics" name="blues" /></span></p>
+ <p><span class="caption">Meschiya Lake - Lucky Devil </span><a class="name" data-type="lyrics" name="blues" /></p>
<blockquote>
<p>I'd like to be the lucky devil who gets to burn with you.</p>
""",
"""
- <p><span class="caption">Caption <a data-type="tbl" name="label" /></span></p>
+ <p><span class="caption">Caption </span><a class="name" data-type="tbl" name="label" /></p>
<table>
<thead>
""",
"""
- <p><span class="caption">Diagram <a data-type="dia" name="seq1" /></span></p>
+ <p><span class="caption">Diagram </span><a class="name" data-type="dia" name="seq1" /></p>
<pre><code class="language-diagram-plantuml">@startuml
Alice -&gt; Bob: Request

Removes Rhino, adds lang3, adds preview, fixes captions

Author DaveJarvis <email>
Date 2023-12-02 14:54:40 GMT-0800
Commit 6b91f5b6faca0790ff67a72863afe24fb315cd1e
Parent 5f4cd50
Delta 204 lines added, 52 lines removed, 152-line increase