Dave Jarvis' Repositories

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

Separate variable injection from key operations

AuthorDaveJarvis <email>
Date2021-12-31 20:48:36 GMT-0800
Commitabc7d8fba2067fe9bb1ab175bf44bc6282e1c4ee
Parent4c15564
Delta65 lines added, 289 lines removed, 224-line decrease
src/test/java/com/keenwrite/sigils/RKeyOperatorTest.java
+/* Copyright 2020-2021 White Magic Software, Ltd. -- All rights reserved. */
+package com.keenwrite.sigils;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Responsible for simulating R variable injection.
+ */
+class RKeyOperatorTest {
+
+ /**
+ * Test that a key name becomes an R variable.
+ */
+ @Test
+ void test_Process_KeyName_Processed() {
+ final var mOperator = new RKeyOperator();
+ final var expected = "v$a$b$c$d";
+ final var actual = mOperator.apply( "a.b.c.d" );
+
+ assertEquals( expected, actual );
+ }
+}
src/test/java/com/keenwrite/sigils/RSigilOperatorTest.java
-/* Copyright 2020-2021 White Magic Software, Ltd. -- All rights reserved. */
-package com.keenwrite.sigils;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-/**
- * Responsible for simulating R variable injection.
- */
-class RSigilOperatorTest {
-
- private final SigilOperator mOperator = createRSigilOperator();
-
- /**
- * Test that a key name becomes an R variable.
- */
- @Test
- void test_Entoken_KeyName_Tokenized() {
- final var expected = "v$a$b$c$d";
- final var actual = mOperator.entoken( "{{a.b.c.d}}" );
- assertEquals( expected, actual );
- }
-
- /**
- * Test that a key name becomes a viable R expression.
- */
- @Test
- void test_Apply_KeyName_Expression() {
- final var expected = "`r#x(v$a$b$c$d)`";
- final var actual = mOperator.apply( "v$a$b$c$d" );
- assertEquals( expected, actual );
- }
-
- private Sigils createRSigils() {
- return createSigils( "x(", ")" );
- }
-
- private Sigils createYamlSigils() {
- return createSigils( "{{", "}}" );
- }
-
- private Sigils createSigils( final String began, final String ended ) {
- return new Sigils( began, ended );
- }
-
- private YamlSigilOperator createYamlSigilOperator() {
- return new YamlSigilOperator( createYamlSigils() );
- }
-
- private RSigilOperator createRSigilOperator() {
- return new RSigilOperator( createRSigils(), createYamlSigilOperator() );
- }
-}
src/main/java/com/keenwrite/sigils/RSigilOperator.java
-/* Copyright 2020-2021 White Magic Software, Ltd. -- All rights reserved. */
-package com.keenwrite.sigils;
-
-/**
- * Brackets variable names between {@link #PREFIX} and {@link #SUFFIX} sigils.
- */
-public final class RSigilOperator extends SigilOperator {
- public static final String PREFIX = "`r#";
- public static final char SUFFIX = '`';
-
- private static final char KEY_SEPARATOR_DEF = '.';
- private static final char KEY_SEPARATOR_R = '$';
-
- /**
- * Definition variables are inserted into the document before R variables,
- * so this is required to reformat the definition variable suitable for R.
- */
- private final SigilOperator mAntecedent;
-
- /**
- * Constructs a new {@link RSigilOperator} capable of wrapping tokens around
- * variable names (keys).
- *
- * @param sigils The starting and ending tokens.
- * @param antecedent The operator to use to undo any previous entokenizing.
- */
- public RSigilOperator( final Sigils sigils, final SigilOperator antecedent ) {
- super( sigils );
-
- mAntecedent = antecedent;
- }
-
- /**
- * Returns the given string with backticks prepended and appended. The
- *
- * @param key The string to adorn with R token delimiters.
- * @return PREFIX + delimiterBegan + variableName + delimiterEnded + SUFFIX.
- */
- @Override
- public String apply( final String key ) {
- assert key != null;
-
- return PREFIX + getBegan() + key + getEnded() + SUFFIX;
- }
-
- /**
- * Transforms a definition key (bracketed by token delimiters) into the
- * expected format for an R variable key name.
- * <p>
- * The algorithm to entoken a definition name is faster than
- * {@link String#replace(char, char)}. Faster still would be to cache the
- * values, but that would mean managing the cache when the user changes
- * the beginning and ending of the R delimiters. This code gives about a
- * 2% performance boost when scrolling using cursor keys. After the JIT
- * warms up, this super-minor bottleneck vanishes.
- * </p>
- *
- * @param key The variable name to transform, can be empty but not null.
- * @return The transformed variable name.
- */
- public String entoken( final String key ) {
- final var detokened = new StringBuilder( key.length() );
-
- detokened.append( "v$" );
- detokened.append( mAntecedent.detoken( key ) );
-
- // The 3 is for "v$X" where X cannot be a period.
- for( int i = detokened.length() - 1; i >= 3; i-- ) {
- if( detokened.charAt( i ) == KEY_SEPARATOR_DEF ) {
- detokened.setCharAt( i, KEY_SEPARATOR_R );
- }
- }
-
- return detokened.toString();
- }
-}
src/main/java/com/keenwrite/sigils/SigilKeyOperator.java
+/* Copyright 2021 White Magic Software, Ltd. -- All rights reserved. */
+package com.keenwrite.sigils;
+
+import java.util.function.UnaryOperator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static java.lang.String.format;
+import static java.util.regex.Pattern.compile;
+import static java.util.regex.Pattern.quote;
+
+/**
+ * Responsible for bracketing definition keys with token delimiters.
+ */
+public class SigilKeyOperator implements UnaryOperator<String> {
+ private final String mBegan;
+ private final String mEnded;
+ private final Pattern mPattern;
+
+ public SigilKeyOperator( final String began, final String ended ) {
+ assert began != null;
+ assert ended != null;
+
+ mBegan = began;
+ mEnded = ended;
+ mPattern = compile( format( "%s(.*?)%s", quote( began ), quote( ended ) ) );
+ }
+
+ @Override
+ public String apply( final String key ) {
+ assert key != null;
+ assert !key.startsWith( mBegan );
+ assert !key.endsWith( mEnded );
+
+ return mBegan + key + mEnded;
+ }
+
+ public Matcher match( final String text ) {
+ return mPattern.matcher( text );
+ }
+}
src/main/java/com/keenwrite/sigils/SigilOperator.java
-/* Copyright 2020-2021 White Magic Software, Ltd. -- All rights reserved. */
-package com.keenwrite.sigils;
-
-import java.util.function.UnaryOperator;
-
-/**
- * Responsible for updating definition keys to use a machine-readable format
- * corresponding to the type of file being edited. This changes a definition
- * key name based on some criteria determined by the factory that creates
- * implementations of this interface.
- */
-public class SigilOperator implements UnaryOperator<String> {
- private final Sigils mSigils;
-
- /**
- * Defines a new {@link SigilOperator} with the given sigils.
- *
- * @param began The sigil that denotes the start of a variable name.
- * @param ended The sigil that denotes the end of a variable name.
- */
- public SigilOperator( final String began, final String ended ) {
- this( new Sigils( began, ended ) );
- }
-
- SigilOperator( final Sigils sigils ) {
- assert sigils != null;
-
- mSigils = sigils;
- }
-
- /**
- * Returns the given {@link String} verbatim. Different implementations
- * can override to inject custom behaviours.
- *
- * @param key Returned verbatim.
- */
- @Override
- public String apply( final String key ) {
- return key;
- }
-
- /**
- * Wraps the given key in the began and ended tokens. This may perform any
- * preprocessing necessary to ensure the transformation happens.
- *
- * @param key The variable name to transform.
- * @return The given key with before/after sigils to delimit the key name.
- */
- public String entoken( final String key ) {
- assert key != null;
- assert !key.startsWith( getBegan() );
- assert !key.endsWith( getEnded() );
-
- return getBegan() + key + getEnded();
- }
-
- /**
- * Removes start and stop definition key delimiters from the given key. This
- * method does not check for delimiters, only that there are sufficient
- * characters to remove from either end of the given key.
- *
- * @param key The key adorned with start and stop tokens.
- * @return The given key with the delimiters removed.
- */
- public String detoken( final String key ) {
- return key;
- }
-
- public Sigils getSigils() {
- return mSigils;
- }
-
- String getBegan() {
- return mSigils.getBegan();
- }
-
- String getEnded() {
- return mSigils.getEnded();
- }
-
- @Override
- public String toString() {
- return mSigils.toString();
- }
-}
src/main/java/com/keenwrite/sigils/Sigils.java
-/* Copyright 2020-2021 White Magic Software, Ltd. -- All rights reserved. */
-package com.keenwrite.sigils;
-
-import java.util.AbstractMap.SimpleImmutableEntry;
-
-/**
- * Convenience class for pairing a start and an end sigil together.
- */
-public final class Sigils extends SimpleImmutableEntry<String, String> {
-
- /**
- * Associates a new key-value pair.
- *
- * @param began The starting sigil.
- * @param ended The ending sigil.
- */
- public Sigils( final String began, final String ended ) {
- super( began, ended );
-
- assert began != null;
- assert !began.isBlank();
- assert ended != null;
- assert !ended.isBlank();
- }
-
- /**
- * @return The opening sigil token.
- */
- public String getBegan() {
- return getKey();
- }
-
- /**
- * @return The closing sigil token, or the empty string if none set.
- */
- public String getEnded() {
- return getValue();
- }
-
- @Override
- public String toString() {
- return getBegan() + getEnded();
- }
-}
src/main/java/com/keenwrite/sigils/YamlSigilOperator.java
-/* Copyright 2020-2021 White Magic Software, Ltd. -- All rights reserved. */
-package com.keenwrite.sigils;
-
-/**
- * Responsible for bracketing definition keys with token delimiters.
- */
-public final class YamlSigilOperator extends SigilOperator {
- public YamlSigilOperator( final Sigils sigils ) {
- super( sigils );
- }
-
- /**
- * Removes start and stop definition key delimiters from the given key.
- *
- * @param key The key that may have start and stop tokens.
- * @return The given key with the delimiters removed.
- */
- public String detoken( final String key ) {
- final var began = getBegan();
- final var ended = getEnded();
- final int bLength = began.length();
- final int eLength = ended.length();
- final var bIndex = key.indexOf( began );
- final var eIndex = key.indexOf( ended, bIndex );
- final var kLength = key.length();
-
- return key.substring(
- bIndex == -1 ? 0 : bLength, eIndex == -1 ? kLength : kLength - eLength );
- }
-}