Add proper folding for line and block comments in JSON

Also line comments separated by more than one line feed are not moved together.
This commit is contained in:
Mikhail Golubev
2014-09-17 19:18:51 +04:00
parent c22d5c0e97
commit e7f321eacb
8 changed files with 83 additions and 25 deletions

View File

@@ -1,16 +1,15 @@
package com.intellij.json.editor.folding;
import com.intellij.json.JsonElementTypes;
import com.intellij.json.psi.JsonLiteral;
import com.intellij.json.psi.JsonObject;
import com.intellij.json.psi.JsonProperty;
import com.intellij.json.psi.JsonValue;
import com.intellij.json.psi.*;
import com.intellij.lang.ASTNode;
import com.intellij.lang.folding.FoldingBuilder;
import com.intellij.lang.folding.FoldingDescriptor;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -37,6 +36,18 @@ public class JsonFoldingBuilder implements FoldingBuilder, DumbAware {
if ((type == JsonElementTypes.OBJECT || type == JsonElementTypes.ARRAY) && spanMultipleLines(node, document)) {
descriptors.add(new FoldingDescriptor(node, node.getTextRange()));
}
else if (type == JsonElementTypes.BLOCK_COMMENT) {
descriptors.add(new FoldingDescriptor(node, node.getTextRange()));
}
else if (type == JsonElementTypes.LINE_COMMENT) {
final Couple<PsiElement> commentRange = JsonPsiUtil.expandLineCommentsRange(node.getPsi());
final int startOffset = commentRange.getFirst().getTextRange().getStartOffset();
final int endOffset = commentRange.getSecond().getTextRange().getEndOffset();
if (document.getLineNumber(startOffset) != document.getLineNumber(endOffset)) {
descriptors.add(new FoldingDescriptor(node, new TextRange(startOffset, endOffset)));
}
}
for (ASTNode child : node.getChildren(null)) {
collectDescriptorsRecursively(child, document, descriptors);
}
@@ -72,6 +83,12 @@ public class JsonFoldingBuilder implements FoldingBuilder, DumbAware {
else if (type == JsonElementTypes.ARRAY) {
return "[...]";
}
else if (type == JsonElementTypes.LINE_COMMENT) {
return "//...";
}
else if (type == JsonElementTypes.BLOCK_COMMENT) {
return "/*...*/";
}
return "...";
}

View File

@@ -4,7 +4,6 @@ import com.intellij.codeInsight.editorActions.moveUpDown.LineMover;
import com.intellij.codeInsight.editorActions.moveUpDown.LineRange;
import com.intellij.json.JsonElementTypes;
import com.intellij.json.psi.*;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
@@ -74,25 +73,9 @@ public class JsonLineMover extends LineMover {
@NotNull
private Pair<PsiElement, PsiElement> expandCommentsInRange(@NotNull Pair<PsiElement, PsiElement> range) {
final PsiElement firstElement = expandComment(range.first, true);
final PsiElement lastElement = expandComment(range.second, false);
return Pair.create(firstElement, lastElement);
}
@NotNull
private PsiElement expandComment(@NotNull PsiElement first, boolean before) {
ASTNode node = first.getNode();
if (node.getElementType() != JsonElementTypes.LINE_COMMENT) {
return first;
}
ASTNode sibling;
do {
sibling = node;
node = before ? node.getTreePrev() : node.getTreeNext();
}
while (node != null && (node.getElementType() == JsonElementTypes.LINE_COMMENT ||
sibling.getElementType() == JsonElementTypes.LINE_COMMENT));
return (before ? sibling.getTreeNext() : sibling.getTreePrev()).getPsi();
final PsiElement upper = JsonPsiUtil.findOutermostLineComment(range.getFirst(), false);
final PsiElement lower = JsonPsiUtil.findOutermostLineComment(range.getSecond(), true);
return Pair.create(upper, lower);
}
@Override

View File

@@ -1,6 +1,11 @@
package com.intellij.json.psi;
import com.intellij.json.JsonElementTypes;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.Couple;
import com.intellij.psi.PsiElement;
import com.intellij.psi.TokenType;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
/**
@@ -46,4 +51,37 @@ public class JsonPsiUtil {
final PsiElement parent = element.getParent();
return parent instanceof JsonProperty && element == ((JsonProperty)parent).getValue();
}
/**
* Find largest region of successive line comments that includes given one.
* @param anchor anchor line comment from which range will be expanded
* @return specified range of comments or pair {@code (anchor, anchor)} if anchor is not line comment
*/
@NotNull
public static Couple<PsiElement> expandLineCommentsRange(@NotNull PsiElement anchor) {
return Couple.of(findOutermostLineComment(anchor, false), findOutermostLineComment(anchor, true));
}
/**
* Lowest or topmost successive line comment for given anchor element.
* @param anchor start element (most probably another line comment)
* @param below whether to scan through element forward or backward
* @return described {@link com.intellij.psi.PsiComment} element or {@code anchor} if it's not a line comment
*/
@NotNull
public static PsiElement findOutermostLineComment(@NotNull PsiElement anchor, boolean below) {
ASTNode node = anchor.getNode();
ASTNode lastSeenComment = node;
while (node != null) {
final IElementType elementType = node.getElementType();
if (elementType == JsonElementTypes.LINE_COMMENT) {
lastSeenComment = node;
}
else if (elementType != TokenType.WHITE_SPACE || node.getText().indexOf('\n', 1) != -1) {
break;
}
node = below ? node.getTreeNext() : node.getTreePrev();
}
return lastSeenComment.getPsi();
}
}

View File

@@ -17,6 +17,10 @@ public class JsonFoldingTest extends JsonTestCase {
doTest();
}
public void testCommentaries() {
doTest();
}
// Moved from JavaScript
public void testObjectLiteral2() {

View File

@@ -0,0 +1,11 @@
// Single line comment is not folded.
<fold text='//...'>// But
// several comments
// are.</fold>
<fold text='{"foo": 42...}'>{
<fold text='/*...*/'>/* Block commentaries
are always folded however.
*/</fold>
"foo": 42
}</fold>

View File

@@ -1,5 +1,7 @@
{
"foo": true,
// This line won't be moved
// Line<caret> 1
// Line 2
"bar": false

View File

@@ -1,5 +1,7 @@
{
"foo": true,
// This line won't be moved
"bar": false
// Line 1
// Line 2

View File

@@ -1,6 +1,7 @@
{
"foo": true,
// This line won't be moved
// Line 1
// Line 2
"foo": true,
"bar": false
}