Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
283 changes: 283 additions & 0 deletions src/simplejavatexteditor/AutoComplete.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
package simplejavatexteditor;

import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Collections;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;

/**
* <h1>Auto complete functionality multiple programming languages, including brackets and
* parentheses</h1>
*
* <p>
* An ArrayList is created for the keywords and the brackets.
* Logic for setting the content of the ArrayList is
* found in UI.java. If the word currently being typed
* matches a word in the list, a Runnable inner class is
* implemented to handle the word completion.
*
* Two other inner classes are also used. The second one handles when the enter
* key is pressed in response to an auto complete suggestion. The third one
* performs additional logic on brackets.
* </p>
*
*
* @author Patrick Slagle
* @since 2016-12-03
*/
public class AutoComplete
implements DocumentListener {

private ArrayList<String> brackets = new ArrayList<>();
private ArrayList<String> bracketCompletions = new ArrayList<>();

private ArrayList<String> words = new ArrayList<>();

SupportedKeywords kw;

//Keep track of when code completion
//has been activated
private enum Mode {

INSERT, COMPLETION
};

private final UI ui;
private Mode mode = Mode.INSERT;
private final JTextArea textArea;
private static final String COMMIT_ACTION = "commit";
private boolean isKeyword;
private int pos;
private String content;

public AutoComplete(UI ui, ArrayList<String> al) {
//Set the keywords
words = al;
kw = new SupportedKeywords();
brackets = kw.getbrackets();
bracketCompletions = kw.getbracketCompletions();

//Access the editor
this.ui = ui;
textArea = ui.getEditor();

//Set the handler for the enter key
InputMap im = textArea.getInputMap();
ActionMap am = textArea.getActionMap();
im.put(KeyStroke.getKeyStroke("ENTER "), COMMIT_ACTION);
am.put(COMMIT_ACTION, new CommitAction());

Collections.sort(words);
}

/**
* A character has been typed into the document.
* This method performs the primary
* check to find a keyword completion.
*
* @param e
*/
@Override
public void insertUpdate(DocumentEvent e) {
pos = e.getOffset();
content = null;

try {
content = textArea.getText(0, pos + 1);
} catch (BadLocationException ex) {
ex.printStackTrace();
}

if (e.getLength() != 1) {
return;
}

//Before checking for a keyword
checkForBracket();

//Get the beginning of the word being typed
int start;
for (start = pos; start >= 0; start--) {
if (!Character.isLetter(content.charAt(start))) {
break;
}
}

//Auto complete will start
//after two characters are typed
if (pos - start < 2) {
return;
}

//Search for a match on the word being typed
//in the keywords ArrayList
String prefix = content.substring(start + 1);
int n = Collections.binarySearch(words, prefix);

if (n < 0 && -n < words.size()) {
String match = words.get(-n - 1);

if (match.startsWith(prefix)) {
String completion = match.substring(pos - start);
isKeyword = true;
SwingUtilities.invokeLater(
new CompletionTask(completion, pos + 1));
} else {
mode = Mode.INSERT;
}
}
}

/**
* Performs a check to see if the last
* key typed was one of the supported
* bracket characters
*/
private void checkForBracket() {
//String of the last typed character
char c = content.charAt(pos);
String s = String.valueOf(c);

for (int i = 0; i < brackets.size(); i++) {
if (brackets.get(i).equals(s)) {
isKeyword = false;
SwingUtilities.invokeLater(
new CompletionTask(bracketCompletions.get(i), pos + 1));
}
}
}

/**
* So that future classes can view the keyword list in the future.
*
* @return the keywords
*/
private ArrayList<String> getKeywords() {
return words;
}

/**
* So that these keywords can be modified or added to in the future.
*
* @param keyword the keyword to set
*/
private void setKeywords(String keyword) {
words.add(keyword);
}

/**
* Handles the auto complete suggestion
* generated when the user is typing a
* word that matches a keyword.
*/
private class CompletionTask
implements Runnable {

private final String completion;
private final int position;

public CompletionTask(String completion, int position) {
this.completion = completion;
this.position = position;
}

@Override
public void run() {
textArea.insert(completion, position);

textArea.setCaretPosition(position + completion.length());
textArea.moveCaretPosition(position);
mode = Mode.COMPLETION;
if (!isKeyword) {
textArea.addKeyListener(new HandleBracketEvent());
}
}
}

/**
* Enter key is pressed in response to an
* auto complete suggestion. Respond
* appropriately.
*/
private class CommitAction
extends AbstractAction {

@Override
public void actionPerformed(ActionEvent e) {

if (mode == Mode.COMPLETION) {
int pos = textArea.getSelectionEnd();

if (isKeyword) {
textArea.insert(" ", pos);
textArea.setCaretPosition(pos + 1);
mode = Mode.INSERT;
}
} else {
mode = Mode.INSERT;
textArea.replaceSelection("\n");
}
}
}

/**
* Additional logic for bracket auto complete
*/
private class HandleBracketEvent
implements KeyListener {

@Override
public void keyTyped(KeyEvent e) {
//Bracket auto complete needs special attention.
//Multiple possible responses are needed.
String keyEvent = String.valueOf(e.getKeyChar());
for (String bracketCompletion : bracketCompletions) {
if (keyEvent.equals(bracketCompletion)) {
textArea.replaceRange("", pos, pos + 1);
mode = Mode.INSERT;
textArea.removeKeyListener(this);
}
}
int currentPosition = textArea.getCaretPosition();
switch (e.getKeyChar()) {
case '\n':
textArea.insert("\n\n", currentPosition);
textArea.setCaretPosition(currentPosition + 1);
mode = Mode.INSERT;
textArea.removeKeyListener(this);
break;
default:
textArea.setCaretPosition(pos);
mode = Mode.INSERT;
textArea.removeKeyListener(this);
break;
}
}

@Override
public void keyPressed(KeyEvent e) {
}

@Override
public void keyReleased(KeyEvent e) {
}
}

@Override
public void removeUpdate(DocumentEvent e) {
}

@Override
public void changedUpdate(DocumentEvent e) {
}
}
62 changes: 62 additions & 0 deletions src/simplejavatexteditor/SupportedKeywords.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package simplejavatexteditor;

import java.util.ArrayList;

/**
* <h1>A class to store the programming language keywords and
* provide access to them.</h1>
*
* <p>Makes multiple language support possible and makes adding new language
* support convenient. To add more keywords, add a string array and getters
* to this class. Then, update the switch statement in UI.java.</p>
*/
public class SupportedKeywords {
private String[] java = {"abstract", "assert", "boolean",
"break", "byte", "case", "catch", "char", "class", "const",
"continue", "default", "do", "double", "else", "extends", "false",
"final", "finally", "float", "for", "goto", "if", "implements",
"import", "instanceof", "int", "System", "out", "print()", "println()",
"new", "null", "package", "private", "protected", "public", "interface",
"long", "native", "return", "short", "static", "strictfp", "super", "switch",
"synchronized", "this", "throw", "throws", "transient", "true",
"try", "void", "volatile", "while", "String"};

private String[] cpp = { "auto", "const", "double", "float", "int", "short",
"struct", "unsigned", "break", "continue", "else", "for", "long", "signed",
"switch", "void", "case", "default", "enum", "goto", "register", "sizeof",
"typedef", "volatile", "char", "do", "extern", "if", "return", "static",
"union", "while", "asm", "dynamic_cast", "namespace", "reinterpret_cast", "try",
"bool", "explicit", "new", "static_cast", "typeid", "catch", "false", "operator",
"template", "typename", "class", "friend", "private", "this", "using", "const_cast",
"inline", "public", "throw", "virtual", "delete", "mutable", "protected", "true", "wchar_t" };

private String[] brackets = { "{", "(" };
private String[] bCompletions = { "}", ")" };
public String[] getJavaKeywords() {
return java;
}
public String[] getCppKeywords() {
return cpp;
}
public ArrayList<String> getbracketCompletions() {
ArrayList<String> al = new ArrayList<>();
for(String completion : bCompletions) {
al.add(completion);
}
return al;
}
public ArrayList<String> getbrackets() {
ArrayList<String> al = new ArrayList<>();
for(String completion : brackets) {
al.add(completion);
}
return al;
}
public ArrayList<String> setKeywords(String[] arr) {
ArrayList<String> al = new ArrayList<>();
for(String words : arr) {
al.add(words);
}
return al;
}
}
Loading