mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Support updates of tag bases docstrings. All existing tests pass
This commit is contained in:
@@ -174,7 +174,7 @@ public class Substring implements CharSequence {
|
||||
}
|
||||
|
||||
public boolean endsWith(@NotNull String prefix) {
|
||||
return myString.lastIndexOf(prefix) == length();
|
||||
return myString.lastIndexOf(prefix) == length() - prefix.length();
|
||||
}
|
||||
|
||||
public int indexOf(@NotNull String s) {
|
||||
|
||||
@@ -36,8 +36,13 @@ public abstract class DocStringLineParser {
|
||||
myLines = Collections.unmodifiableList(content.splitLines());
|
||||
}
|
||||
|
||||
protected static int getIndent(@NotNull CharSequence line) {
|
||||
return PyIndentUtil.getLineIndentSize(line);
|
||||
public int getLineIndentSize(int lineNum) {
|
||||
return PyIndentUtil.getLineIndentSize(getLine(lineNum));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getLineIndent(int lineNum) {
|
||||
return PyIndentUtil.getLineIndent(getLine(lineNum)).toString();
|
||||
}
|
||||
|
||||
public boolean isEmptyOrDoesNotExist(int lineNum) {
|
||||
@@ -53,10 +58,6 @@ public abstract class DocStringLineParser {
|
||||
return myLines.get(lineNum);
|
||||
}
|
||||
|
||||
public int getLineIndent(int lineNum) {
|
||||
return getIndent(myLines.get(lineNum));
|
||||
}
|
||||
|
||||
public int getLineByOffset(int offset) {
|
||||
return StringUtil.countNewLines(myDocStringContent.subSequence(0, offset));
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ public class GoogleCodeStyleDocString extends SectionBasedDocString {
|
||||
}
|
||||
description = colonSeparatedParts.get(1);
|
||||
// parse line with indentation at least one space greater than indentation of the field
|
||||
final Pair<List<Substring>, Integer> pair = parseIndentedBlock(lineNum + 1, getIndent(line), sectionIndent);
|
||||
final Pair<List<Substring>, Integer> pair = parseIndentedBlock(lineNum + 1, getLineIndentSize(lineNum), sectionIndent);
|
||||
final List<Substring> nestedBlock = pair.getFirst();
|
||||
if (!nestedBlock.isEmpty()) {
|
||||
//noinspection ConstantConditions
|
||||
|
||||
@@ -78,7 +78,7 @@ public class NumpyDocString extends SectionBasedDocString {
|
||||
type = name;
|
||||
name = null;
|
||||
}
|
||||
final Pair<List<Substring>, Integer> parsedDescription = parseIndentedBlock(lineNum + 1, getIndent(line), sectionIndent);
|
||||
final Pair<List<Substring>, Integer> parsedDescription = parseIndentedBlock(lineNum + 1, getLineIndentSize(lineNum), sectionIndent);
|
||||
final List<Substring> descriptionLines = parsedDescription.getFirst();
|
||||
if (!descriptionLines.isEmpty()) {
|
||||
description = mergeSubstrings(descriptionLines.get(0), descriptionLines.get(descriptionLines.size() - 1));
|
||||
|
||||
@@ -28,11 +28,11 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
|
||||
import com.jetbrains.python.documentation.docstrings.DocStringProvider;
|
||||
import com.jetbrains.python.documentation.docstrings.TagBasedDocStringBuilder;
|
||||
import com.jetbrains.python.documentation.docstrings.TagBasedDocStringUpdater;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.toolbox.Substring;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -46,36 +46,25 @@ import java.util.List;
|
||||
*/
|
||||
public class PyDocstringGenerator {
|
||||
|
||||
@NotNull
|
||||
private PyDocStringOwner myDocStringOwner;
|
||||
@Nullable
|
||||
private PyFunction myFunction;
|
||||
|
||||
private final List<DocstringParam> myParams = Lists.newArrayList();
|
||||
private final Project myProject;
|
||||
private PyStringLiteralExpression myDocStringExpression;
|
||||
|
||||
private final PsiFile myFile;
|
||||
private boolean myGenerateReturn;
|
||||
|
||||
private final StructuredDocString myStructuredDocString;
|
||||
// Updated after buildAndInsert
|
||||
@NotNull
|
||||
private PyDocStringOwner myDocStringOwner;
|
||||
private final StructuredDocString myOriginalDocString;
|
||||
private final DocStringFormat myDocStringFormat;
|
||||
|
||||
public PyDocstringGenerator(@NotNull PyDocStringOwner docStringOwner) {
|
||||
myDocStringOwner = docStringOwner;
|
||||
if (docStringOwner instanceof PyFunction) {
|
||||
myFunction = (PyFunction)docStringOwner;
|
||||
}
|
||||
myProject = myDocStringOwner.getProject();
|
||||
myFile = myDocStringOwner.getContainingFile();
|
||||
myDocStringFormat = DocStringUtil.getDocStringFormat(docStringOwner);
|
||||
final DocStringProvider provider = myDocStringFormat.getProvider();
|
||||
final PyStringLiteralExpression docStringExpression = myDocStringOwner.getDocStringExpression();
|
||||
if (docStringExpression != null) {
|
||||
myStructuredDocString = provider.parseDocString(docStringExpression);
|
||||
myOriginalDocString = provider.parseDocString(docStringExpression);
|
||||
}
|
||||
else {
|
||||
myStructuredDocString = null;
|
||||
myOriginalDocString = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +91,16 @@ public class PyDocstringGenerator {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PyStringLiteralExpression getDocStringExpression() {
|
||||
return myDocStringOwner.getDocStringExpression();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PyFunction getOwnerFunction() {
|
||||
return PyUtil.as(myDocStringOwner, PyFunction.class);
|
||||
}
|
||||
|
||||
public static String generateRaiseOrReturn(@NotNull PyFunction element, String offset, String prefix, boolean checkReturn) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
if (checkReturn) {
|
||||
@@ -131,14 +130,11 @@ public class PyDocstringGenerator {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private PsiFile getFile() {
|
||||
return myFile;
|
||||
}
|
||||
|
||||
public void startTemplate() {
|
||||
assert myDocStringExpression != null;
|
||||
final PyStringLiteralExpression docStringExpression = getDocStringExpression();
|
||||
assert docStringExpression != null;
|
||||
|
||||
final TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(myDocStringExpression);
|
||||
final TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(docStringExpression);
|
||||
|
||||
if (myParams.size() > 1) {
|
||||
throw new IllegalArgumentException("TemplateBuilder can be created only for one parameter");
|
||||
@@ -146,7 +142,7 @@ public class PyDocstringGenerator {
|
||||
|
||||
final DocstringParam paramToEdit = getParamToEdit();
|
||||
final String paramName = paramToEdit.getName();
|
||||
final String stringContent = myDocStringExpression.getStringValue();
|
||||
final String stringContent = docStringExpression.getStringValue();
|
||||
final StructuredDocString parsed = DocStringUtil.parse(stringContent);
|
||||
if (parsed == null) {
|
||||
return;
|
||||
@@ -161,15 +157,16 @@ public class PyDocstringGenerator {
|
||||
if (substring == null) {
|
||||
return;
|
||||
}
|
||||
builder.replaceRange(substring.getTextRange().shiftRight(myDocStringExpression.getStringValueTextRange().getStartOffset()),
|
||||
builder.replaceRange(substring.getTextRange().shiftRight(docStringExpression.getStringValueTextRange().getStartOffset()),
|
||||
getDefaultType());
|
||||
Template template = ((TemplateBuilderImpl)builder).buildInlineTemplate();
|
||||
final VirtualFile virtualFile = myFile.getVirtualFile();
|
||||
final VirtualFile virtualFile = myDocStringOwner.getContainingFile().getVirtualFile();
|
||||
if (virtualFile == null) return;
|
||||
OpenFileDescriptor descriptor = new OpenFileDescriptor(myProject, virtualFile, myDocStringExpression.getTextOffset());
|
||||
Editor targetEditor = FileEditorManager.getInstance(myProject).openTextEditor(descriptor, true);
|
||||
final Project project = myDocStringOwner.getProject();
|
||||
OpenFileDescriptor descriptor = new OpenFileDescriptor(project, virtualFile, docStringExpression.getTextOffset());
|
||||
Editor targetEditor = FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
|
||||
if (targetEditor != null) {
|
||||
TemplateManager.getInstance(myProject).startTemplate(targetEditor, template);
|
||||
TemplateManager.getInstance(project).startTemplate(targetEditor, template);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +182,7 @@ public class PyDocstringGenerator {
|
||||
|
||||
@NotNull
|
||||
private String buildDocString() {
|
||||
if (myStructuredDocString != null) {
|
||||
if (myOriginalDocString != null) {
|
||||
return updateDocString();
|
||||
}
|
||||
else {
|
||||
@@ -198,10 +195,7 @@ public class PyDocstringGenerator {
|
||||
if (myDocStringFormat == DocStringFormat.EPYTEXT || myDocStringFormat == DocStringFormat.REST) {
|
||||
final TagBasedDocStringBuilder builder = new TagBasedDocStringBuilder(myDocStringFormat == DocStringFormat.EPYTEXT ? "@" : ":");
|
||||
builder.addLine("");
|
||||
String indentation = "";
|
||||
if (myDocStringOwner instanceof PyStatementListContainer) {
|
||||
indentation = PyIndentUtil.getElementIndent(((PyStatementListContainer)myDocStringOwner).getStatementList());
|
||||
}
|
||||
String indentation = getDocStringIndentation();
|
||||
boolean addedReturn = false;
|
||||
for (DocstringParam param : myParams) {
|
||||
if (param.isReturnValue()) {
|
||||
@@ -246,9 +240,32 @@ public class PyDocstringGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getDocStringIndentation() {
|
||||
String indentation = "";
|
||||
if (myDocStringOwner instanceof PyStatementListContainer) {
|
||||
indentation = PyIndentUtil.getElementIndent(((PyStatementListContainer)myDocStringOwner).getStatementList());
|
||||
}
|
||||
return indentation;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String updateDocString() {
|
||||
return "";
|
||||
if (myDocStringFormat == DocStringFormat.EPYTEXT || myDocStringFormat == DocStringFormat.REST) {
|
||||
final String prefix = myDocStringFormat == DocStringFormat.EPYTEXT ? "@" : ":";
|
||||
//noinspection unchecked
|
||||
TagBasedDocStringUpdater updater = new TagBasedDocStringUpdater((TagBasedDocString)myOriginalDocString, prefix, getDocStringIndentation());
|
||||
for (DocstringParam param : myParams) {
|
||||
if (param.isReturnValue()) {
|
||||
updater.addReturnValue(param.getType());
|
||||
}
|
||||
else {
|
||||
updater.addParameter(param.getName(), param.getType());
|
||||
}
|
||||
}
|
||||
return updater.getDocStringText();
|
||||
}
|
||||
return "\"\"\"\"\"\"";
|
||||
}
|
||||
|
||||
public boolean haveParametersToAdd() {
|
||||
@@ -261,10 +278,10 @@ public class PyDocstringGenerator {
|
||||
@Override
|
||||
public boolean apply(DocstringParam param) {
|
||||
if (param.isReturnValue()) {
|
||||
return myStructuredDocString.getReturnTypeSubstring() == null;
|
||||
return myOriginalDocString.getReturnTypeSubstring() == null;
|
||||
}
|
||||
else {
|
||||
return myStructuredDocString.getParamTypeSubstring(param.myName) == null;
|
||||
return myOriginalDocString.getParamTypeSubstring(param.myName) == null;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -284,44 +301,41 @@ public class PyDocstringGenerator {
|
||||
public void buildAndInsert() {
|
||||
final String replacement = buildDocString();
|
||||
|
||||
PyElementGenerator elementGenerator = PyElementGenerator.getInstance(myProject);
|
||||
final Project project = myDocStringOwner.getProject();
|
||||
PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project);
|
||||
// update existing docstring
|
||||
if (myStructuredDocString != null) {
|
||||
final PyStringLiteralExpression docStringExpression = getDocStringExpression();
|
||||
if (docStringExpression != null) {
|
||||
PyExpression str = elementGenerator.createDocstring(replacement).getExpression();
|
||||
myDocStringExpression.replace(str);
|
||||
myDocStringOwner = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(myDocStringOwner);
|
||||
if (myDocStringOwner instanceof PyFunction) {
|
||||
myFunction = (PyFunction)myDocStringOwner;
|
||||
}
|
||||
docStringExpression.replace(str);
|
||||
}
|
||||
// create brand new docstring
|
||||
else {
|
||||
if (myFunction == null) {
|
||||
PyFunction function = PyUtil.as(myDocStringOwner, PyFunction.class);
|
||||
if (function == null) {
|
||||
throw new IllegalStateException("Should be a function");
|
||||
}
|
||||
final PyStatementList statements = myFunction.getStatementList();
|
||||
final PyStatementList statements = function.getStatementList();
|
||||
final String indentation = PyIndentUtil.getExpectedElementIndent(statements);
|
||||
final Document document = PsiDocumentManager.getInstance(myProject).getDocument(getFile());
|
||||
final Document document = PsiDocumentManager.getInstance(project).getDocument(myDocStringOwner.getContainingFile());
|
||||
|
||||
if (document != null) {
|
||||
if (PyUtil.onSameLine(statements, myFunction) || statements.getStatements().length == 0) {
|
||||
PyFunction func = elementGenerator.createFromText(LanguageLevel.forElement(myFunction),
|
||||
if (PyUtil.onSameLine(statements, function) || statements.getStatements().length == 0) {
|
||||
PyFunction func = elementGenerator.createFromText(LanguageLevel.forElement(function),
|
||||
PyFunction.class,
|
||||
"def " + myFunction.getName() + myFunction.getParameterList().getText() + ":\n" +
|
||||
"def " + function.getName() + function.getParameterList().getText() + ":\n" +
|
||||
indentation + replacement + "\n" +
|
||||
indentation + statements.getText());
|
||||
|
||||
myFunction = (PyFunction)myFunction.replace(func);
|
||||
myDocStringOwner = (PyFunction)function.replace(func);
|
||||
}
|
||||
else {
|
||||
PyExpressionStatement str = elementGenerator.createDocstring(replacement);
|
||||
statements.addBefore(str, statements.getStatements()[0]);
|
||||
}
|
||||
}
|
||||
|
||||
CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(myFunction);
|
||||
myDocStringExpression = myFunction.getDocStringExpression();
|
||||
}
|
||||
myDocStringOwner = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(myDocStringOwner);
|
||||
}
|
||||
|
||||
public static class DocstringParam {
|
||||
|
||||
@@ -23,6 +23,7 @@ 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;
|
||||
import com.jetbrains.python.psi.StructuredDocString;
|
||||
import com.jetbrains.python.toolbox.Substring;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -146,7 +147,7 @@ public abstract class SectionBasedDocString extends DocStringLineParser implemen
|
||||
}
|
||||
int lineNum = skipEmptyLines(pair.getSecond());
|
||||
final List<SectionField> fields = new ArrayList<SectionField>();
|
||||
final int sectionIndent = getIndent(getLine(sectionStartLine));
|
||||
final int sectionIndent = getLineIndentSize(sectionStartLine);
|
||||
while (!isSectionBreak(lineNum, sectionIndent)) {
|
||||
if (!isEmpty(lineNum)) {
|
||||
final Pair<SectionField, Integer> result = parseSectionField(lineNum, title, sectionIndent);
|
||||
@@ -217,7 +218,7 @@ public abstract class SectionBasedDocString extends DocStringLineParser implemen
|
||||
protected boolean isSectionBreak(int lineNum, int curSectionIndent) {
|
||||
return lineNum >= getLineCount() ||
|
||||
isSectionStart(lineNum) ||
|
||||
(!isEmpty(lineNum) && getIndent(getLine(lineNum)) <= curSectionIndent);
|
||||
(!isEmpty(lineNum) && getLineIndentSize(lineNum) <= curSectionIndent);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -231,7 +232,7 @@ public abstract class SectionBasedDocString extends DocStringLineParser implemen
|
||||
final List<Substring> result = new ArrayList<Substring>();
|
||||
int lastNonEmpty = lineNum - 1;
|
||||
while (!isSectionBreak(lineNum, sectionIndent)) {
|
||||
if (getIndent(getLine(lineNum)) > blockIndent) {
|
||||
if (getLineIndentSize(lineNum) > blockIndent) {
|
||||
// copy all lines after the last non empty including the current one
|
||||
for (int i = lastNonEmpty + 1; i <= lineNum; i++) {
|
||||
result.add(getLine(i));
|
||||
@@ -302,7 +303,7 @@ public abstract class SectionBasedDocString extends DocStringLineParser implemen
|
||||
if (StringUtil.isEmptyOrSpaces(line)) {
|
||||
continue;
|
||||
}
|
||||
curMinIndent = Math.min(curMinIndent, getIndent(line));
|
||||
curMinIndent = Math.min(curMinIndent, PyIndentUtil.getLineIndentSize(line));
|
||||
}
|
||||
final int minIndent = curMinIndent;
|
||||
final List<String> dedentedLines = ContainerUtil.map(workList, new Function<Substring, String>() {
|
||||
|
||||
@@ -49,7 +49,7 @@ public abstract class DocStringBuilder {
|
||||
|
||||
@NotNull
|
||||
public String buildContent(int indent, boolean indentFirst) {
|
||||
return buildContent(StringUtil.repeat(" ", indent), indentFirst);
|
||||
return buildContent(StringUtil.repeatSymbol(' ', indent), indentFirst);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -17,9 +17,9 @@ package com.jetbrains.python.documentation.docstrings;
|
||||
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.jetbrains.python.documentation.DocStringLineParser;
|
||||
import com.jetbrains.python.psi.PyIndentUtil;
|
||||
import com.jetbrains.python.toolbox.Substring;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -31,43 +31,35 @@ import java.util.List;
|
||||
public abstract class DocStringUpdater<T extends DocStringLineParser> {
|
||||
protected final T myOriginalDocString;
|
||||
private final StringBuilder myBuilder;
|
||||
private final List<UpdateOperation> myUpdates = new ArrayList<UpdateOperation>();
|
||||
protected final List<AddParameter> myAddParameterRequests = new ArrayList<AddParameter>();
|
||||
protected final List<AddException> myAddExceptionRequest = new ArrayList<AddException>();
|
||||
protected final List<AddReturnType> myAddReturnTypeRequests = new ArrayList<AddReturnType>();
|
||||
private final List<Modification> myUpdates = new ArrayList<Modification>();
|
||||
private final String myMinContentIndent;
|
||||
|
||||
public DocStringUpdater(@NotNull T docString) {
|
||||
public DocStringUpdater(@NotNull T docString, @NotNull String minContentIndent) {
|
||||
myBuilder = new StringBuilder(docString.getDocStringContent().getSuperString());
|
||||
myOriginalDocString = docString;
|
||||
myMinContentIndent = minContentIndent;
|
||||
}
|
||||
|
||||
public final void addParameter(@NotNull String name, @Nullable String type) {
|
||||
myAddParameterRequests.add(new AddParameter(name, type));
|
||||
}
|
||||
|
||||
public final void addReturnType(@Nullable String name, @NotNull String type) {
|
||||
myAddReturnTypeRequests.add(new AddReturnType(name, type));
|
||||
}
|
||||
|
||||
public final void addException(@NotNull String type) {
|
||||
myAddExceptionRequest.add(new AddException(type));
|
||||
}
|
||||
|
||||
protected void insert(int offset, @NotNull String text) {
|
||||
myUpdates.add(new UpdateOperation(TextRange.from(offset, 0), text));
|
||||
}
|
||||
|
||||
protected final void insertAfterLine(int lineNumber, @NotNull String text) {
|
||||
final Substring line = myOriginalDocString.getLines().get(lineNumber);
|
||||
insert(line.getEndOffset(), "\n" + text);
|
||||
protected final void replace(@NotNull TextRange range, @NotNull String text) {
|
||||
myUpdates.add(new Modification(range, text));
|
||||
}
|
||||
|
||||
protected final void replace(int startOffset, int endOffset, @NotNull String text) {
|
||||
replace(new TextRange(startOffset, endOffset), text);
|
||||
}
|
||||
|
||||
protected final void replace(@NotNull TextRange range, @NotNull String text) {
|
||||
myUpdates.add(new UpdateOperation(range, text));
|
||||
protected final void insert(int offset, @NotNull String text) {
|
||||
replace(offset, offset, text);
|
||||
}
|
||||
|
||||
protected final void insertAfterLine(int lineNumber, @NotNull String text) {
|
||||
final Substring line = myOriginalDocString.getLines().get(lineNumber);
|
||||
insert(line.getEndOffset(), '\n' + text);
|
||||
}
|
||||
|
||||
protected final void insertBeforeLine(int lineNumber, @NotNull String text) {
|
||||
final Substring line = myOriginalDocString.getLines().get(lineNumber);
|
||||
insert(line.getStartOffset(), text + '\n');
|
||||
}
|
||||
|
||||
protected abstract void scheduleUpdates();
|
||||
@@ -75,11 +67,15 @@ public abstract class DocStringUpdater<T extends DocStringLineParser> {
|
||||
@NotNull
|
||||
public final String getDocStringText() {
|
||||
scheduleUpdates();
|
||||
// if several updates insert in one place (e.g. new field), insert them in backward order
|
||||
// Move closing quotes to the next line, if new lines are going to be inserted
|
||||
if (myOriginalDocString.getLineCount() == 1 && !myUpdates.isEmpty()) {
|
||||
insertAfterLine(0, myMinContentIndent);
|
||||
}
|
||||
// If several updates insert in one place (e.g. new field), insert them in backward order,
|
||||
// so the first added is placed above
|
||||
Collections.reverse(myUpdates);
|
||||
Collections.sort(myUpdates);
|
||||
for (int i = myUpdates.size() - 1; i >= 0; i--) {
|
||||
final UpdateOperation update = myUpdates.get(i);
|
||||
Collections.sort(myUpdates, Collections.reverseOrder());
|
||||
for (final Modification update : myUpdates) {
|
||||
final TextRange updateRange = update.range;
|
||||
if (updateRange.getStartOffset() == updateRange.getEndOffset()) {
|
||||
myBuilder.insert(updateRange.getStartOffset(), update.text);
|
||||
@@ -96,45 +92,26 @@ public abstract class DocStringUpdater<T extends DocStringLineParser> {
|
||||
return myOriginalDocString;
|
||||
}
|
||||
|
||||
protected static class AddParameter {
|
||||
@NotNull final String name;
|
||||
@Nullable final String type;
|
||||
|
||||
public AddParameter(@NotNull String name, @Nullable String type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
@NotNull
|
||||
protected String getLineIndent(int lastNonEmptyLine) {
|
||||
final String indent = myOriginalDocString.getLineIndent(lastNonEmptyLine);
|
||||
if (PyIndentUtil.getLineIndentSize(indent) < PyIndentUtil.getLineIndentSize(myMinContentIndent)) {
|
||||
return myMinContentIndent;
|
||||
}
|
||||
return indent;
|
||||
}
|
||||
|
||||
protected static class AddReturnType {
|
||||
@Nullable final String name;
|
||||
@NotNull final String type;
|
||||
|
||||
public AddReturnType(@Nullable String name, @NotNull String type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
protected static class AddException {
|
||||
@NotNull final String type;
|
||||
|
||||
public AddException(@NotNull String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
private static class UpdateOperation implements Comparable<UpdateOperation> {
|
||||
private static class Modification implements Comparable<Modification> {
|
||||
@NotNull final TextRange range;
|
||||
@NotNull final String text;
|
||||
|
||||
public UpdateOperation(@NotNull TextRange range, @NotNull String newText) {
|
||||
public Modification(@NotNull TextRange range, @NotNull String newText) {
|
||||
this.range = range;
|
||||
this.text = newText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(UpdateOperation o) {
|
||||
public int compareTo(Modification o) {
|
||||
return range.getStartOffset() - o.range.getStartOffset();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -34,8 +34,7 @@ public class EpydocDocStringProvider extends DocStringProvider<EpydocString> {
|
||||
@NotNull
|
||||
@Override
|
||||
public DocStringUpdater updateDocString(@NotNull EpydocString docstring) {
|
||||
return new TagBasedDocStringUpdater<EpydocString>(docstring, TAG_PREFIX) {
|
||||
@Override
|
||||
return new TagBasedDocStringUpdater<EpydocString>(docstring, TAG_PREFIX, "") {
|
||||
public DocStringBuilder createDocStringBuilder() {
|
||||
return createDocString();
|
||||
}
|
||||
|
||||
+1
-2
@@ -34,8 +34,7 @@ public class SphinxDocstringProvider extends DocStringProvider<SphinxDocString>{
|
||||
@NotNull
|
||||
@Override
|
||||
public DocStringUpdater updateDocString(@NotNull SphinxDocString docstring) {
|
||||
return new TagBasedDocStringUpdater<SphinxDocString>(docstring, TAG_PREFIX) {
|
||||
@Override
|
||||
return new TagBasedDocStringUpdater<SphinxDocString>(docstring, TAG_PREFIX, "") {
|
||||
public DocStringBuilder createDocStringBuilder() {
|
||||
return createDocString();
|
||||
}
|
||||
|
||||
+48
-53
@@ -15,84 +15,79 @@
|
||||
*/
|
||||
package com.jetbrains.python.documentation.docstrings;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.jetbrains.python.documentation.TagBasedDocString;
|
||||
import com.jetbrains.python.toolbox.Substring;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Mikhail Golubev
|
||||
*/
|
||||
public abstract class TagBasedDocStringUpdater<T extends TagBasedDocString> extends DocStringUpdater<T>{
|
||||
public class TagBasedDocStringUpdater<T extends TagBasedDocString> extends DocStringUpdater<T>{
|
||||
|
||||
private final String myTagPrefix;
|
||||
|
||||
public TagBasedDocStringUpdater(@NotNull T docString, @NotNull String prefix) {
|
||||
super(docString);
|
||||
myTagPrefix = prefix;
|
||||
public TagBasedDocStringUpdater(@NotNull T docString, @NotNull String tagPrefix, @NotNull String minContentIndent) {
|
||||
super(docString, minContentIndent);
|
||||
myTagPrefix = tagPrefix;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private TagBasedDocStringBuilder createBuilder() {
|
||||
return new TagBasedDocStringBuilder(myTagPrefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void scheduleUpdates() {
|
||||
final int anchorLine = firstLineWithTag();
|
||||
final int anchorLineIndent = myOriginalDocString.getLineIndent(anchorLine);
|
||||
for (AddParameter paramReq : myAddParameterRequests) {
|
||||
if (!myOriginalDocString.getParameters().contains(paramReq.name)) {
|
||||
insertAfterLine(anchorLine, createParameterNameLine(paramReq.name, anchorLineIndent));
|
||||
}
|
||||
if (paramReq.type != null) {
|
||||
final Substring typeSub = myOriginalDocString.getParamTypeSubstring(paramReq.name);
|
||||
if (typeSub != null) {
|
||||
replace(typeSub.getTextRange(), paramReq.type);
|
||||
}
|
||||
else {
|
||||
insertAfterLine(anchorLine, createParameterTypeLine(paramReq.name, paramReq.type, anchorLineIndent));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void addParameter(@NotNull String name, @Nullable String type) {
|
||||
if (type != null) {
|
||||
insertTagLine(createBuilder().addParameterType(name, type));
|
||||
}
|
||||
for (AddReturnType returnReq : myAddReturnTypeRequests) {
|
||||
final Substring typeSub = myOriginalDocString.getReturnTypeSubstring();
|
||||
if (typeSub != null) {
|
||||
replace(typeSub.getTextRange(), returnReq.type);
|
||||
}
|
||||
else {
|
||||
insertAfterLine(anchorLine, createReturnTypeLine(returnReq.type, anchorLineIndent));
|
||||
}
|
||||
}
|
||||
for (AddException exception : myAddExceptionRequest) {
|
||||
//TODO check existing docstring
|
||||
insertAfterLine(anchorLine, createRaisesLine(exception.type, anchorLineIndent));
|
||||
else {
|
||||
insertTagLine(createBuilder().addParameterDescription(name, ""));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String createRaisesLine(@NotNull String type, int indent) {
|
||||
return ""; /*createDocStringBuilder().addException(type).buildContent(indent, true);*/
|
||||
public final void addReturnValue(@Nullable String type) {
|
||||
if (type != null) {
|
||||
insertTagLine(createBuilder().addReturnValueType(type));
|
||||
}
|
||||
else {
|
||||
insertTagLine(createBuilder().addReturnValueDescription(""));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String createParameterNameLine(@NotNull String name, int indent) {
|
||||
return ""; /*createDocStringBuilder().addParameter(name, null).buildContent(indent, true);*/
|
||||
private void insertTagLine(@NotNull DocStringBuilder lineBuilder) {
|
||||
final int firstLineWithTag = findFirstLineWithTag();
|
||||
if (firstLineWithTag >= 0) {
|
||||
final String indent = getLineIndent(firstLineWithTag);
|
||||
insertBeforeLine(firstLineWithTag, lineBuilder.buildContent(indent, true));
|
||||
return;
|
||||
}
|
||||
final int lastNonEmptyLine = findLastNonEmptyLine();
|
||||
final String indent = getLineIndent(lastNonEmptyLine);
|
||||
insertAfterLine(lastNonEmptyLine, lineBuilder.buildContent(indent, true));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String createParameterTypeLine(@NotNull String name, @NotNull String type, int indent) {
|
||||
return ""; /*createDocStringBuilder().addParameterType(name, type).buildContent(indent, true);*/
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String createReturnTypeLine(@NotNull String type, int indent) {
|
||||
return ""; /*createDocStringBuilder().addReturnValue(null, type).buildContent(indent, true);*/
|
||||
}
|
||||
|
||||
private int firstLineWithTag() {
|
||||
for (int i = 0; i < myOriginalDocString.getLineCount(); i++) {
|
||||
final Substring line = myOriginalDocString.getLine(i);
|
||||
if (line.contains(myTagPrefix)) {
|
||||
private int findLastNonEmptyLine() {
|
||||
for (int i = myOriginalDocString.getLineCount() - 1; i >= 0; i--) {
|
||||
if (StringUtil.isEmptyOrSpaces(myOriginalDocString.getLine(i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return myOriginalDocString.getLineCount() - 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public abstract DocStringBuilder createDocStringBuilder();
|
||||
private int findFirstLineWithTag() {
|
||||
for (int i = 0; i < myOriginalDocString.getLineCount(); i++) {
|
||||
final Substring line = myOriginalDocString.getLine(i);
|
||||
if (line.trimLeft().startsWith(myTagPrefix)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public class PyIndentUtil {
|
||||
int stop;
|
||||
for (stop = 0; stop < line.length(); stop++) {
|
||||
final char c = line.charAt(stop);
|
||||
if (c == ' ' || c == '\t') {
|
||||
if (!(c == ' ' || c == '\t')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
def func(x):
|
||||
"""
|
||||
|
||||
|
||||
:rtype: object
|
||||
"""
|
||||
return 1
|
||||
Reference in New Issue
Block a user