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);
+ }
+ }
}