diff --git a/.idea/libraries/idea_full.xml b/.idea/libraries/idea_full.xml index f929536a7b5..aefebb35272 100644 --- a/.idea/libraries/idea_full.xml +++ b/.idea/libraries/idea_full.xml @@ -1,5 +1,8 @@ + + + diff --git a/annotations/com/intellij/codeInsight/completion/annotations.xml b/annotations/com/intellij/codeInsight/completion/annotations.xml new file mode 100644 index 00000000000..7a6997823c8 --- /dev/null +++ b/annotations/com/intellij/codeInsight/completion/annotations.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/annotations/com/intellij/openapi/editor/annotations.xml b/annotations/com/intellij/openapi/editor/annotations.xml new file mode 100644 index 00000000000..1eb0a5a8784 --- /dev/null +++ b/annotations/com/intellij/openapi/editor/annotations.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/completion/DescriptorLookupConverter.java b/idea/src/org/jetbrains/jet/plugin/completion/DescriptorLookupConverter.java index 69a09bc940f..5c051a6dd4f 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/DescriptorLookupConverter.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/DescriptorLookupConverter.java @@ -37,24 +37,16 @@ import org.jetbrains.jet.lang.resolve.lazy.KotlinCodeAnalyzer; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import org.jetbrains.jet.plugin.JetDescriptorIconProvider; -import org.jetbrains.jet.plugin.completion.handlers.JetClassInsertHandler; -import org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler; -import org.jetbrains.jet.plugin.completion.handlers.JetJavaClassInsertHandler; +import org.jetbrains.jet.plugin.completion.handlers.*; import org.jetbrains.jet.renderer.DescriptorRenderer; import java.util.List; +import static org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler.EMPTY_FUNCTION_HANDLER; +import static org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler.PARAMS_BRACES_FUNCTION_HANDLER; +import static org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler.PARAMS_PARENTHESIS_FUNCTION_HANDLER; + public final class DescriptorLookupConverter { - - private final static JetFunctionInsertHandler EMPTY_FUNCTION_HANDLER = new JetFunctionInsertHandler( - JetFunctionInsertHandler.CaretPosition.AFTER_BRACKETS, JetFunctionInsertHandler.BracketType.PARENTHESIS); - - private final static JetFunctionInsertHandler PARAMS_PARENTHESIS_FUNCTION_HANDLER = new JetFunctionInsertHandler( - JetFunctionInsertHandler.CaretPosition.IN_BRACKETS, JetFunctionInsertHandler.BracketType.PARENTHESIS); - - private final static JetFunctionInsertHandler PARAMS_BRACES_FUNCTION_HANDLER = new JetFunctionInsertHandler( - JetFunctionInsertHandler.CaretPosition.IN_BRACKETS, JetFunctionInsertHandler.BracketType.BRACES); - private DescriptorLookupConverter() {} @NotNull @@ -118,9 +110,8 @@ public final class DescriptorLookupConverter { return EMPTY_FUNCTION_HANDLER; } - if (functionDescriptor.getValueParameters().size() == 1 - && KotlinBuiltIns.getInstance().isFunctionOrExtensionFunctionType( - functionDescriptor.getValueParameters().get(0).getType())) { + if (functionDescriptor.getValueParameters().size() == 1 && + KotlinBuiltIns.getInstance().isFunctionOrExtensionFunctionType(functionDescriptor.getValueParameters().get(0).getType())) { return PARAMS_BRACES_FUNCTION_HANDLER; } diff --git a/idea/src/org/jetbrains/jet/plugin/completion/JetKeywordCompletionContributor.java b/idea/src/org/jetbrains/jet/plugin/completion/JetKeywordCompletionContributor.java index 5cc6cf04248..1ccb17b3c78 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/JetKeywordCompletionContributor.java +++ b/idea/src/org/jetbrains/jet/plugin/completion/JetKeywordCompletionContributor.java @@ -40,7 +40,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lexer.JetToken; import org.jetbrains.jet.lexer.JetTokens; -import org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler; import org.jetbrains.jet.plugin.completion.handlers.JetKeywordInsertHandler; import org.jetbrains.jet.plugin.completion.handlers.JetTemplateInsertHandler; @@ -49,6 +48,7 @@ import java.util.Collection; import java.util.List; import static org.jetbrains.jet.lexer.JetTokens.*; +import static org.jetbrains.jet.plugin.completion.handlers.JetFunctionInsertHandler.EMPTY_FUNCTION_HANDLER; /** * A keyword contributor for Kotlin @@ -56,8 +56,6 @@ import static org.jetbrains.jet.lexer.JetTokens.*; public class JetKeywordCompletionContributor extends CompletionContributor { private final static InsertHandler KEYWORDS_INSERT_HANDLER = new JetKeywordInsertHandler(); - private final static InsertHandler FUNCTION_INSERT_HANDLER = new JetFunctionInsertHandler( - JetFunctionInsertHandler.CaretPosition.AFTER_BRACKETS, JetFunctionInsertHandler.BracketType.PARENTHESIS); private final static ElementFilter GENERAL_FILTER = new NotFilter(new OrFilter( new CommentFilter(), // or @@ -250,7 +248,7 @@ public class JetKeywordCompletionContributor extends CompletionContributor { return lookupElementBuilder.withInsertHandler(KEYWORDS_INSERT_HANDLER); } - return lookupElementBuilder.withInsertHandler(FUNCTION_INSERT_HANDLER); + return lookupElementBuilder.withInsertHandler(EMPTY_FUNCTION_HANDLER); } }); } diff --git a/idea/src/org/jetbrains/jet/plugin/completion/handlers/JetFunctionInsertHandler.java b/idea/src/org/jetbrains/jet/plugin/completion/handlers/JetFunctionInsertHandler.java deleted file mode 100644 index 66ecb3eb409..00000000000 --- a/idea/src/org/jetbrains/jet/plugin/completion/handlers/JetFunctionInsertHandler.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright 2010-2013 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 org.jetbrains.jet.plugin.completion.handlers; - -import com.intellij.codeInsight.AutoPopupController; -import com.intellij.codeInsight.completion.InsertHandler; -import com.intellij.codeInsight.completion.InsertionContext; -import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.CodeStyleSettingsManager; -import com.intellij.psi.util.PsiTreeUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; -import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor; -import org.jetbrains.jet.lang.psi.JetFile; -import org.jetbrains.jet.lang.psi.JetImportDirective; -import org.jetbrains.jet.lang.psi.JetQualifiedExpression; -import org.jetbrains.jet.lang.resolve.DescriptorUtils; -import org.jetbrains.jet.lang.resolve.name.FqName; -import org.jetbrains.jet.plugin.completion.JetLookupObject; -import org.jetbrains.jet.plugin.formatter.JetCodeStyleSettings; -import org.jetbrains.jet.plugin.quickfix.ImportInsertHelper; - -/** - * Performs number of code modification after insertion jet function: - * - Insert () - * - Add import directive if necessary for top level functions - */ -public class JetFunctionInsertHandler implements InsertHandler { - - public enum CaretPosition { IN_BRACKETS, AFTER_BRACKETS } - public enum BracketType { PARENTHESIS, BRACES } - - private final CaretPosition caretPosition; - private final BracketType bracketType; - - public JetFunctionInsertHandler(CaretPosition caretPosition, BracketType bracketType) { - if (caretPosition == CaretPosition.AFTER_BRACKETS && bracketType == BracketType.BRACES) { - throw new IllegalArgumentException("CaretPosition.AFTER_BRACKETS with bracketType == BracketType.BRACES combination is not supported"); - } - - this.caretPosition = caretPosition; - this.bracketType = bracketType; - } - - @Override - public void handleInsert(InsertionContext context, LookupElement item) { - PsiDocumentManager.getInstance(context.getProject()).commitAllDocuments(); - - if (context.getCompletionChar() == '(') { - context.setAddCompletionChar(false); - } - - int startOffset = context.getStartOffset(); - PsiElement element = context.getFile().findElementAt(startOffset); - if (element == null) { - return; - } - - if (shouldAddBrackets(element)) { - addBrackets(context, element); - } - - addImport(context, item); - } - - private static boolean shouldAddBrackets(PsiElement element) { - return PsiTreeUtil.getParentOfType(element, JetImportDirective.class) == null; - } - - private void addBrackets(InsertionContext context, PsiElement offsetElement) { - int offset = context.getSelectionEndOffset(); - Document document = context.getDocument(); - - String documentText = document.getText(); - - char completionChar = context.getCompletionChar(); - - boolean braces = bracketType == BracketType.BRACES && completionChar != '('; - char openingBracket = braces ? '{' : '('; - char closingBracket = braces ? '}' : ')'; - - int openingBracketIndex = indexOfSkippingSpace(documentText, openingBracket, offset); - int inBracketsShift = 0; - - if (openingBracketIndex == -1) { - // Insert ()/{} if it's not already exist - if (braces) { - if (completionChar == ' ') { - context.setAddCompletionChar(false); - } - - if (isInsertSpacesInOneLineFunctionEnabled(offsetElement.getProject())) { - document.insertString(offset, " { }"); - inBracketsShift = 1; - } - else { - document.insertString(offset, " {}"); - } - } - else { - document.insertString(offset, "()"); - } - - PsiDocumentManager.getInstance(context.getProject()).commitDocument(document); - documentText = document.getText(); - } - - openingBracketIndex = indexOfSkippingSpace(documentText, openingBracket, offset); - assert openingBracketIndex != -1 : "If there wasn't open bracket it should already have been inserted"; - - // CaretPosition.AFTER_BRACKETS mode cannot work when there are some non-empty chars between open and close bracket - int closeBracketIndex = indexOfSkippingSpace(documentText, closingBracket, openingBracketIndex + 1); - - Editor editor = context.getEditor(); - - // Satisfy TypedHandler.handleRParen() algorithm for preventing doubling ')' char if user typed "()" manually. - boolean forcePlaceCaretIntoParentheses = completionChar == '('; - - if (caretPosition == CaretPosition.IN_BRACKETS || forcePlaceCaretIntoParentheses || closeBracketIndex == -1) { - editor.getCaretModel().moveToOffset(openingBracketIndex + 1 + inBracketsShift); - AutoPopupController.getInstance(context.getProject()).autoPopupParameterInfo(editor, offsetElement); - } - else { - editor.getCaretModel().moveToOffset(closeBracketIndex + 1); - } - - PsiDocumentManager.getInstance(context.getProject()).commitDocument(context.getDocument()); - } - - private static int indexOfSkippingSpace(String str, char ch, int startIndex) { - for (int i = startIndex; i < str.length(); i++) { - char currentChar = str.charAt(i); - if (ch == currentChar) { - return i; - } - - if (!Character.isWhitespace(currentChar)) { - return -1; - } - } - - return -1; - } - - private static boolean isInsertSpacesInOneLineFunctionEnabled(Project project) { - CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(project); - JetCodeStyleSettings jetSettings = settings.getCustomSettings(JetCodeStyleSettings.class); - - return jetSettings.INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD; - } - - private static void addImport(final InsertionContext context, final @NotNull LookupElement item) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - int startOffset = context.getStartOffset(); - PsiElement element = context.getFile().findElementAt(startOffset); - if (element == null) { - return; - } - - if (context.getFile() instanceof JetFile && item.getObject() instanceof JetLookupObject) { - DeclarationDescriptor descriptor = ((JetLookupObject) item.getObject()).getDescriptor(); - if (descriptor instanceof SimpleFunctionDescriptor) { - - final JetFile file = (JetFile) context.getFile(); - final SimpleFunctionDescriptor functionDescriptor = (SimpleFunctionDescriptor) descriptor; - // Don't insert import for qualified expression if don't try to insert extension function - if (PsiTreeUtil.getParentOfType(element, JetQualifiedExpression.class) != null && - functionDescriptor.getReceiverParameter() == null) { - - return; - } - - if (DescriptorUtils.isTopLevelDeclaration(functionDescriptor)) { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - FqName fqn = DescriptorUtils.getFQName(functionDescriptor).toSafe(); - ImportInsertHelper.addImportDirectiveIfNeeded(fqn, file); - } - }); - } - } - } - } - }); - } -} diff --git a/idea/src/org/jetbrains/jet/plugin/completion/handlers/JetFunctionInsertHandler.kt b/idea/src/org/jetbrains/jet/plugin/completion/handlers/JetFunctionInsertHandler.kt new file mode 100644 index 00000000000..33e640034a3 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/completion/handlers/JetFunctionInsertHandler.kt @@ -0,0 +1,188 @@ +/* + * Copyright 2010-2013 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 org.jetbrains.jet.plugin.completion.handlers + +import com.intellij.codeInsight.completion.InsertHandler +import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.codeInsight.completion.InsertionContext +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiElement +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.jet.lang.psi.JetImportDirective +import com.intellij.codeInsight.AutoPopupController +import com.intellij.psi.codeStyle.CodeStyleSettingsManager +import com.intellij.openapi.project.Project +import org.jetbrains.jet.plugin.formatter.JetCodeStyleSettings +import com.intellij.openapi.application.ApplicationManager +import org.jetbrains.jet.lang.psi.JetFile +import org.jetbrains.jet.plugin.completion.JetLookupObject +import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor +import org.jetbrains.jet.lang.psi.JetQualifiedExpression +import org.jetbrains.jet.lang.resolve.DescriptorUtils +import org.jetbrains.jet.lang.resolve.name.FqName +import org.jetbrains.jet.plugin.quickfix.ImportInsertHelper + +public enum class CaretPosition { + IN_BRACKETS + AFTER_BRACKETS +} +public enum class BracketType { + PARENTHESIS + BRACES +} + +public class JetFunctionInsertHandler(val caretPosition : CaretPosition, val bracketType : BracketType) : InsertHandler { + { + if (caretPosition == CaretPosition.AFTER_BRACKETS && bracketType == BracketType.BRACES) { + throw IllegalArgumentException("CaretPosition.AFTER_BRACKETS with bracketType == BracketType.BRACES combination is not supported") + } + } + + public override fun handleInsert(context: InsertionContext?, item: LookupElement?) { + if (context == null || item == null) return + + PsiDocumentManager.getInstance(context.getProject())?.commitAllDocuments() + if ((context.getCompletionChar()) == '(') { + context.setAddCompletionChar(false) + } + + val startOffset = context.getStartOffset() + val element = context.getFile().findElementAt(startOffset) + + if (element == null) return + + if (shouldAddBrackets(element)) { + addBrackets(context, element) + } + + addImport(context, item) + } + + private fun addBrackets(context : InsertionContext, offsetElement : PsiElement) { + val offset = context.getSelectionEndOffset() + val document = context.getDocument() + val completionChar = context.getCompletionChar() + + val braces = bracketType == BracketType.BRACES && completionChar != '(' + + val openingBracket = if (braces) '{' else '(' + val closingBracket = if (braces) '}' else ')' + + var documentText = document.getText() + var openingBracketIndex = indexOfSkippingSpace(documentText, openingBracket, offset) + var inBracketsShift = 0 + if (openingBracketIndex == -1) { + if (braces) { + if (completionChar == ' ') { + context.setAddCompletionChar(false) + } + + if (isInsertSpacesInOneLineFunctionEnabled(offsetElement.getProject())) { + document.insertString(offset, " { }") + inBracketsShift = 1 + } + else { + document.insertString(offset, " {}") + } + } + else { + document.insertString(offset, "()") + } + PsiDocumentManager.getInstance(context.getProject())?.commitDocument(document) + documentText = document.getText() + } + + openingBracketIndex = indexOfSkippingSpace(documentText, openingBracket, offset) + assert (openingBracketIndex != -1) { "If there wasn't open bracket it should already have been inserted" } + + val closeBracketIndex = indexOfSkippingSpace(documentText, closingBracket, openingBracketIndex + 1) + val editor = context.getEditor() + + var forcePlaceCaretIntoParentheses : Boolean = completionChar == '(' + + if (caretPosition == CaretPosition.IN_BRACKETS || forcePlaceCaretIntoParentheses || closeBracketIndex == -1) { + editor.getCaretModel().moveToOffset(openingBracketIndex + 1 + inBracketsShift) + AutoPopupController.getInstance(context.getProject())?.autoPopupParameterInfo(editor, offsetElement) + } + else { + editor.getCaretModel().moveToOffset(closeBracketIndex + 1) + } + + PsiDocumentManager.getInstance(context.getProject())?.commitDocument(context.getDocument()) + } + + class object { + public val EMPTY_FUNCTION_HANDLER: JetFunctionInsertHandler = JetFunctionInsertHandler(CaretPosition.AFTER_BRACKETS, BracketType.PARENTHESIS) + public val PARAMS_PARENTHESIS_FUNCTION_HANDLER: JetFunctionInsertHandler = JetFunctionInsertHandler(CaretPosition.IN_BRACKETS, BracketType.PARENTHESIS) + public val PARAMS_BRACES_FUNCTION_HANDLER: JetFunctionInsertHandler = JetFunctionInsertHandler(CaretPosition.IN_BRACKETS, BracketType.BRACES) + + private fun shouldAddBrackets(element : PsiElement) : Boolean { + return PsiTreeUtil.getParentOfType(element, javaClass()) == null + } + + private fun indexOfSkippingSpace(str : String, ch : Char, startIndex : Int) : Int { + for (i in startIndex..str.length() - 1) { + val currentChar = str.charAt(i) + if (ch == currentChar) { + return i + } + + if (!Character.isWhitespace(currentChar)) { + return -1 + } + } + + return -1 + } + + private open fun isInsertSpacesInOneLineFunctionEnabled(project : Project) : Boolean { + val settings = CodeStyleSettingsManager.getSettings(project) + val jetSettings = settings.getCustomSettings(javaClass())!! + return jetSettings.INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD + } + + private open fun addImport(context : InsertionContext, item : LookupElement) { + ApplicationManager.getApplication()?.runReadAction { () : Unit -> + val startOffset = context.getStartOffset() + val element = context.getFile().findElementAt(startOffset) + + if (element == null) return@runReadAction + + if (context.getFile() is JetFile && item.getObject() is JetLookupObject) { + val descriptor = (item.getObject() as JetLookupObject).getDescriptor() + + if (descriptor is SimpleFunctionDescriptor) { + val file = context.getFile() as JetFile + val functionDescriptor = descriptor as SimpleFunctionDescriptor + + if (PsiTreeUtil.getParentOfType(element, javaClass()) != null && + functionDescriptor.getReceiverParameter() == null) { + return@runReadAction + } + + if (DescriptorUtils.isTopLevelDeclaration(functionDescriptor)) { + ApplicationManager.getApplication()?.runWriteAction { + val fqn = DescriptorUtils.getFQName(functionDescriptor).toSafe() + ImportInsertHelper.addImportDirectiveIfNeeded(fqn, file) + } + } + } + } + } + } + } +} \ No newline at end of file