Dave Jarvis' Repositories

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

Simplify insertion code, fix bug with clobbering punctuation, make code more I18N-friendly

AuthorDaveJarvis <email>
Date2020-05-31 21:30:43 GMT-0700
Commit837233d7fc8f9bcb9728bd930c4697159f081d58
Parentb48554b
Delta46 lines added, 178 lines removed, 132-line decrease
src/main/java/com/scrivenvar/editors/VariableNameInjector.java
import static com.scrivenvar.definition.FindMode.*;
-import static java.lang.Character.isWhitespace;
import static javafx.scene.input.KeyCode.SPACE;
import static javafx.scene.input.KeyCombination.CONTROL_DOWN;
public static final int DEFAULT_MAX_VAR_LENGTH = 64;
-
- /**
- * TODO: Move this into settings.
- */
- private static final String PUNCTUATION = "\"#$%&'()*+,-/:;<=>?@[]^_`{|}~";
/**
* Recipient of name injections.
*/
- private FileEditorTab tab;
+ private FileEditorTab mTab;
/**
* Initiates double-click events.
*/
- private DefinitionPane definitionPane;
+ private DefinitionPane mDefinitionPane;
/**
/**
- * Traps keys for performing various short-cut tasks, such as @-mode variable
- * insertion and control+space for variable autocomplete.
- *
- * @ key is pressed, a new keyboard map is inserted in place of the current
- * map -- this class goes into "variable edit mode" (a.k.a. vMode).
+ * Traps Control+SPACE to auto-insert definition key names.
*/
private void initKeyboardEventListeners() {
- // Control and space are pressed.
- addKeyboardListener( keyPressed( SPACE, CONTROL_DOWN ),
- this::autocomplete );
+ addKeyboardListener(
+ keyPressed( SPACE, CONTROL_DOWN ),
+ this::autoinsert );
}
/**
- * Pressing control+space will find a node that matches the current word and
- * substitute the YAML variable reference. This is called when the user is not
- * editing in vMode.
+ * Pressing Control+SPACE will find a node that matches the current word and
+ * substitute the YAML variable reference.
*
- * @param e Ignored -- it can only be Ctrl+Space.
+ * @param e Ignored -- it can only be Control+SPACE.
*/
- private void autocomplete( final KeyEvent e ) {
+ private void autoinsert( final KeyEvent e ) {
final String paragraph = getCaretParagraph();
final int[] boundaries = getWordBoundariesAtCaret();
final String word = paragraph.substring( boundaries[ 0 ], boundaries[ 1 ] );
-
- VariableTreeItem<String> leaf = findLeafStartsWith( word );
-
- if( leaf == null ) {
- // If a leaf doesn't match using "starts with", then try using "contains".
- leaf = findLeafContains( word );
- }
-
- if( leaf == null ) {
- leaf = findLeafLevenshtein( word );
- }
+ final VariableTreeItem<String> leaf = findLeaf( word );
if( leaf != null ) {
- replaceText( boundaries[ 0 ], boundaries[ 1 ], leaf.toPath() );
- decorate();
+ replaceText(
+ boundaries[ 0 ],
+ boundaries[ 1 ],
+ decorate( leaf.toPath() )
+ );
+
expand( leaf );
}
}
private int[] getWordBoundariesAtCaret() {
final String paragraph = getCaretParagraph();
- final int column = getCurrentCaretColumn();
- final int offset = column - (column == paragraph.length() ? 1 : 0);
+ int offset = getCurrentCaretColumn();
final BreakIterator wordBreaks = BreakIterator.getWordInstance();
wordBreaks.setText( paragraph );
+
+ // Scan back until the first word is found.
+ while( offset > 0 && wordBreaks.isBoundary( offset ) ) {
+ offset--;
+ }
final int[] boundaries = new int[ 2 ];
boundaries[ 1 ] = wordBreaks.following( offset );
boundaries[ 0 ] = wordBreaks.previous();
return boundaries;
- }
-
- /**
- * Called when autocomplete finishes on a valid leaf or when the user presses
- * Enter to finish manual autocomplete.
- */
- private void decorate() {
- // A little bit of duplication...
- final String paragraph = getCaretParagraph();
- final int[] boundaries = getWordBoundaries( paragraph );
- final String old = paragraph.substring( boundaries[ 0 ], boundaries[ 1 ] );
-
- final String newVariable = decorate( old );
-
- final int posEnded = getCurrentCaretPosition();
- final int posBegan = posEnded - old.length();
-
- getEditor().replaceText( posBegan, posEnded, newVariable );
}
/**
- * Called when user double-clicks on a tree view item.
+ * Injects a variable using the syntax specific to the type of document
+ * being edited.
*
- * @param variable The variable to decorate.
+ * @param variable The variable to decorate in dot-notation without any
+ * start or end tokens present.
*/
private String decorate( final String variable ) {
private int getCurrentParagraph() {
return getEditor().getCurrentParagraph();
- }
-
- /**
- * Returns current word boundary indexes into the current paragraph, excluding
- * punctuation.
- *
- * @param p The paragraph wherein to hunt word boundaries.
- * @param offset The offset into the paragraph to begin scanning left and
- * right.
- * @return The starting and ending index of the word closest to the caret.
- */
- private int[] getWordBoundaries( final String p, final int offset ) {
- // Remove dashes, but retain hyphens. Retain same number of characters
- // to preserve relative indexes.
- final String paragraph = p.replace( "---", " " ).replace( "--", " " );
-
- return getWordAt( paragraph, offset );
- }
-
- /**
- * Helper method to get the word boundaries for the current paragraph.
- *
- * @param paragraph The paragraph to search for word boundaries.
- * @return The word boundary indexes into the paragraph.
- */
- private int[] getWordBoundaries( final String paragraph ) {
- return getWordBoundaries( paragraph, getCurrentCaretColumn() );
- }
-
- /**
- * Given an arbitrary offset into a string, this returns the word at that
- * index. The inputs and outputs include:
- *
- * <ul>
- * <li>surrounded by space: <code>hello | world!</code> ("");</li>
- * <li>end of word: <code>hello| world!</code> ("hello");</li>
- * <li>start of a word: <code>hello |world!</code> ("world");</li>
- * <li>within a word: <code>hello wo|rld!</code> ("world");</li>
- * <li>end of a paragraph: <code>hello world!|</code> ("world");</li>
- * <li>start of a paragraph: <code>|hello world!</code> ("hello"); or</li>
- * <li>after punctuation: <code>hello world!|</code> ("world").</li>
- * </ul>
- *
- * @param p The string to scan for a word.
- * @param offset The offset within s to begin searching for the nearest word
- * boundary, must not be out of bounds of s.
- * @return The word in s at the offset.
- */
- private int[] getWordAt( final String p, final int offset ) {
- return new int[]{getWordBegan( p, offset ), getWordEnded( p, offset )};
- }
-
- /**
- * Returns the index into s where a word begins.
- *
- * @param s Never null.
- * @param offset Index into s to begin searching backwards for a word
- * boundary.
- * @return The index where a word begins.
- */
- private int getWordBegan( final String s, int offset ) {
- while( offset > 0 && isBoundary( s.charAt( offset - 1 ) ) ) {
- offset--;
- }
-
- return offset;
- }
-
- /**
- * Returns the index into s where a word ends.
- *
- * @param s Never null.
- * @param offset Index into s to begin searching forwards for a word boundary.
- * @return The index where a word ends.
- */
- private int getWordEnded( final String s, int offset ) {
- final int length = s.length();
-
- while( offset < length && isBoundary( s.charAt( offset ) ) ) {
- offset++;
- }
-
- return offset;
- }
-
- /**
- * Returns true if the given character can be reasonably expected to be part
- * of a word, including punctuation marks.
- *
- * @param c The character to compare.
- * @return false The character is a space character.
- */
- private boolean isBoundary( final char c ) {
- return !isWhitespace( c ) && !isPunctuation( c );
- }
-
- /**
- * Returns true if the given character is part of the set of Latin (English)
- * punctuation marks.
- *
- * @param c The character to determine whether it is punctuation.
- * @return {@code true} when the given character is in the set of
- * {@link #PUNCTUATION}.
- */
- private static boolean isPunctuation( final char c ) {
- return PUNCTUATION.indexOf( c ) != -1;
}
/**
* Returns the text for the paragraph that contains the caret.
*
* @return A non-null string, possibly empty.
*/
private String getCaretParagraph() {
return getEditor().getText( getCurrentParagraph() );
- }
-
- /**
- * Returns the caret position as an offset into the text.
- *
- * @return A value from 0 to the length of the text (minus one).
- */
- private int getCurrentCaretPosition() {
- return getEditor().getCaretPosition();
}
/**
* Returns the caret position within the current paragraph.
*
* @return A value from 0 to the length of the current paragraph.
*/
private int getCurrentCaretColumn() {
return getEditor().getCaretColumn();
+ }
+
+ private VariableTreeItem<String> findLeaf( final String word ) {
+ assert word != null;
+
+ VariableTreeItem<String> leaf;
+
+ leaf = findLeafStartsWith( word );
+ leaf = leaf == null ? findLeafContains( word ) : leaf;
+ leaf = leaf == null ? findLeafLevenshtein( word ) : leaf;
+
+ return leaf;
}
public FileEditorTab getFileEditorTab() {
- return this.tab;
+ return mTab;
}
- public void setFileEditorTab( final FileEditorTab editorTab ) {
- this.tab = editorTab;
+ public void setFileEditorTab( final FileEditorTab tab ) {
+ mTab = tab;
}
private DefinitionPane getDefinitionPane() {
- return this.definitionPane;
+ return mDefinitionPane;
}
private void setDefinitionPane( final DefinitionPane definitionPane ) {
- this.definitionPane = definitionPane;
+ mDefinitionPane = definitionPane;
}
-
}
src/main/java/com/scrivenvar/definition/VariableTreeItem.java
stack.push( root );
- boolean found = false;
+ // Don't try to find keys for blank/empty variable values.
+ boolean found = text.isBlank();
VariableTreeItem<T> node = null;