Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/keenwrite.git
M build.gradle
1
version = '1.0.6'
1
version = '1.0.7'
22
33
apply plugin: 'java'
M src/main/java/com/scrivenvar/processors/AbstractProcessor.java
3939
public abstract class AbstractProcessor<T> implements Processor<T> {
4040
41
  protected static final char NEWLINE = '\n';
42
43
  /**
44
   * When performing string searches using indexOf, a return value of -1
45
   * indicates that the string could not be found.
46
   */
47
  protected static final int INDEX_NOT_FOUND = -1;
48
4149
  /**
4250
   * Used while processing the entire chain; null to signify no more links.
M src/main/java/com/scrivenvar/processors/CaretInsertionProcessor.java
4343
4444
  private final IntegerProperty caretPosition = new SimpleIntegerProperty();
45
  private final static String NEWLINE_CARET_POSITION_MD = NEWLINE + CARET_POSITION_MD;
4546
4647
  public CaretInsertionProcessor(
...
5657
   *
5758
   * @param text The text document to change.
58
   * @param i The caret position token insertion point to use, or -1 to
59
   * return the text without any injection.
59
   * @param i The caret position token insertion point to use, or -1 to return
60
   * the text without any injection.
6061
   *
6162
   * @return The given text with a caret position token inserted at the given
6263
   * offset.
6364
   */
6465
  protected String inject( final String text, final int i ) {
65
    return i > 0 && i <= text.length()
66
      ? new StringBuilder( text ).replace( i, i, CARET_POSITION_MD ).toString()
67
      : text;
66
    if( i > 0 && i <= text.length() ) {
67
      final String replacement = text.charAt( i - 1 ) == NEWLINE
68
        ? NEWLINE_CARET_POSITION_MD
69
        : CARET_POSITION_MD;
70
71
      return new StringBuilder( text ).replace( i, i, replacement ).toString();
72
    }
73
74
    return text;
75
  }
76
77
  /**
78
   * Returns true if i is greater than or equal to min and less than or equal to
79
   * max.
80
   *
81
   * @param i The value to check.
82
   * @param min The lower bound.
83
   * @param max The upper bound.
84
   *
85
   * @return false The value of i is either lower than min or greater than max.
86
   */
87
  protected boolean isBetween( int i, int min, int max ) {
88
    return i >= min && i <= max;
6889
  }
6990
M src/main/java/com/scrivenvar/processors/CaretReplacementProcessor.java
3838
 */
3939
public class CaretReplacementProcessor extends AbstractProcessor<String> {
40
  private static final int INDEX_NOT_FOUND = -1;
4140
4241
  public CaretReplacementProcessor( final Processor<String> processor ) {
M src/main/java/com/scrivenvar/processors/ProcessorFactory.java
132132
    final ObservableValue<Integer> caret = tab.caretPositionProperty();
133133
    final Processor<String> tpc = getCommonProcessor();
134
    final Processor<String> cip = createInsertionProcessor( tpc, caret );
134
    final Processor<String> cip = createMarkdownInsertionProcessor( tpc, caret );
135135
    final Processor<String> dvp = new DefaultVariableProcessor( cip, getResolvedMap() );
136136
137137
    return dvp;
138
  }
139
140
  protected Processor<String> createRProcessor( final FileEditorTab tab ) {
141
    final ObservableValue<Integer> caret = tab.caretPositionProperty();
142
    final Processor<String> tpc = getCommonProcessor();
143
    final Processor<String> cip = createInsertionProcessor( tpc, caret );
144
    final Processor<String> rp = new InlineRProcessor( cip, getResolvedMap(), tab.getPath() );
145
    final Processor<String> rvp = new RVariableProcessor( rp, getResolvedMap() );
146
147
    return rvp;
148138
  }
149139
...
156146
157147
    return dvp;
148
  }
149
150
  protected Processor<String> createRProcessor( final FileEditorTab tab ) {
151
    final ObservableValue<Integer> caret = tab.caretPositionProperty();
152
    final Processor<String> tpc = getCommonProcessor();
153
    final Processor<String> rp = new InlineRProcessor( tpc, getResolvedMap(), tab.getPath() );
154
    final Processor<String> rvp = new RVariableProcessor( rp, getResolvedMap() );
155
    final Processor<String> cip = createRInsertionProcessor( rvp, caret );
156
157
    return cip;
158158
  }
159159
...
169169
  }
170170
171
  private Processor<String> createInsertionProcessor(
171
  private Processor<String> createMarkdownInsertionProcessor(
172172
    final Processor<String> tpc, final ObservableValue<Integer> caret ) {
173173
    return new MarkdownCaretInsertionProcessor( tpc, caret );
174
  }
175
176
  /**
177
   * Create an insertion processor that is aware of R statements and will insert
178
   * a caret outside of any statement the caret falls within.
179
   *
180
   * @param processor Another link in the processor chain.
181
   * @param caret The caret insertion point.
182
   *
183
   * @return A processor that can insert a caret token without disturbing any R
184
   * code.
185
   */
186
  private Processor<String> createRInsertionProcessor(
187
    final Processor<String> processor, final ObservableValue<Integer> caret ) {
188
    return new RMarkdownCaretInsertionProcessor( processor, caret );
174189
  }
175190
A src/main/java/com/scrivenvar/processors/RMarkdownCaretInsertionProcessor.java
1
/*
2
 * Copyright 2016 White Magic Software, Ltd.
3
 *
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions are met:
8
 *
9
 *  o Redistributions of source code must retain the above copyright
10
 *    notice, this list of conditions and the following disclaimer.
11
 *
12
 *  o Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
 */
28
package com.scrivenvar.processors;
29
30
import static com.scrivenvar.decorators.RVariableDecorator.PREFIX;
31
import static com.scrivenvar.decorators.RVariableDecorator.SUFFIX;
32
import static java.lang.Integer.max;
33
import javafx.beans.value.ObservableValue;
34
35
/**
36
 * Responsible for inserting a caret position token into an R document.
37
 *
38
 * @author White Magic Software, Ltd.
39
 */
40
public class RMarkdownCaretInsertionProcessor
41
  extends MarkdownCaretInsertionProcessor {
42
43
  /**
44
   * Constructs a processor capable of inserting a caret marker into Markdown.
45
   *
46
   * @param processor The next processor in the chain.
47
   * @param position The caret's current position in the text.
48
   */
49
  public RMarkdownCaretInsertionProcessor(
50
    final Processor<String> processor,
51
    final ObservableValue<Integer> position ) {
52
    super( processor, position );
53
  }
54
55
  /**
56
   * Changes the text to insert a "caret" at the caret position. This will
57
   * insert the unique key of Constants.MD_CARET_POSITION into the document.
58
   *
59
   * @param text The text document to process.
60
   *
61
   * @return The text with the caret position token inserted at the caret
62
   * position.
63
   */
64
  @Override
65
  public String processLink( final String text ) {
66
    int offset = getCaretPosition();
67
68
    // Search for inline R code from the start of the caret's paragraph.
69
    int index = text.lastIndexOf( NEWLINE, offset );
70
71
    if( index == INDEX_NOT_FOUND ) {
72
      index = 0;
73
    }
74
75
    // Scan for an inline R statement, either from the nearest paragraph or
76
    // the beginning of the file, whichever was found first.
77
    index = text.indexOf( PREFIX, index );
78
79
    // If there was no R prefix then insert at the caret's initial offset...
80
    if( index != INDEX_NOT_FOUND ) {
81
      // Otherwise, retain the starting index of the first R statement in the
82
      // paragraph.
83
      int rPrefix = index + 1;
84
85
      // Scan for inline R prefixes until the text is exhausted or indexed
86
      // beyond the caret position.
87
      while( index != INDEX_NOT_FOUND && index < offset ) {
88
        // Set rPrefix to the index that might precede the caret.
89
        rPrefix = index + 1;
90
91
        // If there are no more R prefixes, exit the loop and look for a
92
        // suffix starting from the rPrefix position.
93
        index = text.indexOf( PREFIX, rPrefix );
94
      }
95
96
      // Scan from the character after the R prefix up to any R suffix.
97
      final int rSuffix = max( text.indexOf( SUFFIX, rPrefix ), rPrefix );
98
99
      final boolean between = isBetween( offset, rPrefix, rSuffix );
100
      
101
      // Insert the caret marker at the start of the R statement.
102
      if( between ) {
103
        offset = rPrefix - 1;
104
      }
105
    }
106
107
    return inject( text, offset );
108
  }
109
}
1110
M src/main/java/com/scrivenvar/processors/RVariableProcessor.java
6666
6767
    for( final String key : map.keySet() ) {
68
      rMap.put( toR( key ), '\'' + map.get( key ) + '\'' );
68
      rMap.put( toRKey( key ), toRValue( map.get( key ) ) );
6969
    }
7070
...
8080
   * @return The transformed variable name.
8181
   */
82
  private String toR( final String key ) {
82
  private String toRKey( final String key ) {
8383
    // Replace all the periods with dollar symbols.
8484
    final StringBuilder sb = new StringBuilder( 'v' + key );
...
9797
9898
    return sb.toString();
99
  }
100
101
  private String toRValue( final String value ) {
102
    return '\'' + escape( value, '\'', "\\'" ) + '\'';
103
  }
104
105
  /**
106
   * TODO: Make generic method for replacing text.
107
   * 
108
   * @see CaretReplacementProcessor.replace
109
   *
110
   * @param haystack Search this string for the needle, must not be null.
111
   * @param needle The character to find in the haystack.
112
   * @param thread Replace the needle with this text, if the needle is found.
113
   *
114
   * @return The haystack with the all instances of needle replaced with thread.
115
   */
116
  private String escape(
117
    final String haystack, final char needle, final String thread ) {
118
    int end = haystack.indexOf( needle );
119
120
    if( end < 0 ) {
121
      return haystack;
122
    }
123
124
    final int length = haystack.length();
125
    int start = 0;
126
127
    // Replace up to 32 occurrences before the string reallocates its buffer.
128
    final StringBuilder sb = new StringBuilder( length + 32 );
129
130
    while( end >= 0 ) {
131
      sb.append( haystack.substring( start, end ) ).append( thread );
132
      start = end + 1;
133
      end = haystack.indexOf( needle, start );
134
    }
135
136
    return sb.append( haystack.substring( start ) ).toString();
99137
  }
100138
}
M src/main/java/com/scrivenvar/processors/XMLCaretInsertionProcessor.java
121121
  }
122122
123
  private boolean isBetween( int i, int min, int max ) {
124
    return i >= min && i <= max;
125
  }
126
127123
  /**
128124
   * Parses the given XML document and returns a high-performance navigator