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