blob: 158e8b60cd2d7f47cf9484bee049cef0eb77fca7 [file] [log] [blame]
/*
* Copyright 2000-2013 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.intellij.psi.impl.source.codeStyle;
import com.intellij.lang.ASTFactory;
import com.intellij.lang.ASTNode;
import com.intellij.lang.LanguageParserDefinitions;
import com.intellij.lang.ParserDefinition;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.impl.source.SourceTreeToPsiMap;
import com.intellij.psi.impl.source.tree.Factory;
import com.intellij.psi.impl.source.tree.LeafElement;
import com.intellij.psi.impl.source.tree.SharedImplUtil;
import com.intellij.psi.jsp.JspElementType;
import com.intellij.psi.jsp.JspTokenType;
import com.intellij.psi.templateLanguages.OuterLanguageElement;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.xml.XmlTokenType;
import com.intellij.util.CharTable;
public class ShiftIndentInsideHelper {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.codeStyle.Helper");
private final CodeStyleSettings mySettings;
private final FileType myFileType;
private final IndentHelper myIndentIndentHelper;
private final Project myProject;
public ShiftIndentInsideHelper(FileType fileType, Project project) {
myProject = project;
mySettings = CodeStyleSettingsManager.getSettings(project);
myFileType = fileType;
myIndentIndentHelper = IndentHelper.getInstance();
}
private static int getStartOffset(ASTNode root, ASTNode child) {
if (child == root) return 0;
ASTNode parent = child.getTreeParent();
int offset = 0;
for (ASTNode child1 = parent.getFirstChildNode(); child1 != child; child1 = child1.getTreeNext()) {
offset += child1.getTextLength();
}
return getStartOffset(root, parent) + offset;
}
public ASTNode shiftIndentInside(ASTNode element, int indentShift) {
if (indentShift == 0) return element;
final CharTable charTableByTree = SharedImplUtil.findCharTableByTree(element);
String text = element.getText();
for (int offset = 0; offset < text.length(); offset++) {
char c = text.charAt(offset);
if (c == '\n' || c == '\r') {
int offset1;
for (offset1 = offset + 1; offset1 < text.length(); offset1++) {
c = text.charAt(offset1);
if (c != ' ' && c != '\t') break;
}
if (c == '\n' || c == '\r') continue;
String space = text.substring(offset + 1, offset1);
int indent = IndentHelperImpl.getIndent(myProject, myFileType, space, true);
int newIndent = indent + indentShift;
newIndent = Math.max(newIndent, 0);
String newSpace = IndentHelperImpl.fillIndent(myProject, myFileType, newIndent);
ASTNode leaf = element.findLeafElementAt(offset);
if (!mayShiftIndentInside(leaf)) {
LOG.error("Error",
leaf.getElementType().toString(),
"Type: " + leaf.getElementType() + " text: " + leaf.getText()
);
}
if (offset1 < text.length()) {
ASTNode next = element.findLeafElementAt(offset1);
if ((next.getElementType() == JavaTokenType.END_OF_LINE_COMMENT
|| next.getElementType() == JavaTokenType.C_STYLE_COMMENT
|| next.getElementType() == JspTokenType.JSP_COMMENT
) &&
next != element) {
if (mySettings.KEEP_FIRST_COLUMN_COMMENT) {
int commentIndent = myIndentIndentHelper.getIndent(myProject, myFileType, next, true);
if (commentIndent == 0) continue;
}
}
else if (next.getElementType() == XmlTokenType.XML_DATA_CHARACTERS) {
continue;
}
}
int leafOffset = getStartOffset(element, leaf);
if (leaf.getElementType() == JavaDocTokenType.DOC_COMMENT_DATA && leafOffset + leaf.getTextLength() == offset + 1) {
ASTNode next = element.findLeafElementAt(offset + 1);
if (next.getElementType() == TokenType.WHITE_SPACE) {
leaf = next;
leafOffset = getStartOffset(element, leaf);
}
else {
if (!newSpace.isEmpty()) {
LeafElement newLeaf = ASTFactory.whitespace(newSpace);
next.getTreeParent().addChild(newLeaf, next);
}
text = text.substring(0, offset + 1) + newSpace + text.substring(offset1);
continue;
}
}
int startOffset = offset + 1 - leafOffset;
int endOffset = offset1 - leafOffset;
if (!LOG.assertTrue(0 <= startOffset && startOffset <= endOffset && endOffset <= leaf.getTextLength())) {
continue;
}
String leafText = leaf.getText();
String newLeafText = leafText.substring(0, startOffset) + newSpace + leafText.substring(endOffset);
if (!newLeafText.isEmpty()) {
LeafElement newLeaf = Factory.createSingleLeafElement(leaf.getElementType(), newLeafText,charTableByTree, SharedImplUtil.getManagerByTree(leaf));
if (leaf.getTreeParent() != null) {
leaf.getTreeParent().replaceChild(leaf, newLeaf);
}
if (leaf == element) {
element = newLeaf;
}
}
else {
ASTNode parent = leaf.getTreeParent();
if (parent != null) {
parent.removeChild(leaf);
}
}
text = text.substring(0, offset + 1) + newSpace + text.substring(offset1);
}
}
return element;
}
public static boolean mayShiftIndentInside(final ASTNode leaf) {
return (isComment(leaf) && !checkJspTexts(leaf))
|| leaf.getElementType() == TokenType.WHITE_SPACE
|| leaf.getElementType() == XmlTokenType.XML_DATA_CHARACTERS
|| leaf.getElementType() == JspTokenType.JAVA_CODE
|| leaf.getElementType() == JspElementType.JSP_SCRIPTLET
|| leaf.getElementType() == XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN;
}
private static boolean checkJspTexts(final ASTNode leaf) {
ASTNode child = leaf.getFirstChildNode();
while(child != null){
if(child instanceof OuterLanguageElement) return true;
child = child.getTreeNext();
}
return false;
}
private static boolean isComment(final ASTNode node) {
final PsiElement psiElement = SourceTreeToPsiMap.treeElementToPsi(node);
if (psiElement instanceof PsiComment) return true;
final ParserDefinition parserDefinition = LanguageParserDefinitions.INSTANCE.forLanguage(psiElement.getLanguage());
if (parserDefinition == null) return false;
final TokenSet commentTokens = parserDefinition.getCommentTokens();
return commentTokens.contains(node.getElementType());
}
public FileType getFileType() {
return myFileType;
}
}