update copyright
[fedora-idea.git] / xml / impl / src / com / intellij / codeInsight / completion / XmlSmartEnterProcessor.java
blob584a1eae2dd730e4bcd0e923723978f042404320
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.codeInsight.completion;
18 import com.intellij.codeInsight.editorActions.smartEnter.SmartEnterProcessor;
19 import com.intellij.lang.ASTNode;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.editor.Document;
22 import com.intellij.openapi.editor.Editor;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.openapi.util.TextRange;
25 import com.intellij.psi.PsiElement;
26 import com.intellij.psi.PsiFile;
27 import com.intellij.psi.util.PsiTreeUtil;
28 import com.intellij.psi.xml.XmlAttribute;
29 import com.intellij.psi.xml.XmlAttributeValue;
30 import com.intellij.psi.xml.XmlChildRole;
31 import com.intellij.psi.xml.XmlTag;
32 import com.intellij.util.IncorrectOperationException;
33 import com.intellij.util.text.CharArrayUtil;
34 import org.jetbrains.annotations.NotNull;
35 import org.jetbrains.annotations.Nullable;
37 /**
38 * @author spleaner
40 public class XmlSmartEnterProcessor extends SmartEnterProcessor {
41 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.XmlSmartEnterProcessor");
43 public boolean process(@NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile psiFile) {
44 final PsiElement atCaret = getStatementAtCaret(editor, psiFile);
45 XmlTag tagAtCaret = PsiTreeUtil.getParentOfType(atCaret, XmlTag.class);
46 if (tagAtCaret != null) {
47 try {
48 final ASTNode emptyTagEnd = XmlChildRole.EMPTY_TAG_END_FINDER.findChild(tagAtCaret.getNode());
49 final ASTNode endTagEnd = XmlChildRole.START_TAG_END_FINDER.findChild(tagAtCaret.getNode());
50 if (emptyTagEnd != null || endTagEnd != null) {
51 return false;
54 int insertionOffset = tagAtCaret.getTextRange().getEndOffset();
55 Document doc = editor.getDocument();
56 int caretAt = editor.getCaretModel().getOffset();
57 final CharSequence text = doc.getCharsSequence();
58 final int probableCommaOffset = CharArrayUtil.shiftForward(text, insertionOffset, " \t");
59 final PsiElement siebling = tagAtCaret.getNextSibling();
60 int caretTo = caretAt;
61 char ch;
63 if (caretAt < probableCommaOffset) {
64 final XmlAttribute xmlAttribute = PsiTreeUtil.getParentOfType(atCaret, XmlAttribute.class, false, XmlTag.class);
66 CharSequence tagNameText = null;
67 if (xmlAttribute != null) {
68 final ASTNode node = tagAtCaret.getNode();
69 if (node != null) {
70 final ASTNode tagName = XmlChildRole.START_TAG_NAME_FINDER.findChild(node);
71 if (tagName != null) {
72 tagNameText = tagName.getText();
76 final XmlAttributeValue valueElement = xmlAttribute.getValueElement();
77 final TextRange textRange = xmlAttribute.getTextRange();
78 caretAt = valueElement == null ? textRange.getStartOffset() : getClosingQuote(xmlAttribute).length() == 0 ? textRange.getEndOffset() : caretAt;
81 if (tagNameText == null) {
82 tagNameText = text.subSequence(tagAtCaret.getTextRange().getStartOffset() + 1, caretAt);
85 final PsiElement element = psiFile.findElementAt(probableCommaOffset);
86 final XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class);
87 final CharSequence text2insert = getClosingPart(xmlAttribute, tagAtCaret, false);
89 if (tag != null && tag.getTextRange().getStartOffset() == probableCommaOffset) {
90 doc.insertString(caretAt, text2insert);
91 if (shouldInsertClosingTag(xmlAttribute, tagAtCaret)) {
92 doc.insertString(tag.getTextRange().getEndOffset() + text2insert.length(), "</" + tagAtCaret.getName() + ">");
95 caretTo = tag.getTextRange().getEndOffset() + text2insert.length();
97 else {
98 doc.insertString(caretAt, text2insert);
99 if (shouldInsertClosingTag(xmlAttribute, tagAtCaret)) {
100 doc.insertString(probableCommaOffset + text2insert.length(), "</" + tagNameText + ">");
103 caretTo = probableCommaOffset + text2insert.length();
106 else if (siebling instanceof XmlTag && siebling.getTextRange().getStartOffset() == caretAt) {
107 final XmlAttribute xmlAttribute = PsiTreeUtil.getParentOfType(atCaret, XmlAttribute.class, false, XmlTag.class);
108 final CharSequence text2insert = getClosingPart(xmlAttribute, tagAtCaret, false);
110 doc.insertString(caretAt, text2insert);
111 if (shouldInsertClosingTag(xmlAttribute, tagAtCaret)) {
112 doc.insertString(siebling.getTextRange().getEndOffset() + text2insert.length(), "</" + tagAtCaret.getName() + ">");
115 caretTo = siebling.getTextRange().getEndOffset() + text2insert.length();
117 else if (probableCommaOffset >= text.length() || ((ch = text.charAt(probableCommaOffset)) != '/' && ch != '>')) {
118 final XmlAttribute xmlAttribute = PsiTreeUtil.getParentOfType(atCaret, XmlAttribute.class, false, XmlTag.class);
119 final CharSequence text2insert = getClosingPart(xmlAttribute, tagAtCaret, true);
121 doc.insertString(insertionOffset, text2insert);
122 caretTo = insertionOffset + text2insert.length();
125 if (isUncommited(project)) {
126 commit(editor);
127 tagAtCaret = PsiTreeUtil.getParentOfType(getStatementAtCaret(editor, psiFile), XmlTag.class);
128 editor.getCaretModel().moveToOffset(caretTo);
131 reformat(tagAtCaret);
132 commit(editor);
134 catch (IncorrectOperationException e) {
135 LOG.error(e);
138 return true;
141 protected boolean shouldInsertClosingTag(final XmlAttribute xmlAttribute, final XmlTag tagAtCaret) {
142 return true;
145 protected String getClosingPart(final XmlAttribute xmlAttribute, final XmlTag tagAtCaret, final boolean emptyTag) {
146 return getClosingQuote(xmlAttribute) + (emptyTag ? "/>" : ">");
149 @NotNull
150 protected static CharSequence getClosingQuote(@Nullable final XmlAttribute attribute) {
151 if (attribute == null) {
152 return "";
155 final XmlAttributeValue element = attribute.getValueElement();
156 if (element == null) {
157 return "";
160 final String s = element.getText();
161 if (s != null && s.length() > 0) {
162 if (s.charAt(0) == '"' && s.charAt(s.length() - 1) != '"') {
163 return "\"";
165 else if (s.charAt(0) == '\'' && s.charAt(s.length() - 1) != '\'') {
166 return "'";
170 return "";