PY-7123 Structure for RST document incorrect

This commit is contained in:
Ekaterina Tuzova
2017-07-05 19:15:31 +03:00
parent 8ecc3ef5bc
commit 4df5c1e27f
9 changed files with 198 additions and 64 deletions

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.rest;
import com.jetbrains.rest.fixtures.RestFixtureTestCase;
import static com.intellij.testFramework.PlatformTestUtil.assertTreeEqual;
public class RestStructureViewTest extends RestFixtureTestCase {
public void testPlain() {
doTest("-plain.rst\n" +
" Chapter 1 Title\n" +
" Chapter 2 Title\n");
}
public void testFileBeginning() {
doTest("-fileBeginning.rst\n" +
" Chapter 1 Title\n" +
" Chapter 2 Title\n");
}
public void testOneInnerSection() {
doTest("-oneInnerSection.rst\n" +
" -Chapter 1 Title\n" +
" -Section 1.1 Title\n" +
" Subsection 1.1.1 Title\n" +
" Section 1.2 Title\n" +
" Chapter 2 Title\n");
}
public void testTree() {
doTest("-tree.rst\n" +
" -Hello, world\n" +
" -A section\n" +
" -A subsection\n" +
" A sub-subsection\n" +
" An other one\n" +
" -Back up\n" +
" And down\n" +
" -twice\n" +
" with feelings\n");
}
private void doTest(final String expected) {
myFixture.configureByFile("/structureView/" + getTestName(true) + ".rst");
myFixture.testStructureView(component -> assertTreeEqual(component.getTree(), expected));
}
}

View File

@@ -0,0 +1,5 @@
Chapter 1 Title
===============
Chapter 2 Title
===============

View File

@@ -0,0 +1,14 @@
Chapter 1 Title
===============
Section 1.1 Title
-----------------
Subsection 1.1.1 Title
~~~~~~~~~~~~~~~~~~~~~~
Section 1.2 Title
-----------------
Chapter 2 Title
===============

View File

@@ -0,0 +1,6 @@
Chapter 1 Title
===============
Chapter 2 Title
===============

View File

@@ -0,0 +1,30 @@
The following document:
============
Hello, world
============
A section
=========
A subsection
------------
A sub-subsection
````````````````
An other one
````````````
Back up
=======
And down
--------
twice
-----
with feelings
`````````````

View File

@@ -28,7 +28,7 @@ import org.jetbrains.annotations.NotNull;
*/
public class RestParser implements PsiParser {
@NotNull
public ASTNode parse(IElementType root, PsiBuilder builder) {
public ASTNode parse(@NotNull IElementType root, @NotNull PsiBuilder builder) {
final PsiBuilder.Marker rootMarker = builder.mark();
while (!builder.eof()) {
IElementType type = builder.getTokenType();

View File

@@ -16,6 +16,7 @@
package com.jetbrains.rest.psi;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.Pair;
import com.jetbrains.rest.validation.RestElementVisitor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -27,6 +28,8 @@ import java.text.StringCharacterIterator;
* User : catherine
*/
public class RestTitle extends RestElement {
private static String ourAdornmentSymbols = "=-`:.'\\~^_*+#>";
public RestTitle(@NotNull final ASTNode node) {
super(node);
}
@@ -79,6 +82,17 @@ public class RestTitle extends RestElement {
return text.substring(start, text.length());
}
public Pair<Character, Character> getAdornments() {
final String text = getNode().getText().trim();
if (text.length() < 2) return Pair.empty();
Character overline = text.charAt(0);
if (ourAdornmentSymbols.indexOf(overline) < 0) {
overline = null;
}
final char underline = text.charAt(text.length()-2);
return Pair.create(overline, underline);
}
@Override
protected void acceptRestVisitor(RestElementVisitor visitor) {
visitor.visitTitle(this);

View File

@@ -16,84 +16,85 @@
package com.jetbrains.rest.structureView;
import com.intellij.ide.structureView.StructureViewTreeElement;
import com.intellij.navigation.ItemPresentation;
import com.intellij.ide.structureView.impl.common.PsiTreeElementBase;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.NavigatablePsiElement;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.jetbrains.rest.psi.RestElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.rest.psi.RestTitle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
/**
* Handles nodes in ReST Structure View.
* User : catherine
*/
public class RestStructureViewElement implements StructureViewTreeElement {
private NavigatablePsiElement myElement;
public class RestStructureViewElement extends PsiTreeElementBase<NavigatablePsiElement> {
public RestStructureViewElement(NavigatablePsiElement element) {
myElement = element;
super(element);
}
public NavigatablePsiElement getValue() {
return myElement;
}
public void navigate(boolean requestFocus) {
myElement.navigate(requestFocus);
}
public boolean canNavigate() {
return myElement.canNavigate();
}
public boolean canNavigateToSource() {
return myElement.canNavigateToSource();
}
@NotNull
public StructureViewTreeElement[] getChildren() {
final Set<RestElement> childrenElements = new LinkedHashSet<>();
myElement.acceptChildren(new PsiElementVisitor() {
@Override
public void visitElement(PsiElement element) {
if (element instanceof RestTitle && ((RestTitle)element).getName() != null)
childrenElements.add((RestElement)element);
else
element.acceptChildren(this);
}
});
StructureViewTreeElement[] children = new StructureViewTreeElement[childrenElements.size()];
int i = 0;
for (RestElement element : childrenElements) {
children[i] = new RestStructureViewElement(element);
i += 1;
@Override
public Collection<StructureViewTreeElement> getChildrenBase() {
Collection<StructureViewTreeElement> result = new LinkedList<>();
final NavigatablePsiElement element = getElement();
if (element == null) {
return result;
}
return children;
final PsiFile file = element.getContainingFile();
final TextRange range = element.equals(file) ? TextRange.EMPTY_RANGE : element.getTextRange();
Pair<Character, Character> elementAdornments = element instanceof RestTitle ? ((RestTitle)element).getAdornments() : Pair.empty();
Pair<Character, Character> adornmentsToUse = null;
final Collection<RestTitle> titles = PsiTreeUtil.findChildrenOfType(file, RestTitle.class);
final HashSet<Pair<Character, Character>> usedAdornments = new HashSet<>();
boolean skipElements = false;
for (RestTitle child : titles) {
final Pair<Character, Character> childAdornments = child.getAdornments();
if (child.getTextRange().getStartOffset() >= range.getEndOffset()) {
final Character overline = childAdornments.getFirst();
final Character underline = childAdornments.getSecond();
if (adornmentsToUse == null) {
adornmentsToUse = childAdornments;
}
if (usedAdornments.contains(childAdornments) || underline == null) {
break;
}
if (underline.equals(adornmentsToUse.getSecond()) && ((adornmentsToUse.getFirst() == null && overline == null) ||
adornmentsToUse.getFirst().equals(overline))) {
result.add(new RestStructureViewElement(child));
}
}
else {
if (element.equals(child)) {
skipElements = false;
}
else if (childAdornments.equals(elementAdornments)) {
skipElements = true;
}
if (!skipElements) {
usedAdornments.add(child.getAdornments());
}
}
}
return result;
}
@NotNull
public ItemPresentation getPresentation() {
return new ItemPresentation() {
public String getPresentableText() {
return myElement.getName();
}
@Nullable
public String getLocationString() {
return null;
}
public Icon getIcon(boolean open) {
return null;
}
};
@Nullable
@Override
public String getPresentableText() {
final NavigatablePsiElement element = getElement();
return element != null ? element.getName() : "";
}
}

View File

@@ -19,6 +19,7 @@ import com.intellij.ide.structureView.StructureViewModel;
import com.intellij.ide.structureView.StructureViewModelBase;
import com.intellij.ide.structureView.StructureViewTreeElement;
import com.intellij.ide.util.treeView.smartTree.Sorter;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiFile;
import com.jetbrains.rest.RestFile;
@@ -44,12 +45,12 @@ public class RestStructureViewModel extends StructureViewModelBase implements St
@Override
public boolean isAlwaysLeaf(StructureViewTreeElement element) {
return element.getValue() instanceof RestTitle;
return element.getChildren().length == 0;
}
@Override
public boolean isAutoExpand(@NotNull StructureViewTreeElement element) {
return element.getValue() instanceof PsiFile;
return element.getValue() instanceof PsiFile || ApplicationManager.getApplication().isUnitTestMode();
}
@Override