|
What this is
Other links
The source code/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.editor.java; import javax.swing.text.BadLocationException; import javax.swing.text.Caret; import org.netbeans.editor.BaseDocument; import org.netbeans.editor.TokenID; import org.netbeans.editor.TokenProcessor; import org.netbeans.editor.TokenContextPath; import org.netbeans.editor.ext.ExtSyntaxSupport; import org.netbeans.editor.ext.java.JavaTokenContext; import org.netbeans.editor.SyntaxSupport; import org.netbeans.editor.TokenItem; import org.netbeans.editor.Settings; import org.netbeans.editor.Utilities; import org.netbeans.modules.editor.java.JavaKit; import org.netbeans.editor.ext.java.JavaSettingsNames; /** * This static class groups the whole aspect of bracket * completion. It is defined to clearly separate the functionality * and keep actions clean. * The methods of the class are called from different actions as * KeyTyped, DeletePreviousChar. */ class BracketCompletion { /** * A hook method called after a character was inserted into the * document. The function checks for special characters for * completion ()[]'"{} and other conditions and optionally performs * changes to the doc and or caret (complets braces, moves caret, * etc.) * @param doc the document where the change occurred * @param dotPos position of the character insertion * @param caret caret * @param ch the character that was inserted * @throws BadLocationException if dotPos is not correct */ static void charInserted(BaseDocument doc, int dotPos, Caret caret, char ch) throws BadLocationException { if (doc.getSyntaxSupport() instanceof ExtSyntaxSupport && completionSettingEnabled()) { if (ch == ')'|| ch == ']'|| ch =='('|| ch =='[') { TokenID tokenAtDot = ((ExtSyntaxSupport)doc.getSyntaxSupport()).getTokenID(dotPos); if (tokenAtDot == JavaTokenContext.RBRACKET || tokenAtDot == JavaTokenContext.RPAREN) { skipClosingBracket(doc, dotPos, caret, ch); } else if (tokenAtDot == JavaTokenContext.LBRACKET || tokenAtDot == JavaTokenContext.LPAREN) { completeOpeningBracket(doc, dotPos, caret, ch); } } else if (ch == '\"' || ch == '\'') { completeQuote(doc, dotPos, caret, ch); } else if (ch == ';') { moveSemicolon(doc, dotPos, caret); } } } private static void moveSemicolon(BaseDocument doc, int dotPos, Caret caret) throws BadLocationException { int eolPos = Utilities.getRowEnd(doc, dotPos); ExtSyntaxSupport ssup = (ExtSyntaxSupport)doc.getSyntaxSupport(); int lastParenPos = dotPos; TokenItem token = ssup.getTokenChain(dotPos, eolPos); for (TokenItem item = token.getNext(); item != null && item.getOffset() <= eolPos; item = item.getNext()) { TokenID tokenID = item.getTokenID(); if (tokenID == JavaTokenContext.RPAREN) { lastParenPos = item.getOffset(); } else if (tokenID != JavaTokenContext.WHITESPACE) { return; } } if (isForLoopSemicolon(token)) { return; } doc.remove(dotPos, 1); doc.insertString(lastParenPos, ";", null); // NOI18N caret.setDot(lastParenPos + 1); } private static boolean isForLoopSemicolon(TokenItem token) { if (token == null || token.getTokenID() != JavaTokenContext.SEMICOLON) { return false; } int parDepth = 0; // parenthesis depth int braceDepth = 0; // brace depth boolean semicolonFound = false; // next semicolon token = token.getPrevious(); // ignore this semicolon while (token != null) { if (token.getTokenID() == JavaTokenContext.LPAREN) { if (parDepth == 0) { // could be a 'for (' token = token.getPrevious(); while(token !=null && (token.getTokenID() == JavaTokenContext.WHITESPACE || token.getTokenID() == JavaTokenContext.BLOCK_COMMENT || token.getTokenID() == JavaTokenContext.LINE_COMMENT)) { token = token.getPrevious(); } if (token.getTokenID() == JavaTokenContext.FOR) { return true; } return false; } else { // non-zero depth parDepth--; } } else if (token.getTokenID() == JavaTokenContext.RPAREN) { parDepth++; } else if (token.getTokenID() == JavaTokenContext.LBRACE) { if (braceDepth == 0) { // unclosed left brace return false; } braceDepth--; } else if (token.getTokenID() == JavaTokenContext.RBRACE) { braceDepth++; } else if (token.getTokenID() == JavaTokenContext.SEMICOLON) { if (semicolonFound) { // one semicolon already found return false; } semicolonFound = true; } token = token.getPrevious(); } return false; } /** * Hook called after a character *ch* was backspace-deleted from * *doc*. The function possibly removes bracket or quote pair if * appropriate. * @param doc the document * @param dotPos position of the change * @param caret caret * @param ch the character that was deleted */ static void charBackspaced(BaseDocument doc, int dotPos, Caret caret, char ch) throws BadLocationException { if (completionSettingEnabled()) { if (ch == '(' || ch == '[') { TokenID tokenAtDot = ((ExtSyntaxSupport)doc. getSyntaxSupport()).getTokenID(dotPos); if ((tokenAtDot == JavaTokenContext.RBRACKET && tokenBalance(doc,0, doc.getLength(), JavaTokenContext.LBRACKET, JavaTokenContext.RBRACKET) != 0) || (tokenAtDot == JavaTokenContext.RPAREN && tokenBalance(doc,0, doc.getLength(), JavaTokenContext.LPAREN, JavaTokenContext.RPAREN) != 0) ) { doc.remove(dotPos, 1); } } else if (ch == '\"' || ch == '\'') { char match [] = doc.getChars(dotPos, 1); if (match != null && (match[0] == '\"' || match[0] == '\'')) { doc.remove(dotPos, 1); } } } } /** * A function to complete opening curly bracket. Various conditions * are checked and the pairing curly is inserted if appropriate. * @param doc the document * @param dotPos position of the opening { * @param caret */ static void completeOpeningCurly(BaseDocument doc, int dotPos, Caret caret) throws BadLocationException { if (completionSettingEnabled()) { TokenID tokenAtDot = ((ExtSyntaxSupport)doc.getSyntaxSupport()).getTokenID(dotPos); if (tokenAtDot == JavaTokenContext.LBRACE && braceBalance(doc,0, doc.getLength()) > 0) { int newPos = dotPos + 1; doc.insertString(newPos,"}", null); // NOI18N doc.getFormatter().indentNewLine(doc, newPos); caret.setDot(dotPos+1); } } } /** * Counts the number of braces starting at dotPos to the end of the * document. Every occurence of { increses the count by 1, every * occurrence of } decreses the count by 1. The result is returned. * @return The number of { - number of } (>0 more { than } ,<0 more } than {) */ private static int braceBalance(BaseDocument doc, int dotPos, int length) throws BadLocationException { return tokenBalance(doc, dotPos, length, JavaTokenContext.LBRACE, JavaTokenContext.RBRACE); } /** * The same as braceBalance but generalized to any pair of matching * tokens. * @param open the token that increses the count * @param close the token that decreses the count */ private static int tokenBalance(BaseDocument doc, int dotPos, int length, TokenID open, TokenID close) throws BadLocationException { ExtSyntaxSupport ssup = (ExtSyntaxSupport)doc.getSyntaxSupport(); int counter = 0; int endOffset = dotPos + length; for (TokenItem it = ssup.getTokenChain(dotPos, endOffset); it != null && it.getOffset() < endOffset; it = it.getNext()) { if (it.getTokenID() == open) counter ++; else if (it.getTokenID() == close) counter--; } return counter; } /** * A hook to be called after closing bracket ) or ] was inserted to * the document. The method checks if the bracket should stay there * or be removed and some exisitng bracket just skipped. * * @param doc the document * @param dotPos position of the inserted bracket * @param caret caret * @param theBracket the bracket character ']' or ')' */ private static void skipClosingBracket(BaseDocument doc, int dotPos, Caret caret, char theBracket) throws BadLocationException { // try to match every same bracket from dot on boolean nomatch = false; int dp ; for (dp = dotPos; dp <= doc.getLength(); dp++) { char [] chars = doc.getChars(dp, 1); if (chars == null || chars[0] != theBracket) break; } dp--; // dp points to the last position with bracket if (dp > dotPos) { ExtSyntaxSupport sup = (ExtSyntaxSupport)doc.getSyntaxSupport(); // test the last bracket for match int [] block = sup.findMatchingBlock(dp, false); /* Fix of #47879 * If the following case occurs: * SwingUtilities.invokeLater(new Runnable() { * public void run(|) * }) * after typing right bracket ')' the bracket should be consumed * but it would not be because it would match * the '(' right before 'new'. * Therefore there is a search necessary * for unclosed '{' as well in the area of the match block. */ if (block != null) { int leftBracketOffset = block[0]; assert (leftBracketOffset < dp); int balance = braceBalance(doc, leftBracketOffset, dp - leftBracketOffset); if (balance != 0) { block = null; // make it think that the inserted bracket breakes source consistency } } if (block == null) { // doesn't match with the bracket // did it match before? block = sup.findMatchingBlock(dp-1, false); if (block != null) { // it did, the new bracket would violate // source consistency, remove it doc.remove(dotPos, 1); caret.setDot(dotPos+1); } } } } /** * Check for various conditions and possibly add a pairing bracket * to the already inserted. * @param doc the document * @param dotPos position of the opening bracket (already in the doc) * @param caret caret * @param theBracket the bracket that was inserted */ private static void completeOpeningBracket(BaseDocument doc, int dotPos, Caret caret, char theBracket) throws BadLocationException { if (isCompletablePosition(doc, dotPos+1)) { String matchinBracket = "" + matching(theBracket); doc.insertString(dotPos + 1, matchinBracket,null); caret.setDot(dotPos+1); } } /** * Check for conditions and possibly complete an already inserted * quote . * @param doc the document * @param dotPos position of the opening bracket (already in the doc) * @param caret caret * @param theBracket the character that was inserted */ private static void completeQuote(BaseDocument doc, int dotPos, Caret caret, char theBracket) throws BadLocationException { if (posWithinQuotes(doc, dotPos+1, theBracket, (theBracket =='\"' ? JavaTokenContext.STRING_LITERAL : JavaTokenContext.CHAR_LITERAL)) && isCompletablePosition(doc, dotPos+1)) { doc.insertString(dotPos + 1, "" + theBracket ,null); caret.setDot(dotPos+1); } else { char [] charss = doc.getChars(dotPos+1, 1); // System.out.println("NOT Within string, " + new String(charss)); if (charss != null && charss[0] == theBracket) { doc.remove(dotPos+1, 1); } } } /** * Checks whether dotPos is a position at which bracket and quote * completion is performed. Brackets and quotes are not completed * everywhere but just at suitable places . * @param doc the document * @param dotPos position to be tested */ private static boolean isCompletablePosition(BaseDocument doc, int dotPos) throws BadLocationException { if (dotPos == doc.getLength()) // there's no other character to test return true; else { // test that we are in front of ) , " or ' char chr = doc.getChars(dotPos,1)[0]; return (chr == ')' || chr == ',' || chr == '\"'|| chr == '\''|| chr == ' ' || chr == ']' || chr == '}' || chr == '\n'|| chr == '\t'|| chr == ';'); } } /** * Returns true if bracket completion is enabled in options. */ private static boolean completionSettingEnabled() { return ((Boolean)Settings.getValue(JavaKit.class, JavaSettingsNames.PAIR_CHARACTERS_COMPLETION)).booleanValue(); } /** * Returns for an opening bracket or quote the appropriate closing * character. */ private static char matching(char theBracket) { switch (theBracket) { case '(' : return ')'; case '[' : return ']'; case '\"' : return '\"'; // NOI18N case '\'' : return '\''; default: return ' '; } } /** * posWithinString(doc, pos) iff position *pos* is within a string * literal in document doc. * @param doc the document * @param dotPos position to be tested */ static boolean posWithinString(BaseDocument doc, int dotPos) { return posWithinQuotes(doc, dotPos, '\"', JavaTokenContext.STRING_LITERAL); } /** * Generalized posWithingString to any token and delimiting * character. It works for tokens are delimited by *quote* and * extend up to the other *quote* or whitespace in case of an * incomplete token. * @param doc the document * @param dotPos position to be tested */ static boolean posWithinQuotes(BaseDocument doc, int dotPos, char quote, TokenID tokenID) { try { MyTokenProcessor proc = new MyTokenProcessor(); doc.getSyntaxSupport().tokenizeText( proc, dotPos-1, doc.getLength(), true); return proc.tokenID == tokenID && (dotPos - proc.tokenStart == 1 || doc.getChars(dotPos-1,1)[0]!=quote); } catch (BadLocationException ex) { return false; } } /** * A token processor used to find out the length of a token. */ static class MyTokenProcessor implements TokenProcessor { public TokenID tokenID = null; public int tokenStart = -1; public boolean token(TokenID tokenID, TokenContextPath tcp, int tokBuffOffset, int tokLength) { this.tokenStart = tokenBuffer2DocumentOffset(tokBuffOffset); this.tokenID = tokenID; // System.out.println("token " + tokenID.getName() + " at " + tokenStart + " (" + // tokBuffOffset + ") len:" + tokLength); return false; } public int eot(int offset) { // System.out.println("EOT"); return 0;} public void nextBuffer(char [] buffer, int offset, int len, int startPos, int preScan, boolean lastBuffer) { // System.out.println("nextBuffer "+ new String(buffer) + "," + offset + "len: " + len + " startPos:"+startPos + " preScan:" + preScan + " lastBuffer:" + lastBuffer); this.bufferStartPos = startPos - offset; } private int bufferStartPos = 0; private int tokenBuffer2DocumentOffset(int offs) { return offs + bufferStartPos;} } } |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.