| /* |
| * 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; |
| } |
| } |