Dave Jarvis' Repositories

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

Inserting words and sub-words from YAML variable names into editor.

Authordjarvis <email>
Date2016-11-03 22:17:32 GMT-0700
Commit9800e15cd06f44bd8490f188eef71d1962d5f947
Parente0b7cdd
Delta150 lines added, 39 lines removed, 111-line increase
src/main/java/com/scrivendor/definition/DefinitionPane.java
import java.util.List;
import java.util.StringTokenizer;
+import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.control.MultipleSelectionModel;
* path.
*/
- public String findNode( final String path ) {
- TreeItem<String> cItem = getTreeView().getRoot();
+ public TreeItem<String> findNode( final String path ) {
+ TreeItem<String> cItem = getTreeRoot();
TreeItem<String> pItem = cItem;
final StringTokenizer st = new StringTokenizer( path, getSeparator() );
+ // Search along a single branch while the tokenized path matches nodes.
while( st.hasMoreTokens() ) {
- final String word = st.nextToken();
-
- // Search along a single branch while the tokenized path matches nodes.
- cItem = findLeaf( cItem, word );
-
- if( cItem == null ) {
+ if( (cItem = findLeaf( cItem, st.nextToken() )) == null ) {
break;
}
pItem = cItem;
}
- String value = (pItem == null ? "" : pItem.getValue());
+ return pItem.isLeaf() ? pItem.getParent() : pItem;
+ }
- System.out.println( "Current = " + value );
+ /**
+ * Returns the last word after the separator.
+ *
+ * @param path The path to a node, which can include a partial node match.
+ *
+ * @return All characters after the last dot.
+ */
+ public String findLastWord( final String path ) {
+ final int index = path.lastIndexOf( getSeparator() );
+ return index > 0 ? path.substring( index + 1 ) : path;
+ }
- return value;
+ /**
+ * Expands the node to the root, recursively.
+ *
+ * @param node The node to expand.
+ */
+ public void expand( TreeItem<String> node ) {
+ if( node != null ) {
+ expand( node.getParent() );
+
+ if( !node.isLeaf() ) {
+ node.setExpanded( true );
+ }
+ }
}
/**
- * Finds an exact match
+ * Collapses the tree, recursively.
+ */
+ public void collapse() {
+ collapse( getTreeRoot().getChildren() );
+ }
+
+ private void collapse( ObservableList<TreeItem<String>> nodes ) {
+ for( final TreeItem<String> node : nodes ) {
+ node.setExpanded( false );
+ collapse( node.getChildren() );
+ }
+ }
+
+ /**
+ * Finds a tree item with a value that exactly matches the given word.
*
* @param trunk The root item containing a list of nodes to search.
return result;
- }
-
- /**
- * Starting at the given parent node, this will return a dot-separated path of
- * all the parent node values concatenated together.
- *
- * @param ti The leaf parent tree item.
- *
- * @return The dot-separated path for this node.
- private String toPath( final TreeItem<String> ti ) {
- // Recurse to the root node, then append the nodes in dot-formation.
- // Iteration would be possible as well, but that requires string
- // insertion, which would end up creating a copy of the string each
- // loop. Plus, it's one line of code.
- return ti == null || ti.getParent() == null
- ? ""
- : toPath( ti.getParent() ) + getSeparator() + ti.getValue();
}
- */
/**
private boolean areEqual( final String s1, final String s2 ) {
return s1.equalsIgnoreCase( s2 );
+ }
+
+ /**
+ * Answers whether the given strings match each other. What match means will
+ * depend on user preferences.
+ *
+ * @param s1 The string to compare against s2.
+ * @param s2 The string to compare against s1.
+ *
+ * @return true if s1 and s2 are a match according to some criteria.
+ */
+ public boolean matches( final String s1, final String s2 ) {
+ return s1.toLowerCase().contains( s2.toLowerCase() );
}
}
+ /**
+ * Returns the root node to the tree view.
+ *
+ * @return getTreeView()
+ */
public Node getNode() {
return getTreeView();
}
- public final TreeView<String> getTreeView() {
+ /**
+ * Returns the tree view that contains the YAML definition hierarchy.
+ *
+ * @return A non-null instance.
+ */
+ private TreeView<String> getTreeView() {
return this.treeView;
+ }
+
+ /**
+ * Returns the root of the tree.
+ *
+ * @return The first node added to the YAML definition tree.
+ */
+ private TreeItem<String> getTreeRoot() {
+ return getTreeView().getRoot();
}
src/main/java/com/scrivendor/MainWindow.java
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
+import javafx.scene.control.IndexRange;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.SplitPane;
import javafx.scene.control.ToolBar;
+import javafx.scene.control.TreeItem;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.InputEvent;
import javafx.scene.input.KeyCode;
import static javafx.scene.input.KeyCode.BACK_SPACE;
import static javafx.scene.input.KeyCode.DIGIT2;
import static javafx.scene.input.KeyCode.ESCAPE;
import static javafx.scene.input.KeyCode.MINUS;
import static javafx.scene.input.KeyCode.PERIOD;
-import static javafx.scene.input.KeyCode.UNDERSCORE;
import static javafx.scene.input.KeyCombination.SHIFT_DOWN;
import javafx.scene.input.KeyEvent;
switch( keyCode ) {
case BACK_SPACE:
- deletePreviousChar();
+ keyBackspace();
// Break out of variable mode by back spacing to the original position.
if( getCaretColumn() > getVModePosition() ) {
break;
}
case ESCAPE:
stopVMode();
+ break;
+
+ case ENTER:
+ case END:
+ acceptSelection();
break;
}
- private void deletePreviousChar() {
- getEditor().deletePreviousChar();
+ /**
+ * Called when the user presses the Backspace key.
+ */
+ private void keyBackspace() {
+ final StyledTextArea textArea = getEditor();
+ textArea.replaceSelection( "" );
+ textArea.deletePreviousChar();
+ }
+
+ /**
+ * Called when the user presses either End or Enter key.
+ */
+ private void acceptSelection() {
+ final StyledTextArea textArea = getEditor();
+ final IndexRange range = textArea.getSelection();
+
+ if( range != null ) {
+ textArea.deselect();
+ textArea.moveTo( range.getEnd() );
+ }
}
final String p = getCurrentParagraph();
final String w = p.substring( getVModePosition(), getCaretColumn() ).trim();
+ final DefinitionPane pane = getDefinitionPane();
+ final TreeItem<String> node = pane.findNode( w );
+ final String lastWord = pane.findLastWord( w );
- String variable = getDefinitionPane().findNode( w );
- System.out.println( "variable = " + variable );
+ TreeItem<String> lastNode = node;
+
+ for( final TreeItem<String> leaf : node.getChildren() ) {
+ if( pane.matches( leaf.getValue(), lastWord ) ) {
+ lastNode = leaf;
+ break;
+ }
+ }
+
+ pane.collapse();
+ pane.expand( node );
+
+ String typeAhead = "";
+
+ if( !lastNode.isLeaf() ) {
+ lastNode.setExpanded( true );
+
+ final String nodeValue = lastNode.getValue();
+ final int index = nodeValue.indexOf( lastWord );
+
+ if( index == 0 && !nodeValue.equals( lastWord ) ) {
+ typeAhead = nodeValue.substring( lastWord.length() );
+ }
+ }
+
+ insertText( typeAhead );
+
+ System.out.println( "lastNode = " + lastNode.getValue() );
+ System.out.println( "lastWord = " + lastWord );
}
* @param s The text to insert.
*/
- private void insertText( String s ) {
+ private void insertText( final String s ) {
final StyledTextArea t = getEditor();
- t.insertText( t.getCaretPosition(), s );
+
+ final int length = s.length();
+ final int posBegan = t.getCaretPosition();
+ final int posEnded = posBegan + length;
+
+ t.replaceSelection( s );
+
+ if( posEnded - posBegan > 1 ) {
+ t.selectRange( posEnded, posBegan );
+ }
}