blob: 36b70ec0f76a942e5433dfc832d5d41b93d2e6ed [file] [log] [blame]
/*
* Copyright 2000-2013 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.google.common.collect.Maps;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
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.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author yole
*/
public abstract class StructuredDocStringBase implements StructuredDocString {
protected final String myDescription;
protected final Map<String, Substring> mySimpleTagValues = Maps.newHashMap();
protected final Map<String, Map<Substring, Substring>> myArgTagValues = Maps.newHashMap();
private static final Pattern RE_STRICT_TAG_LINE = Pattern.compile("([a-z]+)([^:]*| :class:[^:]*): (.*)");
private static final Pattern RE_LOOSE_TAG_LINE = Pattern.compile("([a-z]+) ([a-zA-Z_0-9]*):?([^:]*)");
private static final Pattern RE_ARG_TYPE = Pattern.compile("(.*) ([a-zA-Z_0-9]+)");
public static String[] PARAM_TAGS = new String[]{"param", "parameter", "arg", "argument"};
public static String[] PARAM_TYPE_TAGS = new String[]{"type"};
public static String[] VARIABLE_TAGS = new String[]{"ivar", "cvar", "var"};
public static String[] RAISES_TAGS = new String[]{"raises", "raise", "except", "exception"};
public static String[] RETURN_TAGS = new String[]{"return", "returns"};
@NotNull
private final String myTagPrefix;
public enum ReferenceType {PARAMETER, PARAMETER_TYPE, KEYWORD, VARIABLE, CLASS_VARIABLE, INSTANCE_VARIABLE}
public static String TYPE = "type";
protected StructuredDocStringBase(@NotNull String docStringText, String tagPrefix) {
myTagPrefix = tagPrefix;
final Substring docString = new Substring(docStringText);
final List<Substring> lines = docString.splitLines();
final int nlines = lines.size();
final StringBuilder builder = new StringBuilder();
int lineno = 0;
while (lineno < nlines) {
Substring line = lines.get(lineno).trim();
if (line.startsWith(tagPrefix)) {
lineno = parseTag(lines, lineno, tagPrefix);
}
else {
builder.append(line.toString()).append("\n");
}
lineno++;
}
myDescription = builder.toString();
}
@Override
@NotNull
public String createParameterType(@NotNull final String name, @NotNull final String type) {
return myTagPrefix + TYPE + String.format(" %s %s", name, type);
}
@Override
public String getDescription() {
return myDescription;
}
@Override
public String getSummary() {
final List<String> strings = StringUtil.split(StringUtil.trimLeading(myDescription), "\n", true, false);
if (strings.size() > 1) {
if (strings.get(1).isEmpty()) {
return strings.get(0);
}
}
return "";
}
@NotNull
private Map<Substring, Substring> getTagValuesMap(String key) {
Map<Substring, Substring> map = myArgTagValues.get(key);
if (map == null) {
map = Maps.newLinkedHashMap();
myArgTagValues.put(key, map);
}
return map;
}
protected int parseTag(List<Substring> lines, int lineno, String tagPrefix) {
final Substring lineWithPrefix = lines.get(lineno).trim();
if (lineWithPrefix.startsWith(tagPrefix)) {
final Substring line = lineWithPrefix.substring(tagPrefix.length());
final Matcher strictTagMatcher = RE_STRICT_TAG_LINE.matcher(line);
final Matcher looseTagMatcher = RE_LOOSE_TAG_LINE.matcher(line);
Matcher tagMatcher = null;
if (strictTagMatcher.matches()) {
tagMatcher = strictTagMatcher;
}
else if (looseTagMatcher.matches()) {
tagMatcher = looseTagMatcher;
}
if (tagMatcher != null) {
final Substring tagName = line.getMatcherGroup(tagMatcher, 1);
final Substring argName = line.getMatcherGroup(tagMatcher, 2).trim();
final TextRange firstArgLineRange = line.getMatcherGroup(tagMatcher, 3).trim().getTextRange();
final int linesCount = lines.size();
final int argStart = firstArgLineRange.getStartOffset();
int argEnd = firstArgLineRange.getEndOffset();
while (lineno + 1 < linesCount) {
final Substring nextLine = lines.get(lineno + 1).trim();
if (nextLine.length() == 0 || nextLine.startsWith(tagPrefix)) {
break;
}
argEnd = nextLine.getTextRange().getEndOffset();
lineno++;
}
final Substring argValue = new Substring(argName.getSuperString(), argStart, argEnd);
final String tagNameString = tagName.toString();
if (argName.length() == 0) {
mySimpleTagValues.put(tagNameString, argValue);
}
else {
if ("param".equals(tagNameString) || "parameter".equals(tagNameString) ||
"arg".equals(tagNameString) || "argument".equals(tagNameString)) {
final Matcher argTypeMatcher = RE_ARG_TYPE.matcher(argName);
if (argTypeMatcher.matches()) {
final Substring type = argName.getMatcherGroup(argTypeMatcher, 1).trim();
final Substring arg = argName.getMatcherGroup(argTypeMatcher, 2);
getTagValuesMap(TYPE).put(arg, type);
}
else {
getTagValuesMap(tagNameString).put(argName, argValue);
}
}
else {
getTagValuesMap(tagNameString).put(argName, argValue);
}
}
}
}
return lineno;
}
protected static List<String> toUniqueStrings(List<?> objects) {
final List<String> result = new ArrayList<String>(objects.size());
for (Object o : objects) {
final String s = o.toString();
if (!result.contains(s)) {
result.add(s);
}
}
return result;
}
@Override
@Nullable
public Substring getTagValue(String... tagNames) {
for (String tagName : tagNames) {
final Substring value = mySimpleTagValues.get(tagName);
if (value != null) {
return value;
}
}
return null;
}
@Override
@Nullable
public Substring getTagValue(String tagName, @NotNull String argName) {
final Map<Substring, Substring> argValues = myArgTagValues.get(tagName);
return argValues != null ? argValues.get(new Substring(argName)) : null;
}
@Override
@Nullable
public Substring getTagValue(String[] tagNames, @NotNull String argName) {
for (String tagName : tagNames) {
Map<Substring, Substring> argValues = myArgTagValues.get(tagName);
if (argValues != null) {
return argValues.get(new Substring(argName));
}
}
return null;
}
@Override
public List<Substring> getTagArguments(String... tagNames) {
for (String tagName : tagNames) {
final Map<Substring, Substring> map = myArgTagValues.get(tagName);
if (map != null) {
return new ArrayList<Substring>(map.keySet());
}
}
return Collections.emptyList();
}
@Override
public List<Substring> getParameterSubstrings() {
final List<Substring> results = new ArrayList<Substring>();
results.addAll(getTagArguments(PARAM_TAGS));
results.addAll(getTagArguments(PARAM_TYPE_TAGS));
return results;
}
@Override
@Nullable
public Substring getParamByNameAndKind(@NotNull String name, String kind) {
for (Substring s : getTagArguments(kind)) {
if (name.equals(s.getValue())) {
return s;
}
}
return null;
}
}