2 * Copyright 2000-2014 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.jetbrains.python.documentation.docstrings;
18 import com.google.common.collect.Lists;
19 import com.intellij.execution.configurations.GeneralCommandLine;
20 import com.intellij.execution.process.ProcessOutput;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.module.Module;
23 import com.intellij.openapi.module.ModuleManager;
24 import com.intellij.openapi.module.ModuleUtilCore;
25 import com.intellij.openapi.projectRoots.Sdk;
26 import com.intellij.openapi.vfs.encoding.EncodingProjectManager;
27 import com.intellij.psi.PsiElement;
28 import com.jetbrains.python.HelperPackage;
29 import com.jetbrains.python.PythonHelper;
30 import com.jetbrains.python.psi.PyIndentUtil;
31 import com.jetbrains.python.psi.StructuredDocString;
32 import com.jetbrains.python.sdk.PySdkUtil;
33 import com.jetbrains.python.sdk.PythonEnvUtil;
34 import com.jetbrains.python.sdk.PythonSdkType;
35 import com.jetbrains.python.toolbox.Substring;
36 import org.jetbrains.annotations.NotNull;
37 import org.jetbrains.annotations.Nullable;
40 import java.nio.ByteBuffer;
41 import java.nio.charset.Charset;
42 import java.util.ArrayList;
43 import java.util.HashMap;
44 import java.util.List;
50 public class PyStructuredDocstringFormatter {
51 private static final Logger LOG = Logger.getInstance(PyStructuredDocstringFormatter.class);
53 private PyStructuredDocstringFormatter() {
57 public static List<String> formatDocstring(@NotNull final PsiElement element, @NotNull final String docstring) {
58 Module module = ModuleUtilCore.findModuleForPsiElement(element);
60 final Module[] modules = ModuleManager.getInstance(element.getProject()).getModules();
61 if (modules.length == 0) return Lists.newArrayList();
64 if (module == null) return Lists.newArrayList();
65 final List<String> result = new ArrayList<String>();
67 final String preparedDocstring = PyIndentUtil.removeCommonIndent(docstring, true).trim();
69 final HelperPackage formatter;
70 final StructuredDocString structuredDocString;
71 final DocStringFormat format = DocStringUtil.guessDocStringFormat(preparedDocstring, element);
72 if (format == DocStringFormat.GOOGLE) {
73 formatter = PythonHelper.GOOGLE_FORMATTER;
74 structuredDocString = DocStringUtil.parseDocString(DocStringFormat.GOOGLE, preparedDocstring);
76 else if (format == DocStringFormat.NUMPY) {
77 formatter = PythonHelper.NUMPY_FORMATTER;
78 structuredDocString = DocStringUtil.parseDocString(DocStringFormat.NUMPY, preparedDocstring);
80 else if (format == DocStringFormat.EPYTEXT) {
81 formatter = PythonHelper.EPYDOC_FORMATTER;
82 structuredDocString = DocStringUtil.parseDocString(DocStringFormat.EPYTEXT, preparedDocstring);
83 result.add(formatStructuredDocString(structuredDocString));
85 else if (format == DocStringFormat.REST) {
86 formatter = PythonHelper.REST_FORMATTER;
87 structuredDocString = DocStringUtil.parseDocString(DocStringFormat.REST, preparedDocstring);
94 final String output = runExternalTool(module, formatter, preparedDocstring);
96 result.add(0, output);
99 result.add(0, structuredDocString.getDescription());
106 private static String runExternalTool(@NotNull final Module module,
107 @NotNull final HelperPackage formatter,
108 @NotNull final String docstring) {
109 final Sdk sdk = PythonSdkType.findPython2Sdk(module);
110 if (sdk == null) return null;
112 final String sdkHome = sdk.getHomePath();
113 if (sdkHome == null) return null;
115 final Charset charset = EncodingProjectManager.getInstance(module.getProject()).getDefaultCharset();
117 final ByteBuffer encoded = charset.encode(docstring);
118 final byte[] data = new byte[encoded.limit()];
121 final Map<String, String> env = new HashMap<String, String>();
122 PythonEnvUtil.setPythonDontWriteBytecode(env);
124 final GeneralCommandLine commandLine = formatter.newCommandLine(sdkHome, Lists.<String>newArrayList());
125 LOG.debug("Command for launching docstring formatter: " + commandLine.getCommandLineString());
127 final ProcessOutput output = PySdkUtil.getProcessOutput(commandLine, new File(sdkHome).getParent(), env, 5000, data, true);
128 if (!output.checkSuccess(LOG)) {
131 return output.getStdout();
134 private static String formatStructuredDocString(@NotNull final StructuredDocString docString) {
135 final StringBuilder result = new StringBuilder();
137 final String attributeDescription = docString.getAttributeDescription();
138 if (attributeDescription != null) {
139 result.append(attributeDescription);
140 final String attrType = docString.getParamType(null);
141 if (attrType != null) {
142 result.append(" <i>Type: ").append(attrType).append("</i>");
146 formatParameterDescriptions(docString, result, false);
147 formatParameterDescriptions(docString, result, true);
149 final String returnDescription = docString.getReturnDescription();
150 final String returnType = docString.getReturnType();
151 if (returnDescription != null || returnType != null) {
152 result.append("<br><b>Return value:</b><br>");
153 if (returnDescription != null) {
154 result.append(returnDescription);
156 if (returnType != null) {
157 result.append(" <i>Type: ").append(returnType).append("</i>");
161 final List<String> raisedException = docString.getRaisedExceptions();
162 if (raisedException.size() > 0) {
163 result.append("<br><b>Raises:</b><br>");
164 for (String s : raisedException) {
165 result.append("<b>").append(s).append("</b> - ").append(docString.getRaisedExceptionDescription(s)).append("<br>");
169 if (docString instanceof TagBasedDocString) {
170 final TagBasedDocString taggedDocString = (TagBasedDocString)docString;
171 final List<String> additionalTags = taggedDocString.getAdditionalTags();
172 if (!additionalTags.isEmpty()) {
173 result.append("<br/><br/><b>Additional:</b><br/>");
174 result.append("<table>");
175 for (String tagName : additionalTags) {
176 final List<Substring> args = taggedDocString.getTagArguments(tagName);
177 for (Substring arg : args) {
178 final String s = arg.toString();
179 result.append("<tr><td align=\"right\"><b>").append(tagName);
180 result.append(" ").append(s).append(":</b>");
181 result.append("</td><td>").append(taggedDocString.getTagValue(tagName, s)).append("</td></tr>");
183 result.append("</table>");
187 return result.toString();
190 private static void formatParameterDescriptions(@NotNull final StructuredDocString docString,
191 @NotNull final StringBuilder result,
193 final List<String> parameters = keyword ? docString.getKeywordArguments() : docString.getParameters();
194 if (parameters.size() > 0) {
195 result.append("<br><b>").append(keyword ? "Keyword arguments:" : "Parameters").append("</b><br>");
196 for (String parameter : parameters) {
197 final String description = keyword ? docString.getKeywordArgumentDescription(parameter) : docString.getParamDescription(parameter);
198 result.append("<b>");
199 result.append(parameter);
200 result.append("</b>: ");
201 if (description != null) {
202 result.append(description);
204 final String paramType = docString.getParamType(parameter);
205 if (paramType != null) {
206 result.append(" <i>Type: ").append(paramType).append("</i>");
208 result.append("<br>");