From 3b3b23d4337d4b5b6c998101a410cb5669e277de Mon Sep 17 00:00:00 2001 From: jdf Date: Fri, 9 Apr 2010 02:23:26 +0000 Subject: [PATCH] Fix #1450 --- app/src/processing/app/syntax/Brackets.java | 133 ++++++++++++++++++ .../processing/app/syntax/JEditTextArea.java | 10 +- .../processing/app/syntax/TextUtilities.java | 108 -------------- 3 files changed, 140 insertions(+), 111 deletions(-) create mode 100644 app/src/processing/app/syntax/Brackets.java diff --git a/app/src/processing/app/syntax/Brackets.java b/app/src/processing/app/syntax/Brackets.java new file mode 100644 index 000000000..b5675eed4 --- /dev/null +++ b/app/src/processing/app/syntax/Brackets.java @@ -0,0 +1,133 @@ +package processing.app.syntax; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Jonathan Feinberg <jdf@pobox.com> + * + */ +public class Brackets { + private volatile List offsets = null; + + public void invalidate() { + offsets = null; + } + + public int findMatchingBracket(final String text, final int pos) { + if (offsets == null) + parse(text); + // find this bracket + int p; + for (p = 0; p < offsets.size(); p++) + if (offsets.get(p) == pos) + break; + if (p == offsets.size()) { + return -1; + } + final int direction; + final char alpha = text.charAt(offsets.get(p)); + final char beta; + switch (alpha) { + case '(': + beta = ')'; + direction = 1; + break; + case ')': + beta = '('; + direction = -1; + break; + case '[': + beta = ']'; + direction = 1; + break; + case ']': + beta = '['; + direction = -1; + break; + case '{': + beta = '}'; + direction = 1; + break; + case '}': + beta = '{'; + direction = -1; + break; + default: + System.err.println("Unexpected char " + alpha + " at position " + p); + return -1; + } + int depth = 1; + for (p += direction; p >= 0 && p < offsets.size(); p += direction) { + final int offset = offsets.get(p); + final char c = text.charAt(offset); + if (c == alpha) + depth++; + else if (c == beta) + depth--; + if (depth == 0) + return offset; + } + return -1; + } + + int pos; + + private void parse(final String text) { + offsets = new ArrayList(); + final int len = text.length(); + for (pos = 0; pos < len; pos++) { + final char c = text.charAt(pos); + if (c == '/' && (pos < len - 1)) { + final char d = text.charAt(++pos); + if (d == '/') { + readComment(text); + } else if (d == '*') { + readMLComment(text); + } + } else if (c == '"' || c == '\'') { + readString(text, c); + } else if (c == '{' || c == '[' || c == '(' || c == '}' || c == ']' + || c == ')') { + offsets.add(pos); + } + } + } + + private void readString(final String text, final char q) { + final int len = text.length(); + for (pos++; pos < len; pos++) { + final char c = text.charAt(pos); + if (c == q) { + return; + } + if (c == '\\') { + pos++; + } + } + } + + private void readComment(final String text) { + final int len = text.length(); + for (pos++; pos < len; pos++) + if (text.charAt(pos) == '\n') { + pos++; + return; + } + } + + private void readMLComment(final String text) { + final int len = text.length(); + for (pos++; pos < len; pos++) { + final char c = text.charAt(pos); + if (c == '*' && (pos < len - 1)) { + pos++; + final char d = text.charAt(pos); + if (d == '/') { + return; + } + } + } + } +} diff --git a/app/src/processing/app/syntax/JEditTextArea.java b/app/src/processing/app/syntax/JEditTextArea.java index 0eef6c879..88c0a19b8 100644 --- a/app/src/processing/app/syntax/JEditTextArea.java +++ b/app/src/processing/app/syntax/JEditTextArea.java @@ -129,6 +129,8 @@ public class JEditTextArea extends JComponent } }); } + + private Brackets bracketHelper = new Brackets(); /** * Inline Input Method Support for Japanese. @@ -1714,8 +1716,8 @@ public class JEditTextArea extends JComponent try { - int offset = TextUtilities.findMatchingBracket( - document,newCaretPosition - 1); + int offset = bracketHelper.findMatchingBracket(document.getText(0, + document.getLength()), newCaretPosition - 1); if(offset != -1) { bracketLine = getLineOfOffset(offset); @@ -1733,6 +1735,8 @@ public class JEditTextArea extends JComponent protected void documentChanged(DocumentEvent evt) { + bracketHelper.invalidate(); + DocumentEvent.ElementChange ch = evt.getChange(document.getDefaultRootElement()); @@ -2112,7 +2116,7 @@ public class JEditTextArea extends JComponent return; try { - int bracket = TextUtilities.findMatchingBracket(document, + int bracket = bracketHelper.findMatchingBracket(document.getText(0, document.getLength()), Math.max(0,dot - 1)); if (bracket != -1) { int mark = getMarkPosition(); diff --git a/app/src/processing/app/syntax/TextUtilities.java b/app/src/processing/app/syntax/TextUtilities.java index 29db67716..55cf94bcf 100644 --- a/app/src/processing/app/syntax/TextUtilities.java +++ b/app/src/processing/app/syntax/TextUtilities.java @@ -9,7 +9,6 @@ package processing.app.syntax; -import javax.swing.text.*; /** * Class with several utility functions used by the text area component. @@ -18,113 +17,6 @@ import javax.swing.text.*; */ public class TextUtilities { - /** - * Returns the offset of the bracket matching the one at the - * specified offset of the document, or -1 if the bracket is - * unmatched (or if the character is not a bracket). - * @param doc The document - * @param offset The offset - * @exception BadLocationException If an out-of-bounds access - * was attempted on the document text - */ - public static int findMatchingBracket(Document doc, int offset) - throws BadLocationException - { - if(doc.getLength() == 0) - return -1; - char c = doc.getText(offset,1).charAt(0); - char cprime; // c` - corresponding character - boolean direction; // true = back, false = forward - - switch(c) - { - case '(': cprime = ')'; direction = false; break; - case ')': cprime = '('; direction = true; break; - case '[': cprime = ']'; direction = false; break; - case ']': cprime = '['; direction = true; break; - case '{': cprime = '}'; direction = false; break; - case '}': cprime = '{'; direction = true; break; - default: return -1; - } - - int count; - - // How to merge these two cases is left as an exercise - // for the reader. - - // Go back or forward - if(direction) - { - // Count is 1 initially because we have already - // `found' one closing bracket - count = 1; - - // Get text[0,offset-1]; - String text = doc.getText(0,offset); - - // Scan backwards - for(int i = offset - 1; i >= 0; i--) - { - // If text[i] == c, we have found another - // closing bracket, therefore we will need - // two opening brackets to complete the - // match. - char x = text.charAt(i); - if(x == c) - count++; - - // If text[i] == cprime, we have found a - // opening bracket, so we return i if - // --count == 0 - else if(x == cprime) - { - if(--count == 0) - return i; - } - } - } - else - { - // Count is 1 initially because we have already - // `found' one opening bracket - count = 1; - - // So we don't have to + 1 in every loop - offset++; - - // Number of characters to check - int len = doc.getLength() - offset; - - // Get text[offset+1,len]; - String text = doc.getText(offset,len); - - // Scan forwards - for(int i = 0; i < len; i++) - { - // If text[i] == c, we have found another - // opening bracket, therefore we will need - // two closing brackets to complete the - // match. - char x = text.charAt(i); - - if(x == c) - count++; - - // If text[i] == cprime, we have found an - // closing bracket, so we return i if - // --count == 0 - else if(x == cprime) - { - if(--count == 0) - return i + offset; - } - } - } - - // Nothing found - return -1; - } - /** * Locates the start of the word at the specified position. * @param line The text