From cde66f2f6480c98b42621c850172348746629de7 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Tue, 20 Dec 2016 18:06:03 +0300 Subject: [PATCH] PY-20003 Fixed: Breadcrumbs for Python files Introduce PyBreadcrumbsInfoProvider which supports: classes and functions (incl. async) definitions, lambdas, try-except-else-finally, if-elif-else, for-else (incl. async), while-else, with and key-value statements --- python/python-community.iml | 4 +- python/src/META-INF/python-core-common.xml | 2 + .../breadcrumbs/PyBreadcrumbsInfoProvider.kt | 154 +++++++++++++++ python/testData/breadcrumbs/asyncFor.py | 3 + .../testData/breadcrumbs/asyncFor_crumbs.txt | 8 + .../testData/breadcrumbs/asyncSeveralWith.py | 3 + .../breadcrumbs/asyncSeveralWithAs.py | 3 + .../breadcrumbs/asyncSeveralWithAs_crumbs.txt | 8 + .../breadcrumbs/asyncSeveralWith_crumbs.txt | 8 + python/testData/breadcrumbs/asyncWith.py | 3 + python/testData/breadcrumbs/asyncWithAs.py | 3 + .../breadcrumbs/asyncWithAs_crumbs.txt | 8 + .../testData/breadcrumbs/asyncWith_crumbs.txt | 8 + python/testData/breadcrumbs/class.py | 2 + python/testData/breadcrumbs/class_crumbs.txt | 4 + python/testData/breadcrumbs/dictKey.py | 1 + .../testData/breadcrumbs/dictKey_crumbs.txt | 4 + python/testData/breadcrumbs/elif.py | 4 + python/testData/breadcrumbs/elif_crumbs.txt | 4 + python/testData/breadcrumbs/except.py | 4 + python/testData/breadcrumbs/exceptAll.py | 4 + .../testData/breadcrumbs/exceptAll_crumbs.txt | 4 + python/testData/breadcrumbs/exceptAs.py | 4 + .../testData/breadcrumbs/exceptAs_crumbs.txt | 4 + python/testData/breadcrumbs/except_crumbs.txt | 4 + python/testData/breadcrumbs/finally.py | 4 + .../testData/breadcrumbs/finally_crumbs.txt | 4 + python/testData/breadcrumbs/for.py | 2 + python/testData/breadcrumbs/forElse.py | 4 + .../testData/breadcrumbs/forElse_crumbs.txt | 4 + python/testData/breadcrumbs/for_crumbs.txt | 4 + python/testData/breadcrumbs/function.py | 2 + .../testData/breadcrumbs/function_crumbs.txt | 4 + python/testData/breadcrumbs/if.py | 2 + python/testData/breadcrumbs/ifElse.py | 4 + python/testData/breadcrumbs/ifElse_crumbs.txt | 4 + python/testData/breadcrumbs/if_crumbs.txt | 4 + python/testData/breadcrumbs/lambda.py | 1 + python/testData/breadcrumbs/lambda_crumbs.txt | 4 + python/testData/breadcrumbs/largeLambda.py | 1 + .../breadcrumbs/largeLambda_crumbs.txt | 4 + python/testData/breadcrumbs/method.py | 3 + python/testData/breadcrumbs/method_crumbs.txt | 8 + python/testData/breadcrumbs/severalWith.py | 2 + python/testData/breadcrumbs/severalWithAs.py | 2 + .../breadcrumbs/severalWithAs_crumbs.txt | 4 + .../breadcrumbs/severalWith_crumbs.txt | 4 + python/testData/breadcrumbs/try.py | 4 + python/testData/breadcrumbs/tryElse.py | 6 + .../testData/breadcrumbs/tryElse_crumbs.txt | 4 + python/testData/breadcrumbs/try_crumbs.txt | 4 + python/testData/breadcrumbs/while.py | 3 + python/testData/breadcrumbs/whileElse.py | 5 + .../testData/breadcrumbs/whileElse_crumbs.txt | 4 + python/testData/breadcrumbs/while_crumbs.txt | 4 + python/testData/breadcrumbs/with.py | 2 + python/testData/breadcrumbs/withAs.py | 2 + python/testData/breadcrumbs/withAs_crumbs.txt | 4 + python/testData/breadcrumbs/with_crumbs.txt | 4 + .../python/breadcrumbs/PyBreadcrumbsTest.java | 179 ++++++++++++++++++ python/testSrc/python-community-tests.iml | 1 + 61 files changed, 557 insertions(+), 2 deletions(-) create mode 100644 python/src/com/jetbrains/python/breadcrumbs/PyBreadcrumbsInfoProvider.kt create mode 100644 python/testData/breadcrumbs/asyncFor.py create mode 100644 python/testData/breadcrumbs/asyncFor_crumbs.txt create mode 100644 python/testData/breadcrumbs/asyncSeveralWith.py create mode 100644 python/testData/breadcrumbs/asyncSeveralWithAs.py create mode 100644 python/testData/breadcrumbs/asyncSeveralWithAs_crumbs.txt create mode 100644 python/testData/breadcrumbs/asyncSeveralWith_crumbs.txt create mode 100644 python/testData/breadcrumbs/asyncWith.py create mode 100644 python/testData/breadcrumbs/asyncWithAs.py create mode 100644 python/testData/breadcrumbs/asyncWithAs_crumbs.txt create mode 100644 python/testData/breadcrumbs/asyncWith_crumbs.txt create mode 100644 python/testData/breadcrumbs/class.py create mode 100644 python/testData/breadcrumbs/class_crumbs.txt create mode 100644 python/testData/breadcrumbs/dictKey.py create mode 100644 python/testData/breadcrumbs/dictKey_crumbs.txt create mode 100644 python/testData/breadcrumbs/elif.py create mode 100644 python/testData/breadcrumbs/elif_crumbs.txt create mode 100644 python/testData/breadcrumbs/except.py create mode 100644 python/testData/breadcrumbs/exceptAll.py create mode 100644 python/testData/breadcrumbs/exceptAll_crumbs.txt create mode 100644 python/testData/breadcrumbs/exceptAs.py create mode 100644 python/testData/breadcrumbs/exceptAs_crumbs.txt create mode 100644 python/testData/breadcrumbs/except_crumbs.txt create mode 100644 python/testData/breadcrumbs/finally.py create mode 100644 python/testData/breadcrumbs/finally_crumbs.txt create mode 100644 python/testData/breadcrumbs/for.py create mode 100644 python/testData/breadcrumbs/forElse.py create mode 100644 python/testData/breadcrumbs/forElse_crumbs.txt create mode 100644 python/testData/breadcrumbs/for_crumbs.txt create mode 100644 python/testData/breadcrumbs/function.py create mode 100644 python/testData/breadcrumbs/function_crumbs.txt create mode 100644 python/testData/breadcrumbs/if.py create mode 100644 python/testData/breadcrumbs/ifElse.py create mode 100644 python/testData/breadcrumbs/ifElse_crumbs.txt create mode 100644 python/testData/breadcrumbs/if_crumbs.txt create mode 100644 python/testData/breadcrumbs/lambda.py create mode 100644 python/testData/breadcrumbs/lambda_crumbs.txt create mode 100644 python/testData/breadcrumbs/largeLambda.py create mode 100644 python/testData/breadcrumbs/largeLambda_crumbs.txt create mode 100644 python/testData/breadcrumbs/method.py create mode 100644 python/testData/breadcrumbs/method_crumbs.txt create mode 100644 python/testData/breadcrumbs/severalWith.py create mode 100644 python/testData/breadcrumbs/severalWithAs.py create mode 100644 python/testData/breadcrumbs/severalWithAs_crumbs.txt create mode 100644 python/testData/breadcrumbs/severalWith_crumbs.txt create mode 100644 python/testData/breadcrumbs/try.py create mode 100644 python/testData/breadcrumbs/tryElse.py create mode 100644 python/testData/breadcrumbs/tryElse_crumbs.txt create mode 100644 python/testData/breadcrumbs/try_crumbs.txt create mode 100644 python/testData/breadcrumbs/while.py create mode 100644 python/testData/breadcrumbs/whileElse.py create mode 100644 python/testData/breadcrumbs/whileElse_crumbs.txt create mode 100644 python/testData/breadcrumbs/while_crumbs.txt create mode 100644 python/testData/breadcrumbs/with.py create mode 100644 python/testData/breadcrumbs/withAs.py create mode 100644 python/testData/breadcrumbs/withAs_crumbs.txt create mode 100644 python/testData/breadcrumbs/with_crumbs.txt create mode 100644 python/testSrc/com/jetbrains/python/breadcrumbs/PyBreadcrumbsTest.java diff --git a/python/python-community.iml b/python/python-community.iml index de44e2740069..27c961be19cd 100644 --- a/python/python-community.iml +++ b/python/python-community.iml @@ -24,6 +24,6 @@ + - - + \ No newline at end of file diff --git a/python/src/META-INF/python-core-common.xml b/python/src/META-INF/python-core-common.xml index b86b45dc7a0e..57743f01c072 100644 --- a/python/src/META-INF/python-core-common.xml +++ b/python/src/META-INF/python-core-common.xml @@ -602,6 +602,8 @@ + + diff --git a/python/src/com/jetbrains/python/breadcrumbs/PyBreadcrumbsInfoProvider.kt b/python/src/com/jetbrains/python/breadcrumbs/PyBreadcrumbsInfoProvider.kt new file mode 100644 index 000000000000..3974ffdc4e51 --- /dev/null +++ b/python/src/com/jetbrains/python/breadcrumbs/PyBreadcrumbsInfoProvider.kt @@ -0,0 +1,154 @@ +/* + * Copyright 2000-2016 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.jetbrains.python.breadcrumbs + +import com.intellij.openapi.util.text.StringUtil +import com.intellij.psi.PsiElement +import com.intellij.xml.breadcrumbs.BreadcrumbsInfoProvider +import com.jetbrains.python.PythonLanguage +import com.jetbrains.python.psi.* + +class PyBreadcrumbsInfoProvider : BreadcrumbsInfoProvider() { + + companion object { + private val LANGUAGES = arrayOf(PythonLanguage.getInstance()) + private val HELPERS = listOf>( + LambdaHelper, + SimpleHelper(PyTryPart::class.java, "try"), + ExceptHelper, + SimpleHelper(PyFinallyPart::class.java, "finally"), + SimpleHelper(PyElsePart::class.java, "else"), + IfHelper, + ForHelper, + WhileHelper, + WithHelper, + ClassHelper, + FunctionHelper, + KeyValueHelper + ) + } + + override fun getLanguages() = LANGUAGES + override fun acceptElement(e: PsiElement) = getHelper(e) != null + override fun getElementInfo(e: PsiElement) = getHelper(e)!!.elementInfo(e as PyElement) + override fun getElementTooltip(e: PsiElement) = getHelper(e)!!.elementTooltip(e as PyElement) + + private fun getHelper(e: PsiElement): Helper? { + if (e !is PyElement) return null + + @Suppress("UNCHECKED_CAST") + return HELPERS.firstOrNull { it.type.isInstance(e) && (it as Helper).accepts(e) } as Helper? + } + + private abstract class Helper(val type: Class) { + abstract fun accepts(e: T): Boolean + abstract fun elementInfo(e: T): String + abstract fun elementTooltip(e: T): String + } + + private abstract class AbstractHelper(type: Class) : Helper(type) { + override fun accepts(e: T): Boolean = true + override fun elementInfo(e: T): String = getTruncatedPresentation(e, 16) + override fun elementTooltip(e: T): String = getTruncatedPresentation(e, 96) + + abstract fun getPresentation(e: T): String + + private fun getTruncatedPresentation(e: T, maxLength: Int) = StringUtil.shortenTextWithEllipsis(getPresentation(e), maxLength, 0, true) + } + + private class SimpleHelper(type: Class, val representation: String) : AbstractHelper(type) { + override fun getPresentation(e: T) = representation + } + + private object LambdaHelper : AbstractHelper(PyLambdaExpression::class.java) { + override fun getPresentation(e: PyLambdaExpression) = "lambda ${e.parameterList.getPresentableText(false)}" + } + + private object ExceptHelper : AbstractHelper(PyExceptPart::class.java) { + override fun getPresentation(e: PyExceptPart): String { + val exceptClass = e.exceptClass ?: return "except" + val target = e.target ?: return "except ${exceptClass.text}" + + return "except ${exceptClass.text} as ${target.text}" + } + } + + private object IfHelper : AbstractHelper(PyIfPart::class.java) { + override fun getPresentation(e: PyIfPart): String { + val prefix = if (e.isElif) "elif" else "if" + val condition = e.condition ?: return prefix + + return "$prefix ${condition.text}" + } + } + + private object ForHelper : AbstractHelper(PyForPart::class.java) { + override fun getPresentation(e: PyForPart): String { + val parent = e.parent + val prefix = if (parent is PyForStatement && parent.isAsync) "async for" else "for" + + val target = e.target ?: return prefix + val source = e.source ?: return prefix + + return "$prefix ${target.text} in ${source.text}" + } + } + + private object WhileHelper : AbstractHelper(PyWhilePart::class.java) { + override fun getPresentation(e: PyWhilePart): String { + val condition = e.condition ?: return "while" + + return "while ${condition.text}" + } + } + + private object WithHelper : AbstractHelper(PyWithStatement::class.java) { + override fun getPresentation(e: PyWithStatement): String { + val getItemPresentation = fun(item: PyWithItem): String? { + val expression = item.expression ?: return null + val target = item.target ?: return expression.text + return "${expression.text} as ${target.text}" + } + + val prefix = if (e.isAsync) "async with " else "with " + + return e.withItems + .orEmpty() + .asSequence() + .map(getItemPresentation) + .filterNotNull() + .joinToString(prefix = prefix) + } + } + + private object ClassHelper : AbstractHelper(PyClass::class.java) { + override fun getPresentation(e: PyClass) = e.name ?: "class" + } + + private object FunctionHelper : AbstractHelper(PyFunction::class.java) { + override fun getPresentation(e: PyFunction): String { + val prefix = if (e.isAsync) "async " else "" + val name = e.name ?: return "function" + + return "$prefix$name()" + } + } + + private object KeyValueHelper : AbstractHelper(PyKeyValueExpression::class.java) { + override fun getPresentation(e: PyKeyValueExpression): String = e.key.text ?: "key" + } +} + diff --git a/python/testData/breadcrumbs/asyncFor.py b/python/testData/breadcrumbs/asyncFor.py new file mode 100644 index 000000000000..1778cf9857a7 --- /dev/null +++ b/python/testData/breadcrumbs/asyncFor.py @@ -0,0 +1,3 @@ +async def foo(): + async for i in range(10): + print(i) \ No newline at end of file diff --git a/python/testData/breadcrumbs/asyncFor_crumbs.txt b/python/testData/breadcrumbs/asyncFor_crumbs.txt new file mode 100644 index 000000000000..3793270b94fd --- /dev/null +++ b/python/testData/breadcrumbs/asyncFor_crumbs.txt @@ -0,0 +1,8 @@ +Crumb: +async foo() +Tooltip: +async foo() +Crumb: +async for i in … +Tooltip: +async for i in range(10) \ No newline at end of file diff --git a/python/testData/breadcrumbs/asyncSeveralWith.py b/python/testData/breadcrumbs/asyncSeveralWith.py new file mode 100644 index 000000000000..425c6d7fcb45 --- /dev/null +++ b/python/testData/breadcrumbs/asyncSeveralWith.py @@ -0,0 +1,3 @@ +async def foo(): + async with open("file.txt"), open("file2.txt"): + print("abc") \ No newline at end of file diff --git a/python/testData/breadcrumbs/asyncSeveralWithAs.py b/python/testData/breadcrumbs/asyncSeveralWithAs.py new file mode 100644 index 000000000000..108808f12b2e --- /dev/null +++ b/python/testData/breadcrumbs/asyncSeveralWithAs.py @@ -0,0 +1,3 @@ +async def foo(): + async with open("file.txt") as f, open("file2.txt") as f2: + print("abc") \ No newline at end of file diff --git a/python/testData/breadcrumbs/asyncSeveralWithAs_crumbs.txt b/python/testData/breadcrumbs/asyncSeveralWithAs_crumbs.txt new file mode 100644 index 000000000000..7d2f7603ca92 --- /dev/null +++ b/python/testData/breadcrumbs/asyncSeveralWithAs_crumbs.txt @@ -0,0 +1,8 @@ +Crumb: +async foo() +Tooltip: +async foo() +Crumb: +async with open… +Tooltip: +async with open("file.txt") as f, open("file2.txt") as f2 \ No newline at end of file diff --git a/python/testData/breadcrumbs/asyncSeveralWith_crumbs.txt b/python/testData/breadcrumbs/asyncSeveralWith_crumbs.txt new file mode 100644 index 000000000000..d8056429af39 --- /dev/null +++ b/python/testData/breadcrumbs/asyncSeveralWith_crumbs.txt @@ -0,0 +1,8 @@ +Crumb: +async foo() +Tooltip: +async foo() +Crumb: +async with open… +Tooltip: +async with open("file.txt"), open("file2.txt") \ No newline at end of file diff --git a/python/testData/breadcrumbs/asyncWith.py b/python/testData/breadcrumbs/asyncWith.py new file mode 100644 index 000000000000..8b94c6813377 --- /dev/null +++ b/python/testData/breadcrumbs/asyncWith.py @@ -0,0 +1,3 @@ +async def foo(): + async with open("file.txt"): + print("abc") \ No newline at end of file diff --git a/python/testData/breadcrumbs/asyncWithAs.py b/python/testData/breadcrumbs/asyncWithAs.py new file mode 100644 index 000000000000..d1070250e93c --- /dev/null +++ b/python/testData/breadcrumbs/asyncWithAs.py @@ -0,0 +1,3 @@ +async def foo(): + async with open("file.txt") as f: + print("abc") \ No newline at end of file diff --git a/python/testData/breadcrumbs/asyncWithAs_crumbs.txt b/python/testData/breadcrumbs/asyncWithAs_crumbs.txt new file mode 100644 index 000000000000..c896acf30ab6 --- /dev/null +++ b/python/testData/breadcrumbs/asyncWithAs_crumbs.txt @@ -0,0 +1,8 @@ +Crumb: +async foo() +Tooltip: +async foo() +Crumb: +async with open… +Tooltip: +async with open("file.txt") as f \ No newline at end of file diff --git a/python/testData/breadcrumbs/asyncWith_crumbs.txt b/python/testData/breadcrumbs/asyncWith_crumbs.txt new file mode 100644 index 000000000000..302b39b58b00 --- /dev/null +++ b/python/testData/breadcrumbs/asyncWith_crumbs.txt @@ -0,0 +1,8 @@ +Crumb: +async foo() +Tooltip: +async foo() +Crumb: +async with open… +Tooltip: +async with open("file.txt") \ No newline at end of file diff --git a/python/testData/breadcrumbs/class.py b/python/testData/breadcrumbs/class.py new file mode 100644 index 000000000000..c736b1dbc73b --- /dev/null +++ b/python/testData/breadcrumbs/class.py @@ -0,0 +1,2 @@ +class A: + pass \ No newline at end of file diff --git a/python/testData/breadcrumbs/class_crumbs.txt b/python/testData/breadcrumbs/class_crumbs.txt new file mode 100644 index 000000000000..ef0370e0adab --- /dev/null +++ b/python/testData/breadcrumbs/class_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +A +Tooltip: +A \ No newline at end of file diff --git a/python/testData/breadcrumbs/dictKey.py b/python/testData/breadcrumbs/dictKey.py new file mode 100644 index 000000000000..01a9f357309d --- /dev/null +++ b/python/testData/breadcrumbs/dictKey.py @@ -0,0 +1 @@ +a = {"abc": "def"} \ No newline at end of file diff --git a/python/testData/breadcrumbs/dictKey_crumbs.txt b/python/testData/breadcrumbs/dictKey_crumbs.txt new file mode 100644 index 000000000000..00c7e454b5f5 --- /dev/null +++ b/python/testData/breadcrumbs/dictKey_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +"abc" +Tooltip: +"abc" \ No newline at end of file diff --git a/python/testData/breadcrumbs/elif.py b/python/testData/breadcrumbs/elif.py new file mode 100644 index 000000000000..284a84d84dce --- /dev/null +++ b/python/testData/breadcrumbs/elif.py @@ -0,0 +1,4 @@ +if True: + print "abc" +elif False: + print "def" \ No newline at end of file diff --git a/python/testData/breadcrumbs/elif_crumbs.txt b/python/testData/breadcrumbs/elif_crumbs.txt new file mode 100644 index 000000000000..7a2ccebe22ac --- /dev/null +++ b/python/testData/breadcrumbs/elif_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +elif False +Tooltip: +elif False \ No newline at end of file diff --git a/python/testData/breadcrumbs/except.py b/python/testData/breadcrumbs/except.py new file mode 100644 index 000000000000..b436d541ecd0 --- /dev/null +++ b/python/testData/breadcrumbs/except.py @@ -0,0 +1,4 @@ +try: + print "abc" +except KeyError: + print "def" \ No newline at end of file diff --git a/python/testData/breadcrumbs/exceptAll.py b/python/testData/breadcrumbs/exceptAll.py new file mode 100644 index 000000000000..aa31bf24228b --- /dev/null +++ b/python/testData/breadcrumbs/exceptAll.py @@ -0,0 +1,4 @@ +try: + print "abc" +except: + print "def" \ No newline at end of file diff --git a/python/testData/breadcrumbs/exceptAll_crumbs.txt b/python/testData/breadcrumbs/exceptAll_crumbs.txt new file mode 100644 index 000000000000..eaf7bee4cc28 --- /dev/null +++ b/python/testData/breadcrumbs/exceptAll_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +except +Tooltip: +except \ No newline at end of file diff --git a/python/testData/breadcrumbs/exceptAs.py b/python/testData/breadcrumbs/exceptAs.py new file mode 100644 index 000000000000..98450c359de4 --- /dev/null +++ b/python/testData/breadcrumbs/exceptAs.py @@ -0,0 +1,4 @@ +try: + print "abc" +except KeyError as e: + print "def" \ No newline at end of file diff --git a/python/testData/breadcrumbs/exceptAs_crumbs.txt b/python/testData/breadcrumbs/exceptAs_crumbs.txt new file mode 100644 index 000000000000..d8e4cdf88ec1 --- /dev/null +++ b/python/testData/breadcrumbs/exceptAs_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +except KeyError… +Tooltip: +except KeyError as e \ No newline at end of file diff --git a/python/testData/breadcrumbs/except_crumbs.txt b/python/testData/breadcrumbs/except_crumbs.txt new file mode 100644 index 000000000000..8f3663d1a793 --- /dev/null +++ b/python/testData/breadcrumbs/except_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +except KeyError +Tooltip: +except KeyError \ No newline at end of file diff --git a/python/testData/breadcrumbs/finally.py b/python/testData/breadcrumbs/finally.py new file mode 100644 index 000000000000..af1941ac4daa --- /dev/null +++ b/python/testData/breadcrumbs/finally.py @@ -0,0 +1,4 @@ +try: + print "abc" +finally: + print "def" \ No newline at end of file diff --git a/python/testData/breadcrumbs/finally_crumbs.txt b/python/testData/breadcrumbs/finally_crumbs.txt new file mode 100644 index 000000000000..feaa63589a56 --- /dev/null +++ b/python/testData/breadcrumbs/finally_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +finally +Tooltip: +finally \ No newline at end of file diff --git a/python/testData/breadcrumbs/for.py b/python/testData/breadcrumbs/for.py new file mode 100644 index 000000000000..5688957f0244 --- /dev/null +++ b/python/testData/breadcrumbs/for.py @@ -0,0 +1,2 @@ +for i in range(10): + print i \ No newline at end of file diff --git a/python/testData/breadcrumbs/forElse.py b/python/testData/breadcrumbs/forElse.py new file mode 100644 index 000000000000..c52942057b24 --- /dev/null +++ b/python/testData/breadcrumbs/forElse.py @@ -0,0 +1,4 @@ +for i in range(10): + print i +else: + print "abc" \ No newline at end of file diff --git a/python/testData/breadcrumbs/forElse_crumbs.txt b/python/testData/breadcrumbs/forElse_crumbs.txt new file mode 100644 index 000000000000..244ba2e1338a --- /dev/null +++ b/python/testData/breadcrumbs/forElse_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +else +Tooltip: +else \ No newline at end of file diff --git a/python/testData/breadcrumbs/for_crumbs.txt b/python/testData/breadcrumbs/for_crumbs.txt new file mode 100644 index 000000000000..bdd11a58c458 --- /dev/null +++ b/python/testData/breadcrumbs/for_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +for i in range(… +Tooltip: +for i in range(10) \ No newline at end of file diff --git a/python/testData/breadcrumbs/function.py b/python/testData/breadcrumbs/function.py new file mode 100644 index 000000000000..5c35f441741b --- /dev/null +++ b/python/testData/breadcrumbs/function.py @@ -0,0 +1,2 @@ +def foo(self): + pass \ No newline at end of file diff --git a/python/testData/breadcrumbs/function_crumbs.txt b/python/testData/breadcrumbs/function_crumbs.txt new file mode 100644 index 000000000000..526a95723025 --- /dev/null +++ b/python/testData/breadcrumbs/function_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +foo() +Tooltip: +foo() \ No newline at end of file diff --git a/python/testData/breadcrumbs/if.py b/python/testData/breadcrumbs/if.py new file mode 100644 index 000000000000..5e0230748e01 --- /dev/null +++ b/python/testData/breadcrumbs/if.py @@ -0,0 +1,2 @@ +if True: + print "abc" \ No newline at end of file diff --git a/python/testData/breadcrumbs/ifElse.py b/python/testData/breadcrumbs/ifElse.py new file mode 100644 index 000000000000..eb460b44d343 --- /dev/null +++ b/python/testData/breadcrumbs/ifElse.py @@ -0,0 +1,4 @@ +if True: + print "abc" +else: + print "def" \ No newline at end of file diff --git a/python/testData/breadcrumbs/ifElse_crumbs.txt b/python/testData/breadcrumbs/ifElse_crumbs.txt new file mode 100644 index 000000000000..244ba2e1338a --- /dev/null +++ b/python/testData/breadcrumbs/ifElse_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +else +Tooltip: +else \ No newline at end of file diff --git a/python/testData/breadcrumbs/if_crumbs.txt b/python/testData/breadcrumbs/if_crumbs.txt new file mode 100644 index 000000000000..4d58d9f8013a --- /dev/null +++ b/python/testData/breadcrumbs/if_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +if True +Tooltip: +if True \ No newline at end of file diff --git a/python/testData/breadcrumbs/lambda.py b/python/testData/breadcrumbs/lambda.py new file mode 100644 index 000000000000..86e7f4e67066 --- /dev/null +++ b/python/testData/breadcrumbs/lambda.py @@ -0,0 +1 @@ +map(lambda x, y: x * y, []) \ No newline at end of file diff --git a/python/testData/breadcrumbs/lambda_crumbs.txt b/python/testData/breadcrumbs/lambda_crumbs.txt new file mode 100644 index 000000000000..b62f670b3ad4 --- /dev/null +++ b/python/testData/breadcrumbs/lambda_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +lambda (x, y) +Tooltip: +lambda (x, y) \ No newline at end of file diff --git a/python/testData/breadcrumbs/largeLambda.py b/python/testData/breadcrumbs/largeLambda.py new file mode 100644 index 000000000000..5ed11a50f864 --- /dev/null +++ b/python/testData/breadcrumbs/largeLambda.py @@ -0,0 +1 @@ +map(lambda a, aa, aaa, aaaa, aaaaa, aaaaaa, aaaaaaa, aaaaaaaa, aaaaaaaaa, aaaaaaaaaa, aaaaaaaaaaa, aaaaaaaaaaaa: a * a, []) \ No newline at end of file diff --git a/python/testData/breadcrumbs/largeLambda_crumbs.txt b/python/testData/breadcrumbs/largeLambda_crumbs.txt new file mode 100644 index 000000000000..9d919548e8d3 --- /dev/null +++ b/python/testData/breadcrumbs/largeLambda_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +lambda (a, aa, … +Tooltip: +lambda (a, aa, aaa, aaaa, aaaaa, aaaaaa, aaaaaaa, aaaaaaaa, aaaaaaaaa, aaaaaaaaaa, aaaaaaaaaaa,… \ No newline at end of file diff --git a/python/testData/breadcrumbs/method.py b/python/testData/breadcrumbs/method.py new file mode 100644 index 000000000000..4bc5bb9645d9 --- /dev/null +++ b/python/testData/breadcrumbs/method.py @@ -0,0 +1,3 @@ +class A: + def foo(self): + pass \ No newline at end of file diff --git a/python/testData/breadcrumbs/method_crumbs.txt b/python/testData/breadcrumbs/method_crumbs.txt new file mode 100644 index 000000000000..4f8eab84e78c --- /dev/null +++ b/python/testData/breadcrumbs/method_crumbs.txt @@ -0,0 +1,8 @@ +Crumb: +A +Tooltip: +A +Crumb: +foo() +Tooltip: +foo() \ No newline at end of file diff --git a/python/testData/breadcrumbs/severalWith.py b/python/testData/breadcrumbs/severalWith.py new file mode 100644 index 000000000000..658eacaaa06d --- /dev/null +++ b/python/testData/breadcrumbs/severalWith.py @@ -0,0 +1,2 @@ +with open("file.txt"), open("file2.txt"): + print("abc") \ No newline at end of file diff --git a/python/testData/breadcrumbs/severalWithAs.py b/python/testData/breadcrumbs/severalWithAs.py new file mode 100644 index 000000000000..39dfa74677c9 --- /dev/null +++ b/python/testData/breadcrumbs/severalWithAs.py @@ -0,0 +1,2 @@ +with open("file.txt") as f, open("file2.txt") as f2: + print("abc") \ No newline at end of file diff --git a/python/testData/breadcrumbs/severalWithAs_crumbs.txt b/python/testData/breadcrumbs/severalWithAs_crumbs.txt new file mode 100644 index 000000000000..7429e98fafec --- /dev/null +++ b/python/testData/breadcrumbs/severalWithAs_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +with open("file… +Tooltip: +with open("file.txt") as f, open("file2.txt") as f2 \ No newline at end of file diff --git a/python/testData/breadcrumbs/severalWith_crumbs.txt b/python/testData/breadcrumbs/severalWith_crumbs.txt new file mode 100644 index 000000000000..e590e41e808f --- /dev/null +++ b/python/testData/breadcrumbs/severalWith_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +with open("file… +Tooltip: +with open("file.txt"), open("file2.txt") \ No newline at end of file diff --git a/python/testData/breadcrumbs/try.py b/python/testData/breadcrumbs/try.py new file mode 100644 index 000000000000..135d1be27cfb --- /dev/null +++ b/python/testData/breadcrumbs/try.py @@ -0,0 +1,4 @@ +try: + print "abc" +finally: + print "def" \ No newline at end of file diff --git a/python/testData/breadcrumbs/tryElse.py b/python/testData/breadcrumbs/tryElse.py new file mode 100644 index 000000000000..5c3dc2a45ea6 --- /dev/null +++ b/python/testData/breadcrumbs/tryElse.py @@ -0,0 +1,6 @@ +try: + print "abc" +except: + print "def" +else: + print "ghi" \ No newline at end of file diff --git a/python/testData/breadcrumbs/tryElse_crumbs.txt b/python/testData/breadcrumbs/tryElse_crumbs.txt new file mode 100644 index 000000000000..244ba2e1338a --- /dev/null +++ b/python/testData/breadcrumbs/tryElse_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +else +Tooltip: +else \ No newline at end of file diff --git a/python/testData/breadcrumbs/try_crumbs.txt b/python/testData/breadcrumbs/try_crumbs.txt new file mode 100644 index 000000000000..b475c8313a66 --- /dev/null +++ b/python/testData/breadcrumbs/try_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +try +Tooltip: +try \ No newline at end of file diff --git a/python/testData/breadcrumbs/while.py b/python/testData/breadcrumbs/while.py new file mode 100644 index 000000000000..313c606faf56 --- /dev/null +++ b/python/testData/breadcrumbs/while.py @@ -0,0 +1,3 @@ +i = 10 +while i < 30: + print i \ No newline at end of file diff --git a/python/testData/breadcrumbs/whileElse.py b/python/testData/breadcrumbs/whileElse.py new file mode 100644 index 000000000000..313ece9d8916 --- /dev/null +++ b/python/testData/breadcrumbs/whileElse.py @@ -0,0 +1,5 @@ +i = 10 +while i < 30: + print i +else: + print "abc" \ No newline at end of file diff --git a/python/testData/breadcrumbs/whileElse_crumbs.txt b/python/testData/breadcrumbs/whileElse_crumbs.txt new file mode 100644 index 000000000000..244ba2e1338a --- /dev/null +++ b/python/testData/breadcrumbs/whileElse_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +else +Tooltip: +else \ No newline at end of file diff --git a/python/testData/breadcrumbs/while_crumbs.txt b/python/testData/breadcrumbs/while_crumbs.txt new file mode 100644 index 000000000000..ec40ade6d07d --- /dev/null +++ b/python/testData/breadcrumbs/while_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +while i < 30 +Tooltip: +while i < 30 \ No newline at end of file diff --git a/python/testData/breadcrumbs/with.py b/python/testData/breadcrumbs/with.py new file mode 100644 index 000000000000..370722622a4f --- /dev/null +++ b/python/testData/breadcrumbs/with.py @@ -0,0 +1,2 @@ +with open("file.txt"): + print("abc") \ No newline at end of file diff --git a/python/testData/breadcrumbs/withAs.py b/python/testData/breadcrumbs/withAs.py new file mode 100644 index 000000000000..6251c6d02461 --- /dev/null +++ b/python/testData/breadcrumbs/withAs.py @@ -0,0 +1,2 @@ +with open("file.txt") as f: + print("abc") \ No newline at end of file diff --git a/python/testData/breadcrumbs/withAs_crumbs.txt b/python/testData/breadcrumbs/withAs_crumbs.txt new file mode 100644 index 000000000000..ef64791b33fd --- /dev/null +++ b/python/testData/breadcrumbs/withAs_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +with open("file… +Tooltip: +with open("file.txt") as f \ No newline at end of file diff --git a/python/testData/breadcrumbs/with_crumbs.txt b/python/testData/breadcrumbs/with_crumbs.txt new file mode 100644 index 000000000000..f5bc80910d6a --- /dev/null +++ b/python/testData/breadcrumbs/with_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +with open("file… +Tooltip: +with open("file.txt") \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/breadcrumbs/PyBreadcrumbsTest.java b/python/testSrc/com/jetbrains/python/breadcrumbs/PyBreadcrumbsTest.java new file mode 100644 index 000000000000..c40c2a0f3a7f --- /dev/null +++ b/python/testSrc/com/jetbrains/python/breadcrumbs/PyBreadcrumbsTest.java @@ -0,0 +1,179 @@ +/* + * Copyright 2000-2016 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.jetbrains.python.breadcrumbs; + +import com.google.common.collect.Lists; +import com.intellij.psi.PsiElement; +import com.intellij.xml.breadcrumbs.BreadcrumbsInfoProvider; +import com.jetbrains.python.fixtures.PyTestCase; +import com.jetbrains.python.psi.LanguageLevel; +import one.util.streamex.StreamEx; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class PyBreadcrumbsTest extends PyTestCase { + + @Override + protected String getTestDataPath() { + return super.getTestDataPath() + "/breadcrumbs"; + } + + public void testDictKey() { + doTest(); + } + + public void testLambda() { + doTest(); + } + + public void testLargeLambda() { + doTest(); + } + + public void testTry() { + doTest(); + } + + public void testExcept() { + doTest(); + } + + public void testExceptAll() { + doTest(); + } + + public void testExceptAs() { + doTest(); + } + + public void testFinally() { + doTest(); + } + + public void testTryElse() { + doTest(); + } + + public void testIf() { + doTest(); + } + + public void testElif() { + doTest(); + } + + public void testIfElse() { + doTest(); + } + + public void testFor() { + doTest(); + } + + public void testAsyncFor() { + runWithLanguageLevel(LanguageLevel.PYTHON35, this::doTest); + } + + public void testForElse() { + doTest(); + } + + public void testWhile() { + doTest(); + } + + public void testWhileElse() { + doTest(); + } + + public void testWith() { + doTest(); + } + + public void testWithAs() { + doTest(); + } + + public void testSeveralWith() { + doTest(); + } + + public void testSeveralWithAs() { + doTest(); + } + + public void testAsyncWith() { + runWithLanguageLevel(LanguageLevel.PYTHON35, this::doTest); + } + + public void testAsyncWithAs() { + runWithLanguageLevel(LanguageLevel.PYTHON35, this::doTest); + } + + public void testAsyncSeveralWith() { + runWithLanguageLevel(LanguageLevel.PYTHON35, this::doTest); + } + + public void testAsyncSeveralWithAs() { + runWithLanguageLevel(LanguageLevel.PYTHON35, this::doTest); + } + + public void testClass() { + doTest(); + } + + public void testMethod() { + doTest(); + } + + public void testFunction() { + doTest(); + } + + private void doTest() { + final String testName = getTestName(true); + + myFixture.configureByFile(testName + ".py"); + + final PyBreadcrumbsInfoProvider provider = new PyBreadcrumbsInfoProvider(); + final String breadcrumbsAndTooltips = getBreadcrumbsAndTooltips(provider, elementsFromTopToCaret(provider)); + + assertSameLinesWithFile(getTestDataPath() + "/" + testName + "_crumbs.txt", breadcrumbsAndTooltips); + } + + @NotNull + private List elementsFromTopToCaret(@NotNull BreadcrumbsInfoProvider provider) { + final List fromElementToNullExcluded = StreamEx + .iterate(myFixture.getFile().findElementAt(myFixture.getCaretOffset()), provider::getParent) + .takeWhile(Objects::nonNull) + .filter(provider::acceptElement) + .toList(); + + return Lists.reverse(fromElementToNullExcluded); + } + + @NotNull + private static String getBreadcrumbsAndTooltips(@NotNull BreadcrumbsInfoProvider provider, @NotNull List elements) { + return elements + .stream() + .flatMap(element -> Stream.of("Crumb:", provider.getElementInfo(element), "Tooltip:", provider.getElementTooltip(element))) + .collect(Collectors.joining("\n")); + } +} \ No newline at end of file diff --git a/python/testSrc/python-community-tests.iml b/python/testSrc/python-community-tests.iml index 217d881ed828..d20b759e60cf 100644 --- a/python/testSrc/python-community-tests.iml +++ b/python/testSrc/python-community-tests.iml @@ -22,5 +22,6 @@ + \ No newline at end of file