diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index d467bba19e0..31e618c398d 100644 --- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -74,6 +74,7 @@ import org.jetbrains.kotlin.idea.imports.AbstractOptimizeImportsTest import org.jetbrains.kotlin.idea.intentions.AbstractIntentionTest import org.jetbrains.kotlin.idea.intentions.declarations.AbstractJoinLinesTest import org.jetbrains.kotlin.idea.kdoc.AbstractKDocHighlightingTest +import org.jetbrains.kotlin.idea.kdoc.AbstractKDocTypingTest import org.jetbrains.kotlin.idea.navigation.AbstractGotoSuperTest import org.jetbrains.kotlin.idea.navigation.AbstractKotlinGotoImplementationTest import org.jetbrains.kotlin.idea.navigation.AbstractKotlinGotoTest @@ -628,6 +629,10 @@ fun main(args: Array) { testClass(javaClass()) { model("kdoc/highlighting") } + + testClass() { + model("kdoc/typing") + } } testGroup("idea/tests", "compiler/testData") { diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index e93bd43bb77..b5a41c2f2ae 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -340,6 +340,7 @@ + diff --git a/idea/src/org/jetbrains/kotlin/idea/kdoc/KDocTypedHandler.kt b/idea/src/org/jetbrains/kotlin/idea/kdoc/KDocTypedHandler.kt new file mode 100644 index 00000000000..389190c3cb0 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/kdoc/KDocTypedHandler.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2010-2015 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.kotlin.idea.kdoc + +import com.intellij.codeInsight.CodeInsightSettings +import com.intellij.codeInsight.editorActions.TypedHandlerDelegate +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.editor.EditorModificationUtil +import com.intellij.openapi.fileTypes.FileType +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiFile +import org.jetbrains.kotlin.kdoc.lexer.KDocTokens +import org.jetbrains.kotlin.lexer.JetTokens +import org.jetbrains.kotlin.psi.JetFile + +public class KDocTypedHandler(): TypedHandlerDelegate() { + override fun beforeCharTyped(c: Char, project: Project, editor: Editor, file: PsiFile, fileType: FileType): TypedHandlerDelegate.Result = + if (handleBeforeBracketTyped(c, editor, file)) TypedHandlerDelegate.Result.STOP else TypedHandlerDelegate.Result.CONTINUE + + override fun charTyped(c: Char, project: Project, editor: Editor, file: PsiFile): TypedHandlerDelegate.Result = + if (handleBracketTyped(c, project, editor, file)) TypedHandlerDelegate.Result.STOP else TypedHandlerDelegate.Result.CONTINUE + + private fun handleBeforeBracketTyped(c: Char, editor: Editor, file: PsiFile): Boolean { + if (file !is JetFile) { + return false + } + if (!CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET) { + return false + } + + val offset = editor.getCaretModel().getOffset() + val document = editor.getDocument() + if ((c == ']' || c == ')') && offset < document.getTextLength() && document.getCharsSequence().charAt(offset) == c) { + PsiDocumentManager.getInstance(file.getProject()).commitDocument(document) + val element = file.findElementAt(offset) + // if the bracket is not part of a link, it will be part of KDOC_TEXT, not a separate RBRACKET element + if (c == ']' && + element?.getNode()?.getElementType() == JetTokens.RBRACKET || + (offset > 0 && document.getCharsSequence().charAt(offset - 1) == '[')) { + EditorModificationUtil.moveCaretRelatively(editor, 1) + return true + } + if (c == ')' && element?.getNode()?.getElementType() == KDocTokens.MARKDOWN_INLINE_LINK) { + EditorModificationUtil.moveCaretRelatively(editor, 1) + return true + } + } + return false + } + + private fun handleBracketTyped(c: Char, project: Project, editor: Editor, file: PsiFile): Boolean { + if (file !is JetFile) { + return false + } + if (!CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET) { + return false + } + val offset = editor.getCaretModel().getOffset() + if (offset == 0) { + return false + } + + if (c == '[' || c == '(') { + val document = editor.getDocument() + PsiDocumentManager.getInstance(project).commitDocument(document) + val element = file.findElementAt(offset - 1) + if (element == null || + element.getNode().getElementType() != KDocTokens.TEXT) { + return false + } + if (c == '[') + { + document.insertString(offset, "]") + return true + } + if (c == '(' && offset > 1 && document.getCharsSequence().charAt(offset - 2) == ']') { + document.insertString(offset, ")") + return true + } + } + return false + } +} diff --git a/idea/testData/kdoc/typing/closingBracketNotInLink.kt b/idea/testData/kdoc/typing/closingBracketNotInLink.kt new file mode 100644 index 00000000000..3c384456f12 --- /dev/null +++ b/idea/testData/kdoc/typing/closingBracketNotInLink.kt @@ -0,0 +1,6 @@ +/** + * Just some brackets: ] + */ +fun foo() + +// TYPE: ] \ No newline at end of file diff --git a/idea/testData/kdoc/typing/closingBracketNotInLink.kt.after b/idea/testData/kdoc/typing/closingBracketNotInLink.kt.after new file mode 100644 index 00000000000..43b47363483 --- /dev/null +++ b/idea/testData/kdoc/typing/closingBracketNotInLink.kt.after @@ -0,0 +1,6 @@ +/** + * Just some brackets: ]] + */ +fun foo() + +// TYPE: ] \ No newline at end of file diff --git a/idea/testData/kdoc/typing/closingBracketOvertype.kt b/idea/testData/kdoc/typing/closingBracketOvertype.kt new file mode 100644 index 00000000000..194d10e5711 --- /dev/null +++ b/idea/testData/kdoc/typing/closingBracketOvertype.kt @@ -0,0 +1,6 @@ +/** + * [foo] + */ +fun foo() + +// TYPE: ] \ No newline at end of file diff --git a/idea/testData/kdoc/typing/closingBracketOvertype.kt.after b/idea/testData/kdoc/typing/closingBracketOvertype.kt.after new file mode 100644 index 00000000000..37886a8d5f3 --- /dev/null +++ b/idea/testData/kdoc/typing/closingBracketOvertype.kt.after @@ -0,0 +1,6 @@ +/** + * [foo] + */ +fun foo() + +// TYPE: ] \ No newline at end of file diff --git a/idea/testData/kdoc/typing/closingBracketOvertypeEmpty.kt b/idea/testData/kdoc/typing/closingBracketOvertypeEmpty.kt new file mode 100644 index 00000000000..5aa141cfe1f --- /dev/null +++ b/idea/testData/kdoc/typing/closingBracketOvertypeEmpty.kt @@ -0,0 +1,6 @@ +/** + * [] + */ +fun foo() + +// TYPE: ] \ No newline at end of file diff --git a/idea/testData/kdoc/typing/closingBracketOvertypeEmpty.kt.after b/idea/testData/kdoc/typing/closingBracketOvertypeEmpty.kt.after new file mode 100644 index 00000000000..4c94ef29e40 --- /dev/null +++ b/idea/testData/kdoc/typing/closingBracketOvertypeEmpty.kt.after @@ -0,0 +1,6 @@ +/** + * [] + */ +fun foo() + +// TYPE: ] \ No newline at end of file diff --git a/idea/testData/kdoc/typing/closingBracketRefLinkOvertype.kt b/idea/testData/kdoc/typing/closingBracketRefLinkOvertype.kt new file mode 100644 index 00000000000..ba1925465ae --- /dev/null +++ b/idea/testData/kdoc/typing/closingBracketRefLinkOvertype.kt @@ -0,0 +1,6 @@ +/** + * [foo][bar] + */ +fun foo() + +// TYPE: ] \ No newline at end of file diff --git a/idea/testData/kdoc/typing/closingBracketRefLinkOvertype.kt.after b/idea/testData/kdoc/typing/closingBracketRefLinkOvertype.kt.after new file mode 100644 index 00000000000..1e38d57524c --- /dev/null +++ b/idea/testData/kdoc/typing/closingBracketRefLinkOvertype.kt.after @@ -0,0 +1,6 @@ +/** + * [foo][bar] + */ +fun foo() + +// TYPE: ] \ No newline at end of file diff --git a/idea/testData/kdoc/typing/closingParenOvertype.kt b/idea/testData/kdoc/typing/closingParenOvertype.kt new file mode 100644 index 00000000000..d1864b3b0eb --- /dev/null +++ b/idea/testData/kdoc/typing/closingParenOvertype.kt @@ -0,0 +1,6 @@ +/** + * [foo](bar) + */ +fun foo() + +// TYPE: ) \ No newline at end of file diff --git a/idea/testData/kdoc/typing/closingParenOvertype.kt.after b/idea/testData/kdoc/typing/closingParenOvertype.kt.after new file mode 100644 index 00000000000..93a7b5d2699 --- /dev/null +++ b/idea/testData/kdoc/typing/closingParenOvertype.kt.after @@ -0,0 +1,6 @@ +/** + * [foo](bar) + */ +fun foo() + +// TYPE: ) \ No newline at end of file diff --git a/idea/testData/kdoc/typing/openingBracket.kt b/idea/testData/kdoc/typing/openingBracket.kt new file mode 100644 index 00000000000..d6215b775ad --- /dev/null +++ b/idea/testData/kdoc/typing/openingBracket.kt @@ -0,0 +1,6 @@ +/** + * + */ +fun foo() + +// TYPE: [ \ No newline at end of file diff --git a/idea/testData/kdoc/typing/openingBracket.kt.after b/idea/testData/kdoc/typing/openingBracket.kt.after new file mode 100644 index 00000000000..d5082f945bc --- /dev/null +++ b/idea/testData/kdoc/typing/openingBracket.kt.after @@ -0,0 +1,6 @@ +/** + * [] + */ +fun foo() + +// TYPE: [ \ No newline at end of file diff --git a/idea/testData/kdoc/typing/openingBracketRefLink.kt b/idea/testData/kdoc/typing/openingBracketRefLink.kt new file mode 100644 index 00000000000..e7b697b4c97 --- /dev/null +++ b/idea/testData/kdoc/typing/openingBracketRefLink.kt @@ -0,0 +1,6 @@ +/** + * [foo] + */ +fun foo() + +// TYPE: [ \ No newline at end of file diff --git a/idea/testData/kdoc/typing/openingBracketRefLink.kt.after b/idea/testData/kdoc/typing/openingBracketRefLink.kt.after new file mode 100644 index 00000000000..b12faf52568 --- /dev/null +++ b/idea/testData/kdoc/typing/openingBracketRefLink.kt.after @@ -0,0 +1,6 @@ +/** + * [foo][] + */ +fun foo() + +// TYPE: [ \ No newline at end of file diff --git a/idea/testData/kdoc/typing/openingParen.kt b/idea/testData/kdoc/typing/openingParen.kt new file mode 100644 index 00000000000..a9718da2610 --- /dev/null +++ b/idea/testData/kdoc/typing/openingParen.kt @@ -0,0 +1,6 @@ +/** + * [foo] + */ +fun foo() + +// TYPE: ( \ No newline at end of file diff --git a/idea/testData/kdoc/typing/openingParen.kt.after b/idea/testData/kdoc/typing/openingParen.kt.after new file mode 100644 index 00000000000..2d1ddb2f795 --- /dev/null +++ b/idea/testData/kdoc/typing/openingParen.kt.after @@ -0,0 +1,6 @@ +/** + * [foo]() + */ +fun foo() + +// TYPE: ( \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/kdoc/AbstractKDocTypingTest.kt b/idea/tests/org/jetbrains/kotlin/idea/kdoc/AbstractKDocTypingTest.kt new file mode 100644 index 00000000000..4b52bf1b8e8 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/kdoc/AbstractKDocTypingTest.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2015 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.kotlin.idea.kdoc + +import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase +import org.jetbrains.kotlin.idea.test.JetLightProjectDescriptor +import org.jetbrains.kotlin.test.InTextDirectivesUtils +import org.jetbrains.kotlin.test.JetTestUtils + +public abstract class AbstractKDocTypingTest : JetLightCodeInsightFixtureTestCase() { + override fun getTestDataPath(): String = JetTestUtils.getHomeDirectory() + override fun getProjectDescriptor() = JetLightProjectDescriptor.INSTANCE + + protected fun doTest(fileName: String) { + myFixture.configureByFile(fileName) + val textToType = InTextDirectivesUtils.findStringWithPrefixes(myFixture.getFile().getText(), "// TYPE:") + if (textToType == null) { + throw IllegalArgumentException("Cannot find directive TYPE in input file") + } + myFixture.type(textToType) + myFixture.checkResultByFile(fileName + ".after") + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/kdoc/KDocTypingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/kdoc/KDocTypingTestGenerated.java new file mode 100644 index 00000000000..d6b82f52c62 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/kdoc/KDocTypingTestGenerated.java @@ -0,0 +1,86 @@ +/* + * Copyright 2010-2015 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.kotlin.idea.kdoc; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.InnerTestClasses; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.JetTestUtils; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("idea/testData/kdoc/typing") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class KDocTypingTestGenerated extends AbstractKDocTypingTest { + public void testAllFilesPresentInTyping() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/kdoc/typing"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("closingBracketNotInLink.kt") + public void testClosingBracketNotInLink() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/kdoc/typing/closingBracketNotInLink.kt"); + doTest(fileName); + } + + @TestMetadata("closingBracketOvertype.kt") + public void testClosingBracketOvertype() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/kdoc/typing/closingBracketOvertype.kt"); + doTest(fileName); + } + + @TestMetadata("closingBracketOvertypeEmpty.kt") + public void testClosingBracketOvertypeEmpty() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/kdoc/typing/closingBracketOvertypeEmpty.kt"); + doTest(fileName); + } + + @TestMetadata("closingBracketRefLinkOvertype.kt") + public void testClosingBracketRefLinkOvertype() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/kdoc/typing/closingBracketRefLinkOvertype.kt"); + doTest(fileName); + } + + @TestMetadata("closingParenOvertype.kt") + public void testClosingParenOvertype() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/kdoc/typing/closingParenOvertype.kt"); + doTest(fileName); + } + + @TestMetadata("openingBracket.kt") + public void testOpeningBracket() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/kdoc/typing/openingBracket.kt"); + doTest(fileName); + } + + @TestMetadata("openingBracketRefLink.kt") + public void testOpeningBracketRefLink() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/kdoc/typing/openingBracketRefLink.kt"); + doTest(fileName); + } + + @TestMetadata("openingParen.kt") + public void testOpeningParen() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/kdoc/typing/openingParen.kt"); + doTest(fileName); + } +}