PY-9795 Move various indent operation on string to PyIndentUtil

This commit is contained in:
Mikhail Golubev
2015-09-02 14:35:39 +03:00
committed by Mikhail Golubev
parent 8201381038
commit 9213f503e3
2 changed files with 93 additions and 29 deletions
@@ -19,7 +19,6 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.psi.PyIndentUtil;
@@ -259,33 +258,6 @@ public abstract class SectionBasedDocString extends DocStringLineParser implemen
return line.split(":", 1);
}
// like Python's textwrap.dedent()
@NotNull
protected static String stripCommonIndent(@NotNull Substring text, boolean ignoreFirstStringIfNonEmpty) {
final List<Substring> lines = text.splitLines();
if (lines.isEmpty()) {
return "";
}
final String firstLine = lines.get(0).toString();
final boolean skipFirstLine = ignoreFirstStringIfNonEmpty && !StringUtil.isEmptyOrSpaces(firstLine);
final Iterable<Substring> workList = lines.subList(skipFirstLine ? 1 : 0, lines.size());
int curMinIndent = Integer.MAX_VALUE;
for (Substring line : workList) {
if (StringUtil.isEmptyOrSpaces(line)) {
continue;
}
curMinIndent = Math.min(curMinIndent, PyIndentUtil.getLineIndentSize(line));
}
final int minIndent = curMinIndent;
final List<String> dedentedLines = ContainerUtil.map(workList, new Function<Substring, String>() {
@Override
public String fun(Substring line) {
return line.substring(Math.min(line.length(), minIndent)).toString();
}
});
return StringUtil.join(skipFirstLine ? ContainerUtil.prepend(dedentedLines, firstLine) : dedentedLines, "\n");
}
@NotNull
public List<Section> getSections() {
return Collections.unmodifiableList(mySections);
@@ -624,7 +596,7 @@ public abstract class SectionBasedDocString extends DocStringLineParser implemen
@NotNull
public String getDescription() {
return myDescription == null ? "" : stripCommonIndent(myDescription, true);
return myDescription == null ? "" : PyIndentUtil.removeCommonIndent(myDescription.getValue(), true);
}
@Nullable
@@ -15,7 +15,9 @@
*/
package com.jetbrains.python.psi;
import com.google.common.collect.Iterables;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.LineTokenizer;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
@@ -23,9 +25,14 @@ import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PythonFileType;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
/**
* @author Mikhail Golubev
*/
@@ -99,4 +106,89 @@ public class PyIndentUtil {
public static String getIndentFromSettings(@NotNull Project project) {
return StringUtil.repeatSymbol(' ', getIndentSizeFromSettings(project));
}
@NotNull
public static List<String> removeCommonIndent(@NotNull Iterable<String> lines, boolean ignoreFirstLine) {
return changeIndent(lines, ignoreFirstLine, "");
}
@NotNull
public static String removeCommonIndent(@NotNull String s, boolean ignoreFirstLine) {
final List<String> trimmed = removeCommonIndent(LineTokenizer.tokenizeIntoList(s, false), ignoreFirstLine);
return StringUtil.join(trimmed, "\n");
}
@NotNull
public static String changeIndent(@NotNull String s, boolean ignoreFirstLine, String newIndent) {
final List<String> trimmed = changeIndent(LineTokenizer.tokenizeIntoList(s, false), ignoreFirstLine, newIndent);
return StringUtil.join(trimmed, "\n");
}
/**
* Not that all empty lines will be trimmed.
*/
@NotNull
public static List<String> changeIndent(@NotNull Iterable<String> lines, boolean ignoreFirstLine, final String newIndent) {
final String oldIndent = findCommonIndent(lines, ignoreFirstLine);
if (Iterables.isEmpty(lines)) {
return Collections.emptyList();
}
final List<String> result = ContainerUtil.map(Iterables.skip(lines, ignoreFirstLine ? 1 : 0), new Function<String, String>() {
@Override
public String fun(String line) {
if (StringUtil.isEmptyOrSpaces(line)) {
return "";
}
else {
return newIndent + line.substring(oldIndent.length());
}
}
});
if (ignoreFirstLine) {
return ContainerUtil.prepend(result, Iterables.get(lines, 0));
}
return result;
}
/**
* If lines include non-empty lines, all empty lines or lines that contain only spaces are ignored.
* Otherwise (all line are empty) their common indent are returned as expected. If any two lines
* have different indentation (e.g. one contains tab character and another doesn't), empty prefix
* is returned.
*/
@NotNull
public static String findCommonIndent(@NotNull Iterable<String> lines, boolean ignoreFirstLine) {
String minIndent = null;
boolean allLinesEmpty = true;
if (Iterables.isEmpty(lines)) {
return "";
}
boolean hasBadEmptyLineIndent = false;
for (String line : Iterables.skip(lines, ignoreFirstLine ? 1 : 0)) {
final boolean lineEmpty = StringUtil.isEmptyOrSpaces(line);
if (lineEmpty && !allLinesEmpty) {
continue;
}
final String indent = (String)getLineIndent(line);
if (minIndent == null || (!lineEmpty && allLinesEmpty) || minIndent.startsWith(indent)) {
minIndent = indent;
}
else if (!indent.startsWith(minIndent)) {
if (lineEmpty) {
hasBadEmptyLineIndent = true;
}
else {
return "";
}
}
allLinesEmpty &= lineEmpty;
}
if (allLinesEmpty && hasBadEmptyLineIndent) {
return "";
}
return StringUtil.notNullize(minIndent);
}
}