fixed PY-3777 ReST: Field lists do not wrap correctly

(added formatter)
This commit is contained in:
Ekaterina Tuzova
2012-12-29 13:46:22 +04:00
parent a647dd4499
commit 7d9940f5cb
8 changed files with 226 additions and 15 deletions
+1 -1
View File
@@ -14,7 +14,7 @@
<lang.psiStructureViewFactory language="ReST"
implementationClass="com.jetbrains.rest.structureView.RestStructureViewFactory"/>
<annotator language="ReST" implementationClass="com.jetbrains.rest.validation.RestAnnotatingVisitor"/>
<lang.formatter language="ReST" implementationClass="com.jetbrains.rest.formatter.RestFormattingModelBuilder"/>
</extensions>
@@ -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");
}
@@ -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<RestBlock> 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<Block> getSubBlocks() {
if (mySubBlocks == null) {
mySubBlocks = buildSubBlocks();
}
return new ArrayList<Block>(mySubBlocks);
}
private List<RestBlock> buildSubBlocks() {
List<RestBlock> blocks = new ArrayList<RestBlock>();
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;
}
}
@@ -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;
}
}
@@ -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();
@@ -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);
}
@@ -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();
}
}
@@ -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<RatedResolveResult> 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());