Dave Jarvis' Repositories

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

Caret token no longer interferes with XML elements.

Authordjarvis <email>
Date2016-12-17 18:43:47 GMT-0800
Commit9cabd392215d50ebd487522b44014d49b7852261
Parentff26965
Delta126 lines added, 94 lines removed, 32-line increase
src/main/java/com/scrivenvar/processors/CaretInsertionProcessor.java
+/*
+ * Copyright 2016 White Magic Software, Ltd.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * o Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.scrivenvar.processors;
+
+import static com.scrivenvar.Constants.CARET_POSITION_MD;
+
+/**
+ * Base class for inserting the magic CARET POSITION into the text so that, upon
+ * previewing, the preview pane can scroll to the correct position (relative to
+ * the caret position in the editor).
+ *
+ * @author White Magic Software, Ltd.
+ */
+public abstract class CaretInsertionProcessor extends AbstractProcessor<String> {
+
+ private final int caretPosition;
+
+ public CaretInsertionProcessor(
+ final Processor<String> processor, final int position ) {
+ super( processor );
+ this.caretPosition = position;
+ }
+
+ /**
+ * Inserts the caret position token into the text at an offset that won't
+ * interfere with parsing the text itself, regardless of text format.
+ *
+ * @param text The text document to change.
+ * @param i The caret position token insertion point to use, or -1 to
+ * return the text without any injection.
+ *
+ * @return The given text with a caret position token inserted at the given
+ * offset.
+ */
+ protected String inject( final String text, final int i ) {
+ return i > 0 && i <= text.length()
+ ? new StringBuilder( text ).replace( i, i, CARET_POSITION_MD ).toString()
+ : text;
+ }
+
+ /**
+ * Returns the editor's caret position.
+ *
+ * @return Where the user has positioned the caret.
+ */
+ protected int getCaretPosition() {
+ return this.caretPosition;
+ }
+}
src/main/java/com/scrivenvar/processors/MarkdownCaretInsertionProcessor.java
package com.scrivenvar.processors;
-import static com.scrivenvar.Constants.CARET_POSITION_MD;
import static java.lang.Character.isLetter;
import static java.lang.Math.min;
/**
- * Responsible for inserting the magic CARET POSITION into the markdown so that,
- * upon rendering into HTML, the HTML pane can scroll to the correct position
- * (relative to the caret position in the editor).
+ * Responsible for inserting a caret position token into a markdown document.
*
* @author White Magic Software, Ltd.
*/
-public class MarkdownCaretInsertionProcessor extends AbstractProcessor<String> {
-
- private final int caretPosition;
+public class MarkdownCaretInsertionProcessor extends CaretInsertionProcessor {
/**
* Constructs a processor capable of inserting a caret marker into Markdown.
*
* @param processor The next processor in the chain.
- * @param position The caret's current position in the text, cannot be null.
+ * @param position The caret's current position in the text.
*/
public MarkdownCaretInsertionProcessor(
final Processor<String> processor, final int position ) {
- super( processor );
- this.caretPosition = position;
+ super( processor, position );
}
/**
* Changes the text to insert a "caret" at the caret position. This will
* insert the unique key of Constants.MD_CARET_POSITION into the document.
*
- * @param t The document text to process.
+ * @param t The text document to process.
*
- * @return The document text with the Markdown caret text inserted at the
- * caret position (given at construction time).
+ * @return The text with the caret position token inserted at the caret
+ * position.
*/
@Override
// 4. Find the nearest text node to the caret.
// 5. Insert the CARET_POSITION_MD value in the text at that offsset.
-
// Insert the caret at the closest non-Markdown delimiter (i.e., the
// closest character from the caret position forward).
while( offset < length && !isLetter( t.charAt( offset ) ) ) {
offset++;
}
-
- // Insert the caret position into the Markdown text, but don't interfere
- // with the Markdown iteself.
- return new StringBuilder( t ).replace(
- offset, offset, CARET_POSITION_MD ).toString();
- }
- /**
- * Returns the editor's caret position.
- *
- * @return Where the user has positioned the caret.
- */
- private int getCaretPosition() {
- return this.caretPosition;
+ return inject( t, offset );
}
}
src/main/java/com/scrivenvar/processors/XMLCaretInsertionProcessor.java
import com.scrivenvar.FileEditorTab;
+import com.ximpleware.VTDException;
import com.ximpleware.VTDGen;
import static com.ximpleware.VTDGen.TOKEN_CHARACTER_DATA;
import com.ximpleware.VTDNav;
+import java.text.ParseException;
/**
* Inserts a caret position indicator into the document.
*
* @author White Magic Software, Ltd.
*/
-public class XMLCaretInsertionProcessor extends AbstractProcessor<String> {
+public class XMLCaretInsertionProcessor extends CaretInsertionProcessor {
private FileEditorTab tab;
/**
+ * Constructs a processor capable of inserting a caret marker into XML.
*
- * @param processor Next link in the processing chain.
- * @param tab
+ * @param processor The next processor in the chain.
+ * @param position The caret's current position in the text, cannot be null.
*/
- public XMLCaretInsertionProcessor( final Processor<String> processor, final FileEditorTab tab ) {
- super( processor );
- setFileEditorTab( tab );
+ public XMLCaretInsertionProcessor(
+ final Processor<String> processor, final int position ) {
+ super( processor, position );
}
@Override
public String processLink( final String t ) {
+ final int caretOffset = getCaretPosition();
int insertOffset = -1;
if( t.length() > 0 ) {
try {
- final VTDGen vg = new VTDGen();
-
- vg.setDoc( t.getBytes() );
- vg.parse( true );
-
- final VTDNav vn = vg.getNav();
+ final VTDNav vn = getNavigator( t );
- final int caretOffset = getCaretPosition();
final int tokens = vn.getTokenCount();
- int currTextTokenIndex = 0;
- int prevTextTokenIndex = currTextTokenIndex;
+ int currTokenIndex = 0;
+ int prevTokenIndex = currTokenIndex;
int currTokenOffset = 0;
boolean found = false;
// To find the insertion spot even faster, the algorithm could
// use a binary search or interpolation search algorithm. This
// would reduce the worst-case iterations to O(log n) from O(n).
- while( currTextTokenIndex < tokens && !found ) {
- final int prevTokenOffset = currTokenOffset;
- final int currTokenType = vn.getTokenType( currTextTokenIndex );
-
- if( currTokenType == TOKEN_CHARACTER_DATA ) {
- currTokenOffset = vn.getTokenOffset( currTextTokenIndex );
+ while( currTokenIndex < tokens && !found ) {
+ if( vn.getTokenType( currTokenIndex ) == TOKEN_CHARACTER_DATA ) {
+ final int prevTokenOffset = currTokenOffset;
+ currTokenOffset = vn.getTokenOffset( currTokenIndex );
if( currTokenOffset > caretOffset ) {
found = true;
- final int prevTokenLength = vn.getTokenLength( prevTextTokenIndex );
+ final int prevTokenLength = vn.getTokenLength( prevTokenIndex );
// If the caret falls within the limits of the previous token, then
// insert the caret position marker at the caret offset.
if( isBetween( caretOffset, prevTokenOffset, prevTokenOffset + prevTokenLength ) ) {
insertOffset = caretOffset;
} else {
// The caret position is outside the previous token's text
- // boundaries, but the current text token is far away. The
- // cursor should be positioned into the closer text token.
+ // boundaries, but not inside the current text token. The
+ // caret should be positioned into the closer text token.
// For now, the cursor is positioned at the start of the
// current text token.
insertOffset = currTokenOffset;
}
+ // Done.
continue;
}
- prevTextTokenIndex = currTextTokenIndex;
+ prevTokenIndex = currTokenIndex;
}
- currTextTokenIndex++;
+ currTokenIndex++;
}
} catch( final Exception ex ) {
- ex.printStackTrace();
+ throw new RuntimeException(
+ new ParseException( ex.getMessage(), caretOffset )
+ );
}
- }
-
-
- /*
- System.out.println( "-- CARET --------------------------------" );
- System.out.println( "offset: " + caretOffset );
- System.out.println( "-- BETWEEN PREV TOKEN --------------------" );
- System.out.println( "index : " + prevTextTokenIndex );
- System.out.println( "type : " + prevTokenType );
- System.out.println( "offset : " + prevTokenOffset );
- System.out.println( "length : " + prevTokenLength );
- System.out.println( "offset + length: " + (prevTokenOffset + prevTokenLength - 1) );
- System.out.println( "text : '" + prevToken.trim() + "'" );
- System.out.println( "-- CURR TOKEN ---------------------------" );
- System.out.println( "index : " + currTextTokenIndex );
- System.out.println( "type : " + currTokenType );
- System.out.println( "offset : " + currTokenOffset );
- System.out.println( "length : " + currTokenLength );
- System.out.println( "between: " + currBetween );
- System.out.println( "text : '" + currToken.trim() + "'" );
- */
-
-
- if( insertOffset > 0 ) {
- // Insert the caret at the given offset.
- // TODO: Create and use CaretInsertion superclass.
- System.out.println( "insert offset: " + insertOffset );
- System.out.println( "caret offset : " + getCaretPosition() );
}
- return t;
+ return inject( t, insertOffset );
}
-
-
- private int getCaretPosition() {
- return getFileEditorTab().getCaretPosition();
+ private boolean isBetween( int i, int min, int max ) {
+ return i >= min && i <= max;
}
- private void setFileEditorTab( final FileEditorTab tab ) {
- this.tab = tab;
- }
+ /**
+ * Parses the given XML document and returns a high-performance navigator
+ * instance for scanning through the XML elements.
+ *
+ * @param xml
+ *
+ * @return
+ */
+ private VTDNav getNavigator( final String xml ) throws VTDException {
+ final VTDGen vg = getParser();
- private FileEditorTab getFileEditorTab() {
- return this.tab;
+ vg.setDoc( xml.getBytes() );
+ vg.parse( true );
+ return vg.getNav();
}
- private boolean isBetween( int i, int min, int max ) {
- return i >= min && i <= max;
+ private VTDGen getParser() {
+ return new VTDGen();
}
}