From 7d9940f5cbbcd46665196ec12f953c9e2213c8d5 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Sat, 29 Dec 2012 13:46:22 +0400 Subject: [PATCH] fixed PY-3777 ReST: Field lists do not wrap correctly (added formatter) --- python/rest/resources/META-INF/rest.xml | 2 +- .../com/jetbrains/rest/RestElementTypes.java | 1 + .../jetbrains/rest/formatter/RestBlock.java | 122 ++++++++++++++++++ .../formatter/RestFormattingModelBuilder.java | 48 +++++++ .../jetbrains/rest/parsing/RestParser.java | 38 ++++-- .../jetbrains/rest/psi/RestASTFactory.java | 3 + .../com/jetbrains/rest/psi/RestFieldList.java | 19 +++ .../documentation/doctest/PyDocReference.java | 8 +- 8 files changed, 226 insertions(+), 15 deletions(-) create mode 100644 python/rest/src/com/jetbrains/rest/formatter/RestBlock.java create mode 100644 python/rest/src/com/jetbrains/rest/formatter/RestFormattingModelBuilder.java create mode 100644 python/rest/src/com/jetbrains/rest/psi/RestFieldList.java diff --git a/python/rest/resources/META-INF/rest.xml b/python/rest/resources/META-INF/rest.xml index b8e5f8db5da1..0887f8ca65e5 100644 --- a/python/rest/resources/META-INF/rest.xml +++ b/python/rest/resources/META-INF/rest.xml @@ -14,7 +14,7 @@ - + diff --git a/python/rest/src/com/jetbrains/rest/RestElementTypes.java b/python/rest/src/com/jetbrains/rest/RestElementTypes.java index 8a0dddade12f..9cc55bb26437 100644 --- a/python/rest/src/com/jetbrains/rest/RestElementTypes.java +++ b/python/rest/src/com/jetbrains/rest/RestElementTypes.java @@ -12,5 +12,6 @@ public interface RestElementTypes { RestElementType INLINE_BLOCK = new RestElementType("INLINE_BLOCK"); RestElementType LINE_TEXT = new RestElementType("LINE_TEXT"); + RestElementType FIELD_LIST = new RestElementType("FIELD_LIST"); } diff --git a/python/rest/src/com/jetbrains/rest/formatter/RestBlock.java b/python/rest/src/com/jetbrains/rest/formatter/RestBlock.java new file mode 100644 index 000000000000..7e8814d0a4af --- /dev/null +++ b/python/rest/src/com/jetbrains/rest/formatter/RestBlock.java @@ -0,0 +1,122 @@ +package com.jetbrains.rest.formatter; + + +import com.intellij.formatting.*; +import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.tree.IElementType; +import com.jetbrains.rest.RestElementTypes; +import com.jetbrains.rest.RestTokenTypes; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class RestBlock implements ASTBlock { + private RestBlock myParent; + private ASTNode myNode; + private final Alignment myAlignment; + private final Indent myIndent; + private Wrap myWrap; + private List mySubBlocks = null; + + public RestBlock(RestBlock parent, ASTNode node, final Alignment alignment, Indent indent, Wrap wrap) { + myParent = parent; + myNode = node; + myAlignment = alignment; + myIndent = indent; + myWrap = wrap; + } + + + @Override + public ASTNode getNode() { + return myNode; + } + + @NotNull + @Override + public TextRange getTextRange() { + return myNode.getTextRange(); + } + + @NotNull + public List getSubBlocks() { + if (mySubBlocks == null) { + mySubBlocks = buildSubBlocks(); + } + return new ArrayList(mySubBlocks); + } + private List buildSubBlocks() { + List blocks = new ArrayList(); + for (ASTNode child = myNode.getFirstChildNode(); child != null; child = child.getTreeNext()) { + + IElementType childType = child.getElementType(); + if (child.getTextRange().getLength() == 0) continue; + if (childType == RestTokenTypes.WHITESPACE) { + continue; + } + + blocks.add(buildSubBlock(child)); + } + return Collections.unmodifiableList(blocks); + } + + private RestBlock buildSubBlock(ASTNode child) { + IElementType parentType = myNode.getElementType(); + IElementType childType = child.getElementType(); + IElementType grandparentType = myNode.getTreeParent() == null ? null : myNode.getTreeParent().getElementType(); + Wrap wrap = null; + Indent childIndent = Indent.getNoneIndent(); + Alignment childAlignment = null; + + if (grandparentType == RestElementTypes.FIELD_LIST && parentType == RestElementTypes.LINE_TEXT && + childType == RestTokenTypes.LINE) { + childIndent = Indent.getNormalIndent(); + } + return new RestBlock(this, child, childAlignment, childIndent, wrap); + } + + + @Nullable + @Override + public Wrap getWrap() { + return myWrap; + } + + @Nullable + @Override + public Indent getIndent() { + return myIndent; + } + + @Nullable + @Override + public Alignment getAlignment() { + return myAlignment; + } + + @Nullable + @Override + public Spacing getSpacing(@Nullable Block child1, @NotNull Block child2) { + return null; + } + + @NotNull + @Override + public ChildAttributes getChildAttributes(int newChildIndex) { + return new ChildAttributes(Indent.getNoneIndent(), null); + } + + @Override + public boolean isIncomplete() { + return false; + } + + @Override + public boolean isLeaf() { + return myNode.getFirstChildNode() == null; + } +} diff --git a/python/rest/src/com/jetbrains/rest/formatter/RestFormattingModelBuilder.java b/python/rest/src/com/jetbrains/rest/formatter/RestFormattingModelBuilder.java new file mode 100644 index 000000000000..71edb50acb0d --- /dev/null +++ b/python/rest/src/com/jetbrains/rest/formatter/RestFormattingModelBuilder.java @@ -0,0 +1,48 @@ +package com.jetbrains.rest.formatter; + +import com.intellij.formatting.*; +import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.jetbrains.rest.RestLanguage; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * User : ktisha + */ +@SuppressWarnings("UseOfSystemOutOrSystemErr") +public class RestFormattingModelBuilder implements FormattingModelBuilderEx, CustomFormattingModelBuilder { + + @Override + public boolean isEngagedToFormat(PsiElement context) { + PsiFile file = context.getContainingFile(); + return file != null && file.getLanguage() == RestLanguage.INSTANCE; + } + + @NotNull + @Override + public FormattingModel createModel(@NotNull PsiElement element, + @NotNull CodeStyleSettings settings, + @NotNull FormattingMode mode) { + + final RestBlock block = new RestBlock(null, element.getNode(), null, Indent.getNoneIndent(), null); + return FormattingModelProvider.createFormattingModelForPsiFile(element.getContainingFile(), block, settings); + } + + @NotNull + @Override + public FormattingModel createModel(PsiElement element, CodeStyleSettings settings) { + return createModel(element, settings, FormattingMode.REFORMAT); + } + + @Nullable + @Override + public TextRange getRangeAffectingIndent(PsiFile file, int offset, ASTNode elementAtOffset) { + final PsiElement element = elementAtOffset.getPsi(); + final PsiElement container = element.getParent(); + return container != null ? container.getTextRange() : null; + } +} diff --git a/python/rest/src/com/jetbrains/rest/parsing/RestParser.java b/python/rest/src/com/jetbrains/rest/parsing/RestParser.java index 00ef744bdde4..e06155c0a4fc 100644 --- a/python/rest/src/com/jetbrains/rest/parsing/RestParser.java +++ b/python/rest/src/com/jetbrains/rest/parsing/RestParser.java @@ -47,9 +47,7 @@ public class RestParser implements PsiParser { marker.done(RestTokenTypes.TITLE); } else if (type == RestTokenTypes.FIELD) { - PsiBuilder.Marker marker = builder.mark(); - builder.advanceLexer(); - marker.done(RestTokenTypes.FIELD); + parseFieldList(builder); } else if (type == RestTokenTypes.INLINE_LINE) { PsiBuilder.Marker marker = builder.mark(); @@ -62,13 +60,7 @@ public class RestParser implements PsiParser { marker.done(RestElementTypes.REFERENCE_TARGET); } else if (type == RestTokenTypes.LINE) { - PsiBuilder.Marker marker = builder.mark(); - while (type == RestTokenTypes.LINE || type == RestTokenTypes.WHITESPACE) { - builder.advanceLexer(); - type = builder.getTokenType(); - } - - marker.done(RestElementTypes.LINE_TEXT); + parseLineText(builder, type); } else builder.advanceLexer(); @@ -77,6 +69,32 @@ public class RestParser implements PsiParser { return builder.getTreeBuilt(); } + private static boolean parseLineText(PsiBuilder builder, IElementType type) { + PsiBuilder.Marker marker = builder.mark(); + boolean gotLine = false; + while (type == RestTokenTypes.LINE || type == RestTokenTypes.WHITESPACE) { + builder.advanceLexer(); + type = builder.getTokenType(); + gotLine = true; + } + if (gotLine) + marker.done(RestElementTypes.LINE_TEXT); + else + marker.drop(); + return gotLine; + } + + private static void parseFieldList(PsiBuilder builder) { + PsiBuilder.Marker listMarker = builder.mark(); + PsiBuilder.Marker marker = builder.mark(); + builder.advanceLexer(); + marker.done(RestTokenTypes.FIELD); + if (parseLineText(builder, builder.getTokenType())) + listMarker.done(RestElementTypes.FIELD_LIST); + else + listMarker.drop(); + } + private void parseMarkup(PsiBuilder builder) { PsiBuilder.Marker marker = builder.mark(); IElementType type = builder.getTokenType(); diff --git a/python/rest/src/com/jetbrains/rest/psi/RestASTFactory.java b/python/rest/src/com/jetbrains/rest/psi/RestASTFactory.java index 6e7f15f314f4..6490751365c7 100644 --- a/python/rest/src/com/jetbrains/rest/psi/RestASTFactory.java +++ b/python/rest/src/com/jetbrains/rest/psi/RestASTFactory.java @@ -30,6 +30,9 @@ public class RestASTFactory implements RestTokenTypes, RestElementTypes { if (type == FIELD) { return new RestRole(node); } + if (type == FIELD_LIST) { + return new RestFieldList(node); + } if (type == INLINE_BLOCK) { return new RestInlineBlock(node); } diff --git a/python/rest/src/com/jetbrains/rest/psi/RestFieldList.java b/python/rest/src/com/jetbrains/rest/psi/RestFieldList.java new file mode 100644 index 000000000000..d08696270691 --- /dev/null +++ b/python/rest/src/com/jetbrains/rest/psi/RestFieldList.java @@ -0,0 +1,19 @@ +package com.jetbrains.rest.psi; + +import com.intellij.lang.ASTNode; +import org.jetbrains.annotations.NotNull; + +/** + * User : ktisha + */ +public class RestFieldList extends RestElement { + public RestFieldList(@NotNull final ASTNode node) { + super(node); + } + + @Override + public String toString() { + return "RestFieldList:" + getNode().getElementType().toString(); + } + +} diff --git a/python/src/com/jetbrains/python/documentation/doctest/PyDocReference.java b/python/src/com/jetbrains/python/documentation/doctest/PyDocReference.java index 15a84c184d64..b61ada150c62 100644 --- a/python/src/com/jetbrains/python/documentation/doctest/PyDocReference.java +++ b/python/src/com/jetbrains/python/documentation/doctest/PyDocReference.java @@ -11,7 +11,6 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiLanguageInjectionHost; import com.intellij.psi.ResolveResult; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; -import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.psi.PyQualifiedExpression; import com.jetbrains.python.psi.impl.references.PyReferenceImpl; import com.jetbrains.python.psi.resolve.*; @@ -63,8 +62,8 @@ public class PyDocReference extends PyReferenceImpl { ResolveProcessor processor = new ResolveProcessor(referencedName); - final ScopeOwner scopeOwner = ScopeUtil.getScopeOwner(myElement); - PyResolveUtil.scopeCrawlUp(processor, scopeOwner != null? scopeOwner : (ScopeOwner)file, referencedName, file); + if (file instanceof ScopeOwner) + PyResolveUtil.scopeCrawlUp(processor, (ScopeOwner)file, referencedName, file); final List resultList = getResultsFromProcessor(referencedName, processor, file, file); if (resultList.size() > 0) return resultList.toArray(new RatedResolveResult[resultList.size()]); @@ -86,7 +85,8 @@ public class PyDocReference extends PyReferenceImpl { // include our own names final CompletionVariantsProcessor processor = new CompletionVariantsProcessor(element); - PyResolveUtil.scopeCrawlUp(processor, (ScopeOwner)file, null, null); + if (file instanceof ScopeOwner) + PyResolveUtil.scopeCrawlUp(processor, (ScopeOwner)file, null, null); ret.addAll(processor.getResultList());