EA-75111 Handle projects with no modules and, hence, no configured docstring format
authorMikhail Golubev <mikhail.golubev@jetbrains.com>
Wed, 16 Nov 2016 12:39:08 +0000 (15:39 +0300)
committerMikhail Golubev <mikhail.golubev@jetbrains.com>
Wed, 16 Nov 2016 12:49:40 +0000 (15:49 +0300)
It can happen in IDEA projects that contains no modules or, presumably,
in PyCharm projects which settings have been corrupted for some reason.
DocStringFormat.PLAIN is used in some cases as a fallback format.

python/src/com/jetbrains/python/documentation/docstrings/DocStringUtil.java
python/src/com/jetbrains/python/documentation/docstrings/PyDocstringGenerator.java
python/src/com/jetbrains/python/psi/impl/PyFunctionBuilder.java

index 7ffcd24ac7425364a79339af5f36d8a3dfb42530..e29e68898b100fbac349f0818ce77b003f1fb8cb 100644 (file)
@@ -27,6 +27,7 @@ import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.util.PsiTreeUtil;
 import com.intellij.util.ArrayUtil;
+import com.intellij.util.ObjectUtils;
 import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
 import com.jetbrains.python.documentation.PyDocumentationSettings;
 import com.jetbrains.python.psi.*;
@@ -172,7 +173,7 @@ public class DocStringUtil {
   @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;
+    return guessed == DocStringFormat.PLAIN && anchor != null ? getConfiguredDocStringFormatOrPlain(anchor) : guessed;
   }
 
   /**
@@ -180,12 +181,22 @@ public class DocStringUtil {
    * @return docstring format configured for file or module containing given anchor PSI element
    * @see PyDocumentationSettings#getFormatForFile(PsiFile)
    */
-  @NotNull
+  @Nullable
   public static DocStringFormat getConfiguredDocStringFormat(@NotNull PsiElement anchor) {
-    final PyDocumentationSettings settings = PyDocumentationSettings.getInstance(getModuleForElement(anchor));
+    final Module module = getModuleForElement(anchor);
+    if (module == null) {
+      return null;
+    }
+
+    final PyDocumentationSettings settings = PyDocumentationSettings.getInstance(module);
     return settings.getFormatForFile(anchor.getContainingFile());
   }
 
+  @NotNull
+  public static DocStringFormat getConfiguredDocStringFormatOrPlain(@NotNull PsiElement anchor) {
+    return ObjectUtils.chooseNotNull(getConfiguredDocStringFormat(anchor), DocStringFormat.PLAIN);
+  }
+
   public static boolean isLikeSphinxDocString(@NotNull String text) {
     return text.contains(":param ") || 
            text.contains(":return:") || text.contains(":returns:") || 
@@ -313,16 +324,24 @@ public class DocStringUtil {
    * @return false if no structured docstring format was specified initially and user didn't select any, true otherwise
    */
   public static boolean ensureNotPlainDocstringFormat(@NotNull PsiElement anchor) {
-    return ensureNotPlainDocstringFormatForFile(anchor.getContainingFile(), getModuleForElement(anchor));
+    final Module module = getModuleForElement(anchor);
+    if (module == null) {
+      return false;
+    }
+
+    return ensureNotPlainDocstringFormatForFile(anchor.getContainingFile(), module);
   }
 
-  @NotNull
+  // Might return {@code null} in some rare cases when PSI element doesn't have an associated module.
+  // For instance, an empty IDEA project with a Python scratch file.
+  @Nullable
   private static Module getModuleForElement(@NotNull PsiElement element) {
-    Module module = ModuleUtilCore.findModuleForPsiElement(element);
-    if (module == null) {
-      module = ModuleManager.getInstance(element.getProject()).getModules()[0];
+    final Module module = ModuleUtilCore.findModuleForPsiElement(element);
+    if (module != null) {
+      return module;
     }
-    return module;
+    
+    return ArrayUtil.getFirstElement(ModuleManager.getInstance(element.getProject()).getModules());
   }
 
   private static boolean ensureNotPlainDocstringFormatForFile(@NotNull PsiFile file, @NotNull Module module) {
index 7fc0694fba068ce7f1d01031084803a1d54da96f..11f24d7320d24520f2dea8988f4c0ac129fe2166 100644 (file)
@@ -26,7 +26,6 @@ import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.fileEditor.FileEditorManager;
 import com.intellij.openapi.fileEditor.OpenFileDescriptor;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Condition;
 import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.TextRange;
 import com.intellij.openapi.util.text.StringUtil;
@@ -92,7 +91,7 @@ public class PyDocstringGenerator {
       indentation = PyIndentUtil.getElementIndent(((PyStatementListContainer)owner).getStatementList());
     }
     final String docStringText = owner.getDocStringExpression() == null ? null : owner.getDocStringExpression().getText();
-    return new PyDocstringGenerator(owner, docStringText, DocStringUtil.getConfiguredDocStringFormat(owner), indentation, owner);
+    return new PyDocstringGenerator(owner, docStringText, DocStringUtil.getConfiguredDocStringFormatOrPlain(owner), indentation, owner);
   }
   
   /**
@@ -109,7 +108,7 @@ public class PyDocstringGenerator {
   public static PyDocstringGenerator update(@NotNull PyStringLiteralExpression docString) {
     return new PyDocstringGenerator(PsiTreeUtil.getParentOfType(docString, PyDocStringOwner.class),
                                     docString.getText(), 
-                                    DocStringUtil.getConfiguredDocStringFormat(docString),
+                                    DocStringUtil.getConfiguredDocStringFormatOrPlain(docString),
                                     PyIndentUtil.getElementIndent(docString), 
                                     docString);
   }
index e2456210d26841bfc5ee0104f4b7a57127730773..e4469a12ade43f78a769b4dc4da94b9243f3b648 100644 (file)
@@ -87,7 +87,7 @@ public class PyFunctionBuilder {
    */
   public PyFunctionBuilder(@NotNull String name, @NotNull PsiElement settingsAnchor) {
     myName = name;
-    myDocStringGenerator = PyDocstringGenerator.create(DocStringUtil.getConfiguredDocStringFormat(settingsAnchor), 
+    myDocStringGenerator = PyDocstringGenerator.create(DocStringUtil.getConfiguredDocStringFormatOrPlain(settingsAnchor), 
                                                        PyIndentUtil.getIndentFromSettings(settingsAnchor.getProject()), 
                                                        settingsAnchor);
   }