| /* |
| * Copyright 2000-2009 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.codeInsight.completion.simple; |
| |
| import com.intellij.codeInsight.TailType; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.editor.Document; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.editor.ex.EditorEx; |
| import com.intellij.openapi.editor.highlighter.EditorHighlighter; |
| import com.intellij.openapi.editor.highlighter.HighlighterIterator; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.psi.*; |
| import com.intellij.psi.codeStyle.CommonCodeStyleSettings; |
| import com.intellij.psi.impl.source.tree.ElementType; |
| import com.intellij.psi.tree.IElementType; |
| import com.intellij.psi.tree.java.IJavaElementType; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import org.jetbrains.annotations.NonNls; |
| |
| /** |
| * @author peter |
| */ |
| public abstract class RParenthTailType extends TailType { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.simple.RParenthSimpleTailType"); |
| |
| private static TextRange getRangeToCheckParensBalance(PsiFile file, final Document document, int startOffset){ |
| PsiElement element = file.findElementAt(startOffset); |
| element = PsiTreeUtil.getParentOfType(element, PsiStatement.class, false); |
| if (element != null) { |
| final PsiElement parent = element.getParent(); |
| if (parent instanceof PsiLoopStatement) { |
| element = parent; |
| } |
| } |
| return element == null ? new TextRange(0, document.getTextLength()) : element.getTextRange(); |
| } |
| |
| protected abstract boolean isSpaceWithinParentheses(CommonCodeStyleSettings styleSettings, Editor editor, final int tailOffset); |
| |
| @Override |
| public int processTail(final Editor editor, int tailOffset) { |
| return addRParenth(editor, tailOffset, isSpaceWithinParentheses(getLocalCodeStyleSettings(editor, tailOffset), editor, tailOffset)); |
| } |
| |
| public static int addRParenth(Editor editor, int offset, boolean spaceWithinParens) { |
| int existingRParenthOffset = getExistingRParenthOffset(editor, offset); |
| |
| if (existingRParenthOffset < 0){ |
| if (spaceWithinParens){ |
| offset = insertChar(editor, offset, ' '); |
| } |
| editor.getDocument().insertString(offset, ")"); |
| return moveCaret(editor, offset, 1); |
| } |
| if (spaceWithinParens && offset == existingRParenthOffset) { |
| existingRParenthOffset = insertChar(editor, offset, ' '); |
| } |
| return moveCaret(editor, existingRParenthOffset, 1); |
| } |
| |
| @NonNls |
| public String toString() { |
| return "RParenth"; |
| } |
| |
| private static int getExistingRParenthOffset(final Editor editor, final int tailOffset) { |
| final Document document = editor.getDocument(); |
| if (tailOffset >= document.getTextLength()) return -1; |
| |
| final CharSequence charsSequence = document.getCharsSequence(); |
| EditorHighlighter highlighter = ((EditorEx) editor).getHighlighter(); |
| |
| int existingRParenthOffset = -1; |
| for(HighlighterIterator iterator = highlighter.createIterator(tailOffset); !iterator.atEnd(); iterator.advance()){ |
| final IElementType tokenType = iterator.getTokenType(); |
| |
| if ((!(tokenType instanceof IJavaElementType) || !ElementType.JAVA_COMMENT_OR_WHITESPACE_BIT_SET.contains(tokenType)) && |
| tokenType != TokenType.WHITE_SPACE) { |
| final int start = iterator.getStart(); |
| if (iterator.getEnd() == start + 1 && ')' == charsSequence.charAt(start)) { |
| existingRParenthOffset = start; |
| } |
| break; |
| } |
| } |
| |
| if (existingRParenthOffset >= 0){ |
| final PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(editor.getProject()); |
| psiDocumentManager.commitDocument(document); |
| TextRange range = getRangeToCheckParensBalance(psiDocumentManager.getPsiFile(document), document, editor.getCaretModel().getOffset()); |
| int balance = calcParensBalance(document, highlighter, range.getStartOffset(), range.getEndOffset()); |
| if (balance > 0){ |
| return -1; |
| } |
| } |
| return existingRParenthOffset; |
| } |
| |
| private static int calcParensBalance(Document document, EditorHighlighter highlighter, int rangeStart, int rangeEnd){ |
| LOG.assertTrue(0 <= rangeStart); |
| LOG.assertTrue(rangeStart <= rangeEnd); |
| LOG.assertTrue(rangeEnd <= document.getTextLength()); |
| |
| HighlighterIterator iterator = highlighter.createIterator(rangeStart); |
| int balance = 0; |
| while(!iterator.atEnd() && iterator.getStart() < rangeEnd){ |
| IElementType tokenType = iterator.getTokenType(); |
| if (tokenType == JavaTokenType.LPARENTH){ |
| balance++; |
| } |
| else if (tokenType == JavaTokenType.RPARENTH){ |
| balance--; |
| } |
| iterator.advance(); |
| } |
| return balance; |
| } |
| |
| } |