From cb2e4d4e9bdc43fb898c5740eaf2b8455e212606 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 20 Apr 2011 18:48:21 +0200 Subject: [PATCH] completion of tag names in epydoc and sphinx docstrings --- python/src/META-INF/python-plugin-common.xml | 1 + .../DocStringReferenceContributor.java | 5 +- .../DocStringTagCompletionContributor.java | 68 +++++++++++++++++++ .../python/documentation/EpydocString.java | 6 ++ .../python/documentation/SphinxDocString.java | 3 +- python/testData/completion/epydocTags.py | 2 + .../completion/epydocTagsMiddle.after.py | 2 + .../testData/completion/epydocTagsMiddle.py | 2 + .../python/PythonCompletionTest.java | 32 +++++++-- 9 files changed, 112 insertions(+), 9 deletions(-) create mode 100644 python/src/com/jetbrains/python/documentation/DocStringTagCompletionContributor.java create mode 100644 python/testData/completion/epydocTags.py create mode 100644 python/testData/completion/epydocTagsMiddle.after.py create mode 100644 python/testData/completion/epydocTagsMiddle.py diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index b579d19fe6ce..0baec9f75fd7 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -288,6 +288,7 @@ + diff --git a/python/src/com/jetbrains/python/documentation/DocStringReferenceContributor.java b/python/src/com/jetbrains/python/documentation/DocStringReferenceContributor.java index ba7ddcf73431..862efb976dee 100644 --- a/python/src/com/jetbrains/python/documentation/DocStringReferenceContributor.java +++ b/python/src/com/jetbrains/python/documentation/DocStringReferenceContributor.java @@ -2,9 +2,6 @@ package com.jetbrains.python.documentation; import com.intellij.psi.PsiReferenceContributor; import com.intellij.psi.PsiReferenceRegistrar; -import com.jetbrains.python.psi.PyStringLiteralExpression; - -import static com.intellij.patterns.PlatformPatterns.psiElement; /** * @author yole @@ -12,7 +9,7 @@ import static com.intellij.patterns.PlatformPatterns.psiElement; public class DocStringReferenceContributor extends PsiReferenceContributor { @Override public void registerReferenceProviders(PsiReferenceRegistrar registrar) { - registrar.registerReferenceProvider(psiElement(PyStringLiteralExpression.class), + registrar.registerReferenceProvider(DocStringTagCompletionContributor.DOCSTRING_PATTERN, new DocStringReferenceProvider()); } } diff --git a/python/src/com/jetbrains/python/documentation/DocStringTagCompletionContributor.java b/python/src/com/jetbrains/python/documentation/DocStringTagCompletionContributor.java new file mode 100644 index 000000000000..166e6b6184f9 --- /dev/null +++ b/python/src/com/jetbrains/python/documentation/DocStringTagCompletionContributor.java @@ -0,0 +1,68 @@ +package com.jetbrains.python.documentation; + +import com.intellij.codeInsight.completion.*; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.patterns.PsiElementPattern; +import com.intellij.psi.PsiFile; +import com.intellij.util.ProcessingContext; +import com.jetbrains.python.psi.PyDocStringOwner; +import com.jetbrains.python.psi.PyExpressionStatement; +import com.jetbrains.python.psi.PyStringLiteralExpression; +import org.jetbrains.annotations.NotNull; + +import static com.intellij.patterns.PlatformPatterns.psiElement; + +/** + * @author yole + */ +public class DocStringTagCompletionContributor extends CompletionContributor { + public static final PsiElementPattern.Capture DOCSTRING_PATTERN = psiElement(PyStringLiteralExpression.class) + .withParent(psiElement(PyExpressionStatement.class).inside(PyDocStringOwner.class)); + + public DocStringTagCompletionContributor() { + extend(CompletionType.BASIC, psiElement().withParent(DOCSTRING_PATTERN), + new CompletionProvider() { + @Override + protected void addCompletions(@NotNull CompletionParameters parameters, + ProcessingContext context, + @NotNull CompletionResultSet result) { + final PyDocumentationSettings settings = PyDocumentationSettings.getInstance(parameters.getPosition().getProject()); + if (settings.isEpydocFormat() || settings.isReSTFormat()) { + int offset = parameters.getOffset(); + final PsiFile file = parameters.getOriginalFile(); + final String text = file.getText(); + char prefix = settings.isEpydocFormat() ? '@' : ':'; + if (offset > 0) { + offset--; + } + StringBuilder prefixBuilder = new StringBuilder(); + while(offset > 0 && (Character.isLetterOrDigit(text.charAt(offset)) || text.charAt(offset) == prefix)) { + prefixBuilder.insert(0, text.charAt(offset)); + if (text.charAt(offset) == prefix) { + offset--; + break; + } + offset--; + } + while(offset > 0) { + offset--; + if (text.charAt(offset) == '\n' || text.charAt(offset) == '\"' || text.charAt(offset) == '\'') { + break; + } + if (!Character.isWhitespace(text.charAt(offset))) { + return; + } + } + String[] allTags = settings.isEpydocFormat() ? EpydocString.ALL_TAGS : SphinxDocString.ALL_TAGS; + if (prefixBuilder.length() > 0) { + result = result.withPrefixMatcher(prefixBuilder.toString()); + } + for (String tag : allTags) { + result.addElement(LookupElementBuilder.create(tag)); + } + } + } + } + ); + } +} diff --git a/python/src/com/jetbrains/python/documentation/EpydocString.java b/python/src/com/jetbrains/python/documentation/EpydocString.java index 74e8d059fb70..da656bca74cd 100644 --- a/python/src/com/jetbrains/python/documentation/EpydocString.java +++ b/python/src/com/jetbrains/python/documentation/EpydocString.java @@ -16,6 +16,12 @@ public class EpydocString extends StructuredDocString { public static String[] RTYPE_TAGS = new String[] { "rtype", "returntype" }; public static String[] KEYWORD_ARGUMENT_TAGS = new String[] { "keyword", "kwarg", "kwparam" }; + public static String[] ALL_TAGS = new String[] { + "@param", "@type", "@return", "@rtype", "@keyword", "@raise", "@ivar", "@cvar", "@var", "@group", "@sort", "@note", "@attention", + "@bug", "@warning", "@version", "@todo", "@deprecated", "@since", "@status", "@change", "@permission", "@requires", + "@precondition", "@postcondition", "@invariant", "@author", "@organization", "@copyright", "@license", "@contact", "@summary", "@see" + }; + public EpydocString(String docstringText) { super(docstringText, "@"); } diff --git a/python/src/com/jetbrains/python/documentation/SphinxDocString.java b/python/src/com/jetbrains/python/documentation/SphinxDocString.java index 2f17d46ae885..12d726bb634f 100644 --- a/python/src/com/jetbrains/python/documentation/SphinxDocString.java +++ b/python/src/com/jetbrains/python/documentation/SphinxDocString.java @@ -7,6 +7,7 @@ import java.util.List; */ public class SphinxDocString extends StructuredDocString { public static String[] KEYWORD_ARGUMENT_TAGS = new String[] { "keyword", "key" }; + public static String[] ALL_TAGS = new String[] { ":param", ":keyword", ":type", ":raises", ":var", ":cvar", ":ivar", ":returns", ":rtype" }; public SphinxDocString(String docstringText) { super(docstringText, ":"); @@ -44,7 +45,7 @@ public class SphinxDocString extends StructuredDocString { @Override public String getReturnDescription() { - return getTagValue("return"); + return getTagValue(EpydocString.RETURN_TAGS); } @Override diff --git a/python/testData/completion/epydocTags.py b/python/testData/completion/epydocTags.py new file mode 100644 index 000000000000..1e8f31b57433 --- /dev/null +++ b/python/testData/completion/epydocTags.py @@ -0,0 +1,2 @@ +def foo(bar): + """ """ \ No newline at end of file diff --git a/python/testData/completion/epydocTagsMiddle.after.py b/python/testData/completion/epydocTagsMiddle.after.py new file mode 100644 index 000000000000..266bb2500067 --- /dev/null +++ b/python/testData/completion/epydocTagsMiddle.after.py @@ -0,0 +1,2 @@ +def foo(bar): + """ @param """ \ No newline at end of file diff --git a/python/testData/completion/epydocTagsMiddle.py b/python/testData/completion/epydocTagsMiddle.py new file mode 100644 index 000000000000..36abe1aa7110 --- /dev/null +++ b/python/testData/completion/epydocTagsMiddle.py @@ -0,0 +1,2 @@ +def foo(bar): + """ @pa """ \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index 7e371c6d0a1e..f57ec561514e 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -1,10 +1,8 @@ -/* - * User: anna - * Date: 06-Mar-2008 - */ package com.jetbrains.python; import com.intellij.codeInsight.lookup.LookupElement; +import com.jetbrains.python.documentation.DocStringFormat; +import com.jetbrains.python.documentation.PyDocumentationSettings; import com.jetbrains.python.fixtures.PyLightFixtureTestCase; import com.jetbrains.python.psi.LanguageLevel; import com.jetbrains.python.psi.impl.PythonLanguageLevelPusher; @@ -324,4 +322,30 @@ public class PythonCompletionTest extends PyLightFixtureTestCase { public void testEpydocParamTag() { doTest(); } + + public void testEpydocTags() { + final PyDocumentationSettings settings = PyDocumentationSettings.getInstance(myFixture.getProject()); + settings.setFormat(DocStringFormat.EPYTEXT); + try { + myFixture.configureByFile("completion/epydocTags.py"); + myFixture.completeBasic(); + assertTrue(myFixture.getLookupElementStrings().contains("@param")); + } + finally { + settings.setFormat(DocStringFormat.PLAIN); + } + } + + public void testEpydocTagsMiddle() { + final PyDocumentationSettings settings = PyDocumentationSettings.getInstance(myFixture.getProject()); + settings.setFormat(DocStringFormat.EPYTEXT); + try { + myFixture.configureByFile("completion/epydocTagsMiddle.py"); + myFixture.completeBasic(); + myFixture.checkResultByFile("completion/epydocTagsMiddle.after.py"); + } + finally { + settings.setFormat(DocStringFormat.PLAIN); + } + } }