final List<PsiReference> result = new ArrayList<PsiReference>();
final int offset = ranges.get(0).getStartOffset();
// XXX: It does not work with multielement docstrings
- StructuredDocString docString = DocStringUtil.parse(text);
+ StructuredDocString docString = DocStringUtil.parse(text, element);
if (docString instanceof TagBasedDocString) {
final TagBasedDocString taggedDocString = (TagBasedDocString)docString;
result.addAll(referencesFromNames(expr, offset, docString,
return PyPsiUtils.strValue(owner.getDocStringExpression());
}
+ /**
+ * Attempts to detect docstring format from given text and parses it into corresponding structured docstring.
+ * It's recommended to use more reliable {@link #parse(String, PsiElement)} that fallbacks to format specified in settings.
+ *
+ * @return structured docstring for one of supported formats or instance of {@link PlainDocString} if none was recognized.
+ * @see #parse(String, PsiElement)
+ */
@Nullable
- public static StructuredDocString parse(@Nullable String text) {
- if (text == null) {
- return null;
- }
- if (isSphinxDocString(text)) {
- return parseDocStringContent(DocStringFormat.REST, text);
- }
- if (isGoogleDocString(text)) {
- return parseDocStringContent(DocStringFormat.GOOGLE, text);
- }
- if (isNumpyDocstring(text)) {
- return parseDocStringContent(DocStringFormat.NUMPY, text);
- }
- return parseDocStringContent(DocStringFormat.EPYTEXT, text);
+ public static StructuredDocString parse(@NotNull String text) {
+ return parse(text, null);
}
-
+ /**
+ * Attempts to detects docstring format first from given text, next from settings and parses text into corresponding structured docstring.
+ *
+ * @return structured docstring for one of supported formats or instance of {@link PlainDocString} if none was recognized.
+ * @see DocStringFormat#ALL_NAMES_BUT_PLAIN
+ * @see #guessDocStringFormat(String, PsiElement)
+ */
+ @NotNull
+ public static StructuredDocString parse(@NotNull String text, @Nullable PsiElement anchor) {
+ final DocStringFormat format = guessDocStringFormat(text, anchor);
+ return parseDocStringContent(format, text);
+ }
+
@NotNull
public static StructuredDocString parseDocString(@NotNull DocStringFormat format,
@NotNull PyStringLiteralExpression literalExpression) {
case NUMPY:
return new NumpyDocString(content);
default:
- throw new UnsupportedOperationException("Not supported for plain docstrings. Use PyDocStringUtil#ensureNotPlainDocstringFormat");
+ return new PlainDocString(content);
}
}
final TextRange contentRange = PyStringLiteralExpressionImpl.getNodeTextRange(text);
return new Substring(text, contentRange.getStartOffset(), contentRange.getEndOffset());
}
+
+ /**
+ * @return docstring format inferred heuristically solely from its content. For more reliable result use anchored version
+ * {@link #guessDocStringFormat(String, PsiElement)} of this method.
+ * @see #guessDocStringFormat(String, PsiElement)
+ */
+ @NotNull
+ public static DocStringFormat guessDocStringFormat(@NotNull String text) {
+ if (isLikeNumpyDocstring(text)) {
+ return DocStringFormat.NUMPY;
+ }
+ if (isLikeGoogleDocString(text)) {
+ return DocStringFormat.GOOGLE;
+ }
+ if (isLikeEpydocDocString(text)) {
+ return DocStringFormat.EPYTEXT;
+ }
+ if (isLikeSphinxDocString(text)) {
+ return DocStringFormat.REST;
+ }
+ return DocStringFormat.PLAIN;
+ }
+
+ /**
+ * @return docstring inferred heuristically and if unsuccessful fallback to configured format retrieved from anchor PSI element
+ * @see #getConfiguredDocStringFormat(PsiElement)
+ */
+ @NotNull
+ public static DocStringFormat guessDocStringFormat(@NotNull String text, @Nullable PsiElement anchor) {
+ final DocStringFormat guessed = guessDocStringFormat(text);
+ return guessed == DocStringFormat.PLAIN && anchor != null ? getConfiguredDocStringFormat(anchor) : guessed;
+ }
- public static boolean isSphinxDocString(@NotNull String text) {
+ /**
+ * @return docstring format configured for file or module containing given anchor PSI element
+ * @see PyDocumentationSettings#getFormatForFile(PsiFile)
+ */
+ @NotNull
+ public static DocStringFormat getConfiguredDocStringFormat(@NotNull PsiElement anchor) {
+ final PyDocumentationSettings settings = PyDocumentationSettings.getInstance(getModuleForElement(anchor));
+ return settings.getFormatForFile(anchor.getContainingFile());
+ }
+
+ public static boolean isLikeSphinxDocString(@NotNull String text) {
return text.contains(":param ") || text.contains(":rtype") || text.contains(":type");
}
- public static boolean isEpydocDocString(@NotNull String text) {
+ public static boolean isLikeEpydocDocString(@NotNull String text) {
return text.contains("@param ") || text.contains("@rtype") || text.contains("@type");
}
- public static boolean isGoogleDocString(@NotNull String text) {
+ public static boolean isLikeGoogleDocString(@NotNull String text) {
for (@NonNls String title : StringUtil.findMatches(text, GoogleCodeStyleDocString.SECTION_HEADER, 1)) {
if (SectionBasedDocString.SECTION_NAMES.contains(title.toLowerCase())) {
return true;
return false;
}
- public static boolean isNumpyDocstring(@NotNull String text) {
+ public static boolean isLikeNumpyDocstring(@NotNull String text) {
final String[] lines = StringUtil.splitByLines(text, false);
for (int i = 0; i < lines.length; i++) {
final String line = lines[i];
return null;
}
- public static StructuredDocString getStructuredDocString(PyDocStringOwner owner) {
- return parse(owner.getDocStringValue());
+ @Nullable
+ public static StructuredDocString getStructuredDocString(@NotNull PyDocStringOwner owner) {
+ final String value = owner.getDocStringValue();
+ return value == null ? null : parse(value, owner);
}
public static boolean isDocStringExpression(@Nullable PyExpression expression) {
}
return true;
}
-
- @NotNull
- public static DocStringFormat getDocStringFormat(@NotNull PsiElement anchor) {
- final PyDocumentationSettings settings = PyDocumentationSettings.getInstance(getModuleForElement(anchor));
- return settings.getFormatForFile(anchor.getContainingFile());
- }
}
--- /dev/null
+/*
+ * Copyright 2000-2015 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.documentation;
+
+import com.jetbrains.python.psi.PyIndentUtil;
+import com.jetbrains.python.psi.StructuredDocString;
+import com.jetbrains.python.toolbox.Substring;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * Stub docstring that is capable only of extracting summary and description
+ * @author Mikhail Golubev
+ */
+public class PlainDocString extends DocStringLineParser implements StructuredDocString {
+ private final String mySummary;
+ private final String myDescription;
+
+ public PlainDocString(@NotNull Substring content) {
+ super(content);
+ if (!isEmpty(0) && isEmptyOrDoesNotExist(1)) {
+ mySummary = getLine(0).trim().toString();
+ final int next = skipEmptyLines(1);
+ if (next != 1) {
+ final String remaining = getLine(next).union(getLine(getLineCount() - 1)).toString();
+ myDescription = PyIndentUtil.removeCommonIndent(remaining, false);
+ }
+ else {
+ myDescription = "";
+ }
+ }
+ else {
+ mySummary = "";
+ myDescription = PyIndentUtil.removeCommonIndent(myDocStringContent.toString(), true);
+ }
+ }
+
+ @Override
+ public String getSummary() {
+ return mySummary;
+ }
+
+ @Override
+ public String getDescription() {
+ return myDescription;
+ }
+
+ @Override
+ protected boolean isBlockEnd(int lineNum) {
+ return false;
+ }
+
+ @NotNull
+ @Override
+ public String createParameterType(@NotNull String name, @NotNull String type) {
+ return "";
+ }
+
+ @Override
+ public List<String> getParameters() {
+ return null;
+ }
+
+ @Override
+ public List<Substring> getParameterSubstrings() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String getParamType(@Nullable String paramName) {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public Substring getParamTypeSubstring(@Nullable String paramName) {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String getParamDescription(@Nullable String paramName) {
+ return null;
+ }
+
+ @Override
+ public List<String> getKeywordArguments() {
+ return null;
+ }
+
+ @Override
+ public List<Substring> getKeywordArgumentSubstrings() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String getKeywordArgumentDescription(@Nullable String paramName) {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String getReturnType() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public Substring getReturnTypeSubstring() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String getReturnDescription() {
+ return null;
+ }
+
+ @Override
+ public List<String> getRaisedExceptions() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String getRaisedExceptionDescription(@Nullable String exceptionName) {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String getAttributeDescription() {
+ return null;
+ }
+}
indentation = PyIndentUtil.getElementIndent(((PyStatementListContainer)owner).getStatementList());
}
final String docStringText = owner.getDocStringExpression() == null ? null : owner.getDocStringExpression().getText();
- return new PyDocstringGenerator(owner, docStringText, DocStringUtil.getDocStringFormat(owner), indentation);
+ return new PyDocstringGenerator(owner, docStringText, DocStringUtil.getConfiguredDocStringFormat(owner), indentation);
}
@NotNull
@NotNull
public static PyDocstringGenerator update(@NotNull PyStringLiteralExpression docString) {
return new PyDocstringGenerator(PsiTreeUtil.getParentOfType(docString, PyDocStringOwner.class),
- docString.getText(), DocStringUtil.getDocStringFormat(docString),
+ docString.getText(), DocStringUtil.getConfiguredDocStringFormat(docString),
PyIndentUtil.getElementIndent(docString));
}
}
private static Pair<String, String> getTypeAndDescr(String docString, @NotNull PyNamedParameter followed) {
- final StructuredDocString structuredDocString = DocStringUtil.parse(docString);
+ final StructuredDocString structuredDocString = docString != null ? DocStringUtil.parse(docString, followed) : null;
String type = null;
String desc = null;
if (structuredDocString != null) {
module = modules[0];
}
if (module == null) return Lists.newArrayList();
- final PyDocumentationSettings documentationSettings = PyDocumentationSettings.getInstance(module);
final List<String> result = new ArrayList<String>();
final String[] lines = PyDocumentationBuilder.removeCommonIndentation(docstring);
final String formatter;
final StructuredDocString structuredDocString;
- if (documentationSettings.isGoogleFormat(element.getContainingFile()) || DocStringUtil.isGoogleDocString(preparedDocstring)) {
+ final DocStringFormat format = DocStringUtil.guessDocStringFormat(preparedDocstring, element);
+ if (format == DocStringFormat.GOOGLE) {
formatter = PythonHelpersLocator.getHelperPath("google_formatter.py");
structuredDocString = DocStringUtil.parseDocString(DocStringFormat.GOOGLE, preparedDocstring);
}
- else if (documentationSettings.isNumpyFormat(element.getContainingFile()) || DocStringUtil.isNumpyDocstring(preparedDocstring)) {
+ else if (format == DocStringFormat.NUMPY) {
formatter = PythonHelpersLocator.getHelperPath("numpy_formatter.py");
structuredDocString = DocStringUtil.parseDocString(DocStringFormat.NUMPY, preparedDocstring);
}
- else if (documentationSettings.isEpydocFormat(element.getContainingFile()) || DocStringUtil.isEpydocDocString(preparedDocstring)) {
+ else if (format == DocStringFormat.EPYTEXT) {
formatter = PythonHelpersLocator.getHelperPath("epydoc_formatter.py");
structuredDocString = DocStringUtil.parseDocString(DocStringFormat.EPYTEXT, preparedDocstring);
result.add(formatStructuredDocString(structuredDocString));
}
- else if (documentationSettings.isReSTFormat(element.getContainingFile()) || DocStringUtil.isSphinxDocString(preparedDocstring)) {
+ else if (format == DocStringFormat.REST) {
formatter = PythonHelpersLocator.getHelperPath("rest_formatter.py");
structuredDocString = DocStringUtil.parseDocString(DocStringFormat.REST, preparedDocstring);
}
String summary = "";
final PyStringLiteralExpression docStringExpression = func.getDocStringExpression();
if (docStringExpression != null) {
- final StructuredDocString docString = DocStringUtil.parse(docStringExpression.getStringValue());
- if (docString != null) {
- summary = docString.getSummary();
- }
+ final StructuredDocString docString = DocStringUtil.parse(docStringExpression.getStringValue(), docStringExpression);
+ summary = docString.getSummary();
}
return $(cat.toString()).add(describeDecorators(func, LSame2, ", ", LSame1)).add(describeFunction(func, LSame2, LSame1))
.toString() + "\n" + summary;
}
}
if (docStringExpression != null) {
- final StructuredDocString docString = DocStringUtil.parse(docStringExpression.getStringValue());
- if (docString != null) {
- summary = docString.getSummary();
- }
+ final StructuredDocString docString = DocStringUtil.parse(docStringExpression.getStringValue(), docStringExpression);
+ summary = docString.getSummary();
}
return describeDecorators(cls, LSame2, ", ", LSame1).add(describeClass(cls, LSame2, false, false)).toString() + "\n" + summary;
return false;
}
- StructuredDocString docString = DocStringUtil.parse(text);
+ StructuredDocString docString = DocStringUtil.parse(text, node);
if (docString == null) {
return false;
import com.jetbrains.python.debugger.PySignatureCacheManager;
import com.jetbrains.python.debugger.PySignatureUtil;
import com.jetbrains.python.documentation.DocStringUtil;
-import com.jetbrains.python.psi.StructuredDocString;
-import com.jetbrains.python.toolbox.Substring;
import com.jetbrains.python.psi.PyElementGenerator;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.PyStringLiteralExpression;
+import com.jetbrains.python.psi.StructuredDocString;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.PyTypeChecker;
import com.jetbrains.python.psi.types.PyTypeParser;
+import com.jetbrains.python.toolbox.Substring;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
return;
}
- StructuredDocString docString = DocStringUtil.parse(text);
+ StructuredDocString docString = DocStringUtil.parse(text, function);
if (docString == null) {
return;
}
public static PyType getTypeFromComment(PyTargetExpressionImpl targetExpression) {
String docComment = DocStringUtil.getAttributeDocComment(targetExpression);
if (docComment != null) {
- StructuredDocString structuredDocString = DocStringUtil.parse(docComment);
+ StructuredDocString structuredDocString = DocStringUtil.parse(docComment, targetExpression);
if (structuredDocString != null) {
String typeName = structuredDocString.getParamType(null);
if (typeName == null) {
// PY-16766
public void testGoogleDocStringContentDetection() {
- assertTrue(DocStringUtil.isGoogleDocString(
+ assertTrue(DocStringUtil.isLikeGoogleDocString(
"\n" +
" My Section:\n" +
" some user defined section\n" +