/* * Copyright 2013-2017 consulo.io * * 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 consulo.csharp.ide.completion.util; import org.jetbrains.annotations.Nullable; import com.intellij.codeInsight.completion.InsertHandler; import com.intellij.codeInsight.completion.InsertionContext; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiWhiteSpace; /** * @author peter * @see com.intellij.codeInsight.completion.util.ParenthesesInsertHandler */ public abstract class LtGtInsertHandler<T extends LookupElement> implements InsertHandler<T> { public static final LtGtInsertHandler<LookupElement> WITH_PARAMETERS = new LtGtInsertHandler<LookupElement>() { @Override protected boolean placeCaretInsideParentheses(final InsertionContext context, final LookupElement item) { return true; } }; public static final LtGtInsertHandler<LookupElement> NO_PARAMETERS = new LtGtInsertHandler<LookupElement>() { @Override protected boolean placeCaretInsideParentheses(final InsertionContext context, final LookupElement item) { return false; } }; public static LtGtInsertHandler<LookupElement> getInstance(boolean hasParameters) { return hasParameters ? WITH_PARAMETERS : NO_PARAMETERS; } public static LtGtInsertHandler<LookupElement> getInstance(final boolean hasParameters, final boolean spaceBeforeParentheses, final boolean spaceBetweenParentheses, final boolean insertRightParenthesis, boolean allowParametersOnNextLine) { return new LtGtInsertHandler<LookupElement>(spaceBeforeParentheses, spaceBetweenParentheses, insertRightParenthesis, allowParametersOnNextLine) { @Override protected boolean placeCaretInsideParentheses(InsertionContext context, LookupElement item) { return hasParameters; } }; } private final boolean mySpaceBeforeParentheses; private final boolean mySpaceBetweenParentheses; private final boolean myMayInsertRightParenthesis; private final boolean myAllowParametersOnNextLine; protected LtGtInsertHandler(final boolean spaceBeforeParentheses, final boolean spaceBetweenParentheses, final boolean mayInsertRightParenthesis) { this(spaceBeforeParentheses, spaceBetweenParentheses, mayInsertRightParenthesis, false); } protected LtGtInsertHandler(boolean spaceBeforeParentheses, boolean spaceBetweenParentheses, boolean mayInsertRightParenthesis, boolean allowParametersOnNextLine) { mySpaceBeforeParentheses = spaceBeforeParentheses; mySpaceBetweenParentheses = spaceBetweenParentheses; myMayInsertRightParenthesis = mayInsertRightParenthesis; myAllowParametersOnNextLine = allowParametersOnNextLine; } protected LtGtInsertHandler() { this(false, false, true); } private static boolean isToken(@Nullable final PsiElement element, final String text) { return element != null && text.equals(element.getText()); } protected abstract boolean placeCaretInsideParentheses(final InsertionContext context, final T item); @Override public void handleInsert(final InsertionContext context, final T item) { final Editor editor = context.getEditor(); final Document document = editor.getDocument(); context.commitDocument(); PsiElement element = findNextToken(context); final char completionChar = context.getCompletionChar(); final boolean putCaretInside = completionChar == '<' || placeCaretInsideParentheses(context, item); if(completionChar == '<') { context.setAddCompletionChar(false); } if(isToken(element, "<")) { int lparenthOffset = element.getTextRange().getStartOffset(); if(mySpaceBeforeParentheses && lparenthOffset == context.getTailOffset()) { document.insertString(context.getTailOffset(), " "); lparenthOffset++; } if(completionChar == '<' || completionChar == '\t') { editor.getCaretModel().moveToOffset(lparenthOffset + 1); } else { editor.getCaretModel().moveToOffset(context.getTailOffset()); } context.setTailOffset(lparenthOffset + 1); PsiElement list = element.getParent(); PsiElement last = list.getLastChild(); if(isToken(last, ">")) { int rparenthOffset = last.getTextRange().getStartOffset(); context.setTailOffset(rparenthOffset + 1); if(!putCaretInside) { for(int i = lparenthOffset + 1; i < rparenthOffset; i++) { if(!Character.isWhitespace(document.getCharsSequence().charAt(i))) { return; } } editor.getCaretModel().moveToOffset(context.getTailOffset()); } else if(mySpaceBetweenParentheses && document.getCharsSequence().charAt(lparenthOffset) == ' ') { editor.getCaretModel().moveToOffset(lparenthOffset + 2); } else { editor.getCaretModel().moveToOffset(lparenthOffset + 1); } return; } } else { document.insertString(context.getTailOffset(), getSpace(mySpaceBeforeParentheses) + "<" + getSpace(mySpaceBetweenParentheses)); editor.getCaretModel().moveToOffset(context.getTailOffset()); } if(!myMayInsertRightParenthesis) { return; } if(context.getCompletionChar() == '<') { //todo use BraceMatchingUtil.isPairedBracesAllowedBeforeTypeInFileType int tail = context.getTailOffset(); if(tail < document.getTextLength() && StringUtil.isJavaIdentifierPart(document.getCharsSequence().charAt(tail))) { return; } } document.insertString(context.getTailOffset(), getSpace(mySpaceBetweenParentheses) + ">"); if(!putCaretInside) { editor.getCaretModel().moveToOffset(context.getTailOffset()); } } private static String getSpace(boolean needSpace) { return needSpace ? " " : ""; } @Nullable protected com.intellij.psi.PsiElement findNextToken(final InsertionContext context) { final PsiFile file = context.getFile(); PsiElement element = file.findElementAt(context.getTailOffset()); if(element instanceof PsiWhiteSpace) { if(!myAllowParametersOnNextLine && element.getText().contains("\n")) { return null; } element = file.findElementAt(element.getTextRange().getEndOffset()); } return element; } }