diff --git a/python/src/com/jetbrains/python/breadcrumbs/PyBreadcrumbsInfoProvider.kt b/python/src/com/jetbrains/python/breadcrumbs/PyBreadcrumbsInfoProvider.kt index 3974ffdc4e51..e76ddd51f721 100644 --- a/python/src/com/jetbrains/python/breadcrumbs/PyBreadcrumbsInfoProvider.kt +++ b/python/src/com/jetbrains/python/breadcrumbs/PyBreadcrumbsInfoProvider.kt @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -15,9 +15,16 @@ */ package com.jetbrains.python.breadcrumbs +import com.intellij.openapi.fileEditor.FileEditorManager +import com.intellij.openapi.fileEditor.TextEditor import com.intellij.openapi.util.text.StringUtil +import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiElement +import com.intellij.psi.PsiWhiteSpace +import com.intellij.psi.impl.source.tree.LeafElement +import com.intellij.psi.util.PsiTreeUtil import com.intellij.xml.breadcrumbs.BreadcrumbsInfoProvider +import com.jetbrains.python.PyTokenTypes import com.jetbrains.python.PythonLanguage import com.jetbrains.python.psi.* @@ -27,10 +34,10 @@ class PyBreadcrumbsInfoProvider : BreadcrumbsInfoProvider() { private val LANGUAGES = arrayOf(PythonLanguage.getInstance()) private val HELPERS = listOf>( LambdaHelper, - SimpleHelper(PyTryPart::class.java, "try"), + SimpleHelper(PyTryPart::class.java, "try"), ExceptHelper, - SimpleHelper(PyFinallyPart::class.java, "finally"), - SimpleHelper(PyElsePart::class.java, "else"), + SimpleHelper(PyFinallyPart::class.java, "finally"), + SimpleHelper(PyElsePart::class.java, "else"), IfHelper, ForHelper, WhileHelper, @@ -42,7 +49,24 @@ class PyBreadcrumbsInfoProvider : BreadcrumbsInfoProvider() { } override fun getLanguages() = LANGUAGES + override fun acceptElement(e: PsiElement) = getHelper(e) != null + + override fun getParent(e: PsiElement): PsiElement? { + val default = e.parent + + val currentOffset = currentOffset(e) ?: return default + if (!isElementToMoveBackward(e, currentOffset)) return default + + val nonWhiteSpace = moveBackward(e, currentOffset) ?: return default + + val psiFile = e.containingFile ?: return default + val document = PsiDocumentManager.getInstance(e.project).getDocument(psiFile) ?: return default + val sameLine = document.getLineNumber(nonWhiteSpace.textOffset) == document.getLineNumber(currentOffset) + + return if (sameLine) nonWhiteSpace.parent else default + } + override fun getElementInfo(e: PsiElement) = getHelper(e)!!.elementInfo(e as PyElement) override fun getElementTooltip(e: PsiElement) = getHelper(e)!!.elementTooltip(e as PyElement) @@ -53,7 +77,30 @@ class PyBreadcrumbsInfoProvider : BreadcrumbsInfoProvider() { return HELPERS.firstOrNull { it.type.isInstance(e) && (it as Helper).accepts(e) } as Helper? } - private abstract class Helper(val type: Class) { + private fun currentOffset(e: PsiElement): Int? { + val virtualFile = e.containingFile?.virtualFile ?: return null + val selectedEditor = FileEditorManager.getInstance(e.project).getSelectedEditor(virtualFile) as? TextEditor ?: return null + + return selectedEditor.editor.caretModel.offset + } + + private fun isElementToMoveBackward(e: PsiElement, currentOffset: Int): Boolean { + if (e is PsiWhiteSpace) return true + if (e !is LeafElement || e.startOffset < currentOffset) return false + + val elementType = e.elementType + return elementType == PyTokenTypes.COMMA || elementType in PyTokenTypes.CLOSE_BRACES + } + + private fun moveBackward(e: PsiElement, currentOffset: Int): PsiElement? { + var result = PsiTreeUtil.prevLeaf(e) + while (result != null && isElementToMoveBackward(result, currentOffset)) { + result = PsiTreeUtil.prevLeaf(result) + } + return result + } + + 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 diff --git a/python/testData/breadcrumbs/functionBodyCaret.py b/python/testData/breadcrumbs/functionBodyCaret.py new file mode 100644 index 000000000000..1620d30f3c01 --- /dev/null +++ b/python/testData/breadcrumbs/functionBodyCaret.py @@ -0,0 +1,3 @@ +class A: + def foo(self): + pass diff --git a/python/testData/breadcrumbs/functionBodyCaret_crumbs.txt b/python/testData/breadcrumbs/functionBodyCaret_crumbs.txt new file mode 100644 index 000000000000..4f8eab84e78c --- /dev/null +++ b/python/testData/breadcrumbs/functionBodyCaret_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/functionBodySpaceCaret.py b/python/testData/breadcrumbs/functionBodySpaceCaret.py new file mode 100644 index 000000000000..5774a6368434 --- /dev/null +++ b/python/testData/breadcrumbs/functionBodySpaceCaret.py @@ -0,0 +1,3 @@ +class A: + def foo(self): + pass diff --git a/python/testData/breadcrumbs/functionBodySpaceCaretNewLineFunction.py b/python/testData/breadcrumbs/functionBodySpaceCaretNewLineFunction.py new file mode 100644 index 000000000000..f9b9f8f2bafe --- /dev/null +++ b/python/testData/breadcrumbs/functionBodySpaceCaretNewLineFunction.py @@ -0,0 +1,5 @@ +class A: + def foo(self): + pass + def abc(self): + pass diff --git a/python/testData/breadcrumbs/functionBodySpaceCaretNewLineFunction_crumbs.txt b/python/testData/breadcrumbs/functionBodySpaceCaretNewLineFunction_crumbs.txt new file mode 100644 index 000000000000..4f8eab84e78c --- /dev/null +++ b/python/testData/breadcrumbs/functionBodySpaceCaretNewLineFunction_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/functionBodySpaceCaret_crumbs.txt b/python/testData/breadcrumbs/functionBodySpaceCaret_crumbs.txt new file mode 100644 index 000000000000..4f8eab84e78c --- /dev/null +++ b/python/testData/breadcrumbs/functionBodySpaceCaret_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/functionNewLineCaretNewLineFunction.py b/python/testData/breadcrumbs/functionNewLineCaretNewLineFunction.py new file mode 100644 index 000000000000..578d515d468f --- /dev/null +++ b/python/testData/breadcrumbs/functionNewLineCaretNewLineFunction.py @@ -0,0 +1,6 @@ +class A: + def foo(self): + pass + + def abc(self): + pass \ No newline at end of file diff --git a/python/testData/breadcrumbs/functionNewLineCaretNewLineFunction_crumbs.txt b/python/testData/breadcrumbs/functionNewLineCaretNewLineFunction_crumbs.txt new file mode 100644 index 000000000000..ef0370e0adab --- /dev/null +++ b/python/testData/breadcrumbs/functionNewLineCaretNewLineFunction_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +A +Tooltip: +A \ No newline at end of file diff --git a/python/testData/breadcrumbs/functionNewLineCaretSpaceFunction.py b/python/testData/breadcrumbs/functionNewLineCaretSpaceFunction.py new file mode 100644 index 000000000000..82a5bacccb0a --- /dev/null +++ b/python/testData/breadcrumbs/functionNewLineCaretSpaceFunction.py @@ -0,0 +1,5 @@ +class A: + def foo(self): + pass + def abc(self): + pass \ No newline at end of file diff --git a/python/testData/breadcrumbs/functionNewLineCaretSpaceFunction_crumbs.txt b/python/testData/breadcrumbs/functionNewLineCaretSpaceFunction_crumbs.txt new file mode 100644 index 000000000000..ef0370e0adab --- /dev/null +++ b/python/testData/breadcrumbs/functionNewLineCaretSpaceFunction_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +A +Tooltip: +A \ No newline at end of file diff --git a/python/testData/breadcrumbs/functionNewLineSpaceCaretNewLineFunction.py b/python/testData/breadcrumbs/functionNewLineSpaceCaretNewLineFunction.py new file mode 100644 index 000000000000..59209c7fabef --- /dev/null +++ b/python/testData/breadcrumbs/functionNewLineSpaceCaretNewLineFunction.py @@ -0,0 +1,6 @@ +class A: + def foo(self): + pass + + def abc(self): + pass \ No newline at end of file diff --git a/python/testData/breadcrumbs/functionNewLineSpaceCaretNewLineFunction_crumbs.txt b/python/testData/breadcrumbs/functionNewLineSpaceCaretNewLineFunction_crumbs.txt new file mode 100644 index 000000000000..ef0370e0adab --- /dev/null +++ b/python/testData/breadcrumbs/functionNewLineSpaceCaretNewLineFunction_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +A +Tooltip: +A \ No newline at end of file diff --git a/python/testData/breadcrumbs/functionNewLineSpaceCaretSpaceFunction.py b/python/testData/breadcrumbs/functionNewLineSpaceCaretSpaceFunction.py new file mode 100644 index 000000000000..c6c605c2e7a3 --- /dev/null +++ b/python/testData/breadcrumbs/functionNewLineSpaceCaretSpaceFunction.py @@ -0,0 +1,5 @@ +class A: + def foo(self): + pass + def abc(self): + pass \ No newline at end of file diff --git a/python/testData/breadcrumbs/functionNewLineSpaceCaretSpaceFunction_crumbs.txt b/python/testData/breadcrumbs/functionNewLineSpaceCaretSpaceFunction_crumbs.txt new file mode 100644 index 000000000000..ef0370e0adab --- /dev/null +++ b/python/testData/breadcrumbs/functionNewLineSpaceCaretSpaceFunction_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +A +Tooltip: +A \ No newline at end of file diff --git a/python/testData/breadcrumbs/multiLineDictLiteralCaretAfterFirstKeyValueExpression.py b/python/testData/breadcrumbs/multiLineDictLiteralCaretAfterFirstKeyValueExpression.py new file mode 100644 index 000000000000..56a7d6b6dc09 --- /dev/null +++ b/python/testData/breadcrumbs/multiLineDictLiteralCaretAfterFirstKeyValueExpression.py @@ -0,0 +1,4 @@ +dct = { + "a": 1, + "b": 2 +} \ No newline at end of file diff --git a/python/testData/breadcrumbs/multiLineDictLiteralCaretAfterFirstKeyValueExpression_crumbs.txt b/python/testData/breadcrumbs/multiLineDictLiteralCaretAfterFirstKeyValueExpression_crumbs.txt new file mode 100644 index 000000000000..322fb118f311 --- /dev/null +++ b/python/testData/breadcrumbs/multiLineDictLiteralCaretAfterFirstKeyValueExpression_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +"a" +Tooltip: +"a" \ No newline at end of file diff --git a/python/testData/breadcrumbs/singleLineDictLiteralCaretAfterComma.py b/python/testData/breadcrumbs/singleLineDictLiteralCaretAfterComma.py new file mode 100644 index 000000000000..a9b64205ec69 --- /dev/null +++ b/python/testData/breadcrumbs/singleLineDictLiteralCaretAfterComma.py @@ -0,0 +1 @@ +dct = {"a": 1, "b": 2} \ No newline at end of file diff --git a/python/testData/breadcrumbs/singleLineDictLiteralCaretAfterComma_crumbs.txt b/python/testData/breadcrumbs/singleLineDictLiteralCaretAfterComma_crumbs.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/breadcrumbs/singleLineDictLiteralCaretAfterFirstKeyValueExpression.py b/python/testData/breadcrumbs/singleLineDictLiteralCaretAfterFirstKeyValueExpression.py new file mode 100644 index 000000000000..62b85729cad4 --- /dev/null +++ b/python/testData/breadcrumbs/singleLineDictLiteralCaretAfterFirstKeyValueExpression.py @@ -0,0 +1 @@ +dct = {"a": 1, "b": 2} \ No newline at end of file diff --git a/python/testData/breadcrumbs/singleLineDictLiteralCaretAfterFirstKeyValueExpression_crumbs.txt b/python/testData/breadcrumbs/singleLineDictLiteralCaretAfterFirstKeyValueExpression_crumbs.txt new file mode 100644 index 000000000000..322fb118f311 --- /dev/null +++ b/python/testData/breadcrumbs/singleLineDictLiteralCaretAfterFirstKeyValueExpression_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +"a" +Tooltip: +"a" \ No newline at end of file diff --git a/python/testData/breadcrumbs/singleLineDictLiteralCaretAfterSecondKeyValueExpression.py b/python/testData/breadcrumbs/singleLineDictLiteralCaretAfterSecondKeyValueExpression.py new file mode 100644 index 000000000000..d22e702ac9f5 --- /dev/null +++ b/python/testData/breadcrumbs/singleLineDictLiteralCaretAfterSecondKeyValueExpression.py @@ -0,0 +1 @@ +dct = {"a": 1, "b": 2} \ No newline at end of file diff --git a/python/testData/breadcrumbs/singleLineDictLiteralCaretAfterSecondKeyValueExpression_crumbs.txt b/python/testData/breadcrumbs/singleLineDictLiteralCaretAfterSecondKeyValueExpression_crumbs.txt new file mode 100644 index 000000000000..bb72e77e46f1 --- /dev/null +++ b/python/testData/breadcrumbs/singleLineDictLiteralCaretAfterSecondKeyValueExpression_crumbs.txt @@ -0,0 +1,4 @@ +Crumb: +"b" +Tooltip: +"b" \ 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 index c40c2a0f3a7f..ad6f0d6becf5 100644 --- a/python/testSrc/com/jetbrains/python/breadcrumbs/PyBreadcrumbsTest.java +++ b/python/testSrc/com/jetbrains/python/breadcrumbs/PyBreadcrumbsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -147,6 +147,50 @@ public class PyBreadcrumbsTest extends PyTestCase { doTest(); } + public void testFunctionBodySpaceCaret() { + doTest(); + } + + public void testFunctionBodyCaret() { + doTest(); + } + + public void testFunctionBodySpaceCaretNewLineFunction() { + doTest(); + } + + public void testFunctionNewLineSpaceCaretNewLineFunction() { + doTest(); + } + + public void testFunctionNewLineCaretNewLineFunction() { + doTest(); + } + + public void testFunctionNewLineSpaceCaretSpaceFunction() { + doTest(); + } + + public void testFunctionNewLineCaretSpaceFunction() { + doTest(); + } + + public void testMultiLineDictLiteralCaretAfterFirstKeyValueExpression() { + doTest(); + } + + public void testSingleLineDictLiteralCaretAfterFirstKeyValueExpression() { + doTest(); + } + + public void testSingleLineDictLiteralCaretAfterComma() { + doTest(); + } + + public void testSingleLineDictLiteralCaretAfterSecondKeyValueExpression() { + doTest(); + } + private void doTest() { final String testName = getTestName(true); diff --git a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java index bcc7fbd998f7..bc27fb1a3a5b 100644 --- a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java +++ b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java @@ -291,7 +291,7 @@ public class BreadcrumbsXmlWrapper implements BreadcrumbsItemListener