blob: 83004f90747773fd5fa50d8b248d427304907e2d [file] [log] [blame]
/*
* Copyright 2000-2014 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.
*/
/*
* Created by IntelliJ IDEA.
* User: spleaner
* Date: Aug 8, 2007
* Time: 2:20:33 PM
*/
package com.intellij.xml.refactoring;
import com.intellij.codeInsight.daemon.impl.quickfix.EmptyExpression;
import com.intellij.codeInsight.highlighting.HighlightManager;
import com.intellij.codeInsight.template.*;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.xml.XmlChildRole;
import com.intellij.psi.xml.XmlTag;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.util.PairProcessor;
import com.intellij.util.containers.Stack;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class XmlTagInplaceRenamer {
@NonNls private static final String PRIMARY_VARIABLE_NAME = "PrimaryVariable";
@NonNls private static final String OTHER_VARIABLE_NAME = "OtherVariable";
private final Editor myEditor;
private final static Stack<XmlTagInplaceRenamer> ourRenamersStack = new Stack<XmlTagInplaceRenamer>();
private ArrayList<RangeHighlighter> myHighlighters;
private XmlTagInplaceRenamer(@NotNull final Editor editor) {
myEditor = editor;
}
public static void rename(final Editor editor, @NotNull final XmlTag tag) {
if (!ourRenamersStack.isEmpty()) {
ourRenamersStack.peek().finish();
}
final XmlTagInplaceRenamer renamer = new XmlTagInplaceRenamer(editor);
ourRenamersStack.push(renamer);
renamer.rename(tag);
}
private void rename(@NotNull final XmlTag tag) {
final Pair<ASTNode, ASTNode> pair = getNamePair(tag);
if (pair == null) return;
final Project project = myEditor.getProject();
if (project != null) {
final List<TextRange> highlightRanges = new ArrayList<TextRange>();
highlightRanges.add(pair.first.getTextRange());
if (pair.second != null) {
highlightRanges.add(pair.second.getTextRange());
}
if (!CommonRefactoringUtil.checkReadOnlyStatus(project, tag)) {
return;
}
myHighlighters = new ArrayList<RangeHighlighter>();
CommandProcessor.getInstance().executeCommand(project, new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
final int offset = myEditor.getCaretModel().getOffset();
myEditor.getCaretModel().moveToOffset(tag.getTextOffset());
final Template t = buildTemplate(tag, pair);
TemplateManager.getInstance(project).startTemplate(myEditor, t, new TemplateEditingAdapter() {
@Override
public void templateFinished(final Template template, boolean brokenOff) {
finish();
}
@Override
public void templateCancelled(final Template template) {
finish();
}
}, new PairProcessor<String, String>() {
@Override
public boolean process(final String variableName, final String value) {
return value.length() == 0 || value.charAt(value.length() - 1) != ' ';
}
});
// restore old offset
myEditor.getCaretModel().moveToOffset(offset);
addHighlights(highlightRanges, myEditor, myHighlighters);
}
});
}
}, RefactoringBundle.message("rename.title"), null);
}
}
private void finish() {
ourRenamersStack.pop();
if (myHighlighters != null) {
final HighlightManager highlightManager = HighlightManager.getInstance(myEditor.getProject());
for (final RangeHighlighter highlighter : myHighlighters) {
highlightManager.removeSegmentHighlighter(myEditor, highlighter);
}
}
}
private Pair<ASTNode, ASTNode> getNamePair(@NotNull final XmlTag tag) {
final int offset = myEditor.getCaretModel().getOffset();
final ASTNode node = tag.getNode();
assert node != null;
final ASTNode startTagName = XmlChildRole.START_TAG_NAME_FINDER.findChild(node);
if (startTagName == null) return null;
final ASTNode endTagName = XmlChildRole.CLOSING_TAG_NAME_FINDER.findChild(node);
final ASTNode selected = (endTagName == null ||
startTagName.getTextRange().contains(offset) ||
startTagName.getTextRange().contains(offset - 1))
? startTagName
: endTagName;
final ASTNode other = (selected == startTagName) ? endTagName : startTagName;
return Pair.create(selected, other);
}
private static Template buildTemplate(@NotNull final XmlTag tag, @NotNull final Pair<ASTNode, ASTNode> pair) {
final TemplateBuilderImpl builder = new TemplateBuilderImpl(tag);
final ASTNode selected = pair.first;
final ASTNode other = pair.second;
builder.replaceElement(selected.getPsi(), PRIMARY_VARIABLE_NAME, new EmptyExpression() {
@Override
public Result calculateQuickResult(final ExpressionContext context) {
return new TextResult(selected.getText());
}
@Override
public Result calculateResult(final ExpressionContext context) {
return new TextResult(selected.getText());
}
}, true);
if (other != null) {
builder.replaceElement(other.getPsi(), OTHER_VARIABLE_NAME, PRIMARY_VARIABLE_NAME, false);
}
return builder.buildInlineTemplate();
}
private static void addHighlights(List<TextRange> ranges, Editor editor, ArrayList<RangeHighlighter> highlighters) {
EditorColorsManager colorsManager = EditorColorsManager.getInstance();
final TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.WRITE_SEARCH_RESULT_ATTRIBUTES);
final HighlightManager highlightManager = HighlightManager.getInstance(editor.getProject());
for (final TextRange range : ranges) {
highlightManager.addOccurrenceHighlight(editor, range.getStartOffset(), range.getEndOffset(), attributes, 0, highlighters, null);
}
for (RangeHighlighter highlighter : highlighters) {
highlighter.setGreedyToLeft(true);
highlighter.setGreedyToRight(true);
}
}
}