Merge branch 'master' of git@git.labs.intellij.net:idea/community

This commit is contained in:
Denis Zhdanov
2010-05-07 17:08:21 +04:00
23 changed files with 277 additions and 196 deletions
@@ -59,7 +59,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageBaseFix {
protected boolean isAvailableImpl(int offset) {
final PsiMethodCallExpression call = getMethodCall();
if (call == null) return false;
if (call == null || !call.isValid()) return false;
PsiReferenceExpression ref = call.getMethodExpression();
String name = ref.getReferenceName();
@@ -90,8 +90,8 @@ public class CreateMethodFromUsageFix extends CreateFromUsageBaseFix {
PsiExpressionList argumentList = call.getArgumentList();
List<HighlightInfo> errorsInArgList = DaemonCodeAnalyzerImpl.getHighlights(document, HighlightSeverity.ERROR, project,
//strictly inside arg list
argumentList.getTextRange().getStartOffset()+1,
//strictly inside arg list
argumentList.getTextRange().getStartOffset()+1,
argumentList.getTextRange().getEndOffset()-1);
return !errorsInArgList.isEmpty();
}
@@ -117,7 +117,6 @@ public class CreateMethodFromUsageFix extends CreateFromUsageBaseFix {
}
protected void invokeImpl(final PsiClass targetClass) {
if (targetClass == null) return;
PsiMethodCallExpression expression = getMethodCall();
if (expression == null) return;
@@ -170,6 +169,9 @@ public class CreateMethodFromUsageFix extends CreateFromUsageBaseFix {
setupVisibility(parentClass, targetClass, method.getModifierList());
expression = getMethodCall();
LOG.assertTrue(expression.isValid());
if (shouldCreateStaticMember(expression.getMethodExpression(), targetClass) && !shouldBeAbstract(targetClass)) {
PsiUtil.setModifierProperty(method, PsiModifier.STATIC, true);
}
@@ -98,7 +98,12 @@ public class JavaResolveCache {
type = ConcurrencyUtil.cacheOrGet(myCalculatedTypes, expr, type);
}
if (!type.isValid()) {
LOG.error("Type is invalid: " + type+"; expr: '"+expr+"' is "+(expr.isValid() ? "valid":"invalid"));
if (expr.isValid()) {
LOG.error("Type is invalid: " + type + "; expr: '" + expr + "' is valid");
}
else {
LOG.error("Expression: '"+expr+"' is invalid, must not be used for getType()");
}
}
return type == NULL_TYPE ? null : type;
}
@@ -179,6 +179,10 @@ public class ProblemsHolder {
return myOnTheFly;
}
public PsiFile getFile() {
return myFile;
}
public final Project getProject() {
return myManager.getProject();
}
@@ -18,6 +18,9 @@ package com.intellij.psi;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
/**
* Trivial implementation of {@link ResolveResult}.
@@ -67,4 +70,15 @@ public class PsiElementResolveResult implements ResolveResult{
public String toString() {
return "PsiElementResolveResult: " + (myElement instanceof PsiNamedElement ? ((PsiNamedElement)myElement).getName() : myElement.getText());
}
public static ResolveResult[] createResults(@Nullable Collection<? extends PsiElement> elements) {
if (elements == null || elements.isEmpty()) return EMPTY_ARRAY;
final ResolveResult[] results = new ResolveResult[elements.size()];
int i = 0;
for (PsiElement element : elements) {
results[i++] = new PsiElementResolveResult(element);
}
return results;
}
}
@@ -41,4 +41,15 @@ public abstract class PsiPolyVariantReferenceBase<T extends PsiElement> extends
ResolveResult[] resolveResults = multiResolve(false);
return resolveResults.length == 1 ? resolveResults[0].getElement() : null;
}
@Override
public boolean isReferenceTo(PsiElement element) {
final ResolveResult[] results = multiResolve(false);
for (ResolveResult result : results) {
if (getElement().getManager().areElementsEquivalent(result.getElement(), element)) {
return true;
}
}
return false;
}
}
@@ -21,7 +21,7 @@ import org.jetbrains.annotations.NotNull;
/**
* @author cdr
*/
public abstract class WalkingState<T> {
public class WalkingState<T> {
public interface TreeGuide<T> {
T getNextSibling(@NotNull T element);
T getPrevSibling(@NotNull T element);
@@ -33,9 +33,9 @@ public abstract class WalkingState<T> {
private final TreeGuide<T> myWalker;
private boolean stopped;
public abstract void elementFinished(@NotNull T element);
public void elementFinished(@NotNull T element) {}
protected WalkingState(@NotNull TreeGuide<T> delegate) {
public WalkingState(@NotNull TreeGuide<T> delegate) {
myWalker = delegate;
}
@@ -100,4 +100,4 @@ public abstract class WalkingState<T> {
public void stopWalking() {
stopped = true;
}
}
}
@@ -41,8 +41,8 @@ public class DaemonTooltipUtil {
public static void showInfoTooltip(@NotNull final HighlightInfo info, final Editor editor, final int defaultOffset, final int currentWidth) {
if (info.toolTip == null) return;
Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
int endOffset = info.highlighter.getEndOffset();
int startOffset = info.highlighter.getStartOffset();
int startOffset = info.getActualStartOffset();
int endOffset = info.getActualEndOffset();
Point top = editor.logicalPositionToXY(editor.offsetToLogicalPosition(startOffset));
Point bottom = editor.logicalPositionToXY(editor.offsetToLogicalPosition(endOffset));
@@ -21,8 +21,6 @@ import com.intellij.openapi.editor.ex.ErrorStripeEvent;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.project.Project;
import java.util.List;
public class ErrorStripeHandler extends ErrorStripeAdapter {
private final Project myProject;
@@ -39,14 +37,9 @@ public class ErrorStripeHandler extends ErrorStripeAdapter {
}
}
private HighlightInfo findInfo(RangeHighlighter highlighter) {
List<HighlightInfo> highlights = DaemonCodeAnalyzerImpl.getHighlights(highlighter.getDocument(), myProject);
if (highlights == null) return null;
for (HighlightInfo info : highlights) {
if (info.highlighter == highlighter) {
return info;
}
}
private static HighlightInfo findInfo(final RangeHighlighter highlighter) {
Object o = highlighter.getErrorStripeTooltip();
if (o instanceof HighlightInfo) return (HighlightInfo)o;
return null;
}
}
@@ -22,12 +22,12 @@ import com.intellij.codeHighlighting.TextEditorHighlightingPassFactory;
import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.ex.InspectionProfileWrapper;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.Disposable;
import com.intellij.profile.Profile;
import com.intellij.profile.ProfileChangeAdapter;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
@@ -106,4 +106,4 @@ public class WholeFileLocalInspectionsPassFactory extends AbstractProjectCompone
};
}
}
}
@@ -58,12 +58,10 @@ public class DefaultHighlightVisitor implements HighlightVisitor, DumbAware {
public static final ExtensionPointName<HighlightErrorFilter> FILTER_EP_NAME = ExtensionPointName.create("com.intellij.highlightErrorFilter");
private final HighlightErrorFilter[] myErrorFilters;
private final Project myProject;
private final boolean myDumb;
public DefaultHighlightVisitor(Project project) {
myProject = project;
myErrorFilters = Extensions.getExtensions(FILTER_EP_NAME, project);
myDumb = DumbService.getInstance(myProject).isDumb();
}
public boolean suitableForFile(final PsiFile file) {
@@ -122,10 +120,11 @@ public class DefaultHighlightVisitor implements HighlightVisitor, DumbAware {
private void runAnnotators(final PsiElement element, HighlightInfoHolder holder, final AnnotationHolderImpl annotationHolder) {
List<Annotator> annotators = cachedAnnotators.get(element.getLanguage());
if (annotators.isEmpty()) return;
final boolean dumb = DumbService.getInstance(myProject).isDumb();
JobUtil.invokeConcurrentlyUnderMyProgress(annotators, new Processor<Annotator>() {
public boolean process(Annotator annotator) {
if (myDumb && !(annotator instanceof DumbAware)) {
if (dumb && !(annotator instanceof DumbAware)) {
return true;
}
@@ -520,29 +520,28 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec
public void documentChanged(DocumentEvent event) {
final Document document = event.getDocument();
final FileViewProvider viewProvider = getCachedViewProvider(document);
if (viewProvider != null) {
if (viewProvider.getVirtualFile().getFileType().isBinary()) return;
if (viewProvider == null) return;
if (viewProvider.getVirtualFile().getFileType().isBinary()) return;
final List<PsiFile> files = viewProvider.getAllFiles();
boolean commitNecessary = false;
for (PsiFile file : files) {
if (file == null || file instanceof PsiFileImpl && ((PsiFileImpl)file).getTreeElement() == null) continue;
final TextBlock textBlock = getTextBlock(document, file);
if (textBlock.isLocked()) continue;
final List<PsiFile> files = viewProvider.getAllFiles();
boolean commitNecessary = false;
for (PsiFile file : files) {
if (file == null || file instanceof PsiFileImpl && ((PsiFileImpl)file).getTreeElement() == null) continue;
final TextBlock textBlock = getTextBlock(document, file);
if (textBlock.isLocked()) continue;
if (mySmartPointerManager != null) { // mock tests
SmartPointerManagerImpl.unfastenBelts(file);
}
textBlock.documentChanged(event);
assert file instanceof PsiFileImpl : event + "; file="+file+"; allFiles="+files+"; viewProvider="+viewProvider;
myUncommittedDocuments.add(document);
commitNecessary = true;
if (mySmartPointerManager != null) { // mock tests
SmartPointerManagerImpl.unfastenBelts(file);
}
if (commitNecessary && ApplicationManager.getApplication().getCurrentWriteAction(ExternalChangeAction.class) != null){
commitDocument(document);
}
textBlock.documentChanged(event);
assert file instanceof PsiFileImpl : event + "; file="+file+"; allFiles="+files+"; viewProvider="+viewProvider;
myUncommittedDocuments.add(document);
commitNecessary = true;
}
if (commitNecessary && ApplicationManager.getApplication().getCurrentWriteAction(ExternalChangeAction.class) != null){
commitDocument(document);
}
}
@@ -253,6 +253,11 @@ public class UndoManagerImpl extends UndoManager implements ProjectComponent, Ap
clearUndoRedoQueue(DocumentReferenceManager.getInstance().create(file));
}
@TestOnly
public void clearUndoRedoQueueInTests(Document document) {
clearUndoRedoQueue(DocumentReferenceManager.getInstance().create(document));
}
protected void compact() {
if (myCurrentOperationState == NONE && myCommandTimestamp % COMMAND_TO_RUN_COMPACT == 0) {
doCompact();
@@ -70,7 +70,7 @@ public class FoldRegionImpl extends RangeMarkerImpl implements FoldRegion {
}
public boolean isValid() {
return super.isValid() && getStartOffset() + 1 < getEndOffset();
return super.isValid() && myStart + 1 < myEnd;
}
public void setExpandedInternal(boolean toExpand) {
@@ -30,16 +30,14 @@ public class ReadWriteAccessUsageInfo2UsageAdapter extends UsageInfo2UsageAdapte
super(usageInfo);
myAccessedForReading = accessedForReading;
myAccessedForWriting = accessedForWriting;
if (myIcon == null) {
if (myAccessedForReading && myAccessedForWriting) {
myIcon = Icons.VARIABLE_RW_ACCESS;
}
else if (myAccessedForWriting) {
myIcon = Icons.VARIABLE_WRITE_ACCESS; // If icon is changed, don't forget to change UTCompositeUsageNode.getIcon();
}
else if (myAccessedForReading){
myIcon = Icons.VARIABLE_READ_ACCESS; // If icon is changed, don't forget to change UTCompositeUsageNode.getIcon();
}
if (myAccessedForReading && myAccessedForWriting) {
myIcon = Icons.VARIABLE_RW_ACCESS;
}
else if (myAccessedForWriting) {
myIcon = Icons.VARIABLE_WRITE_ACCESS; // If icon is changed, don't forget to change UTCompositeUsageNode.getIcon();
}
else if (myAccessedForReading){
myIcon = Icons.VARIABLE_READ_ACCESS; // If icon is changed, don't forget to change UTCompositeUsageNode.getIcon();
}
}
@@ -21,8 +21,6 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.editor.ex.DocumentEx;
import com.intellij.openapi.editor.impl.PersistentRangeMarker;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
@@ -58,7 +56,10 @@ import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil;
import org.jetbrains.plugins.groovy.refactoring.NameValidator;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* @author ilyas
@@ -85,10 +86,10 @@ public class GroovyMethodInliner implements InlineHandler.Inliner {
Map<PsiElement, String> conflicts = new HashMap<PsiElement, String>();
for (GroovyInlineMethodUtil.ReferenceExpressionInfo info : infos) {
if (!(PsiUtil.isAccessible(call, info.declaration))) {
if (!PsiUtil.isAccessible(call, info.declaration)) {
if (info.declaration instanceof PsiMethod) {
String className = info.containingClass.getName();
String signature = GroovyRefactoringUtil.getMethodSignature(((PsiMethod) info.declaration));
String signature = GroovyRefactoringUtil.getMethodSignature((PsiMethod) info.declaration);
String name = CommonRefactoringUtil.htmlEmphasize(className + "." + signature);
conflicts.put(info.declaration, GroovyRefactoringBundle.message("method.is.not.accessible.form.context.0", name));
} else if (info.declaration instanceof PsiField) {
@@ -147,9 +148,9 @@ public class GroovyMethodInliner implements InlineHandler.Inliner {
qualifier = ((GrParenthesizedExpression) qualifier).getOperand();
}
qualifierDeclaration = factory.createVariableDeclaration(ArrayUtil.EMPTY_STRING_ARRAY, qualifier, null, qualName);
innerQualifier = ((GrReferenceExpression) factory.createExpressionFromText(qualName));
innerQualifier = (GrReferenceExpression) factory.createExpressionFromText(qualName);
} else {
innerQualifier = ((GrReferenceExpression) qualifier);
innerQualifier = (GrReferenceExpression) qualifier;
}
}
}
@@ -159,7 +160,7 @@ public class GroovyMethodInliner implements InlineHandler.Inliner {
if (result != null) {
GrExpression expression = call.replaceWithExpression(result, false);
TextRange range = expression.getTextRange();
return editor != null ? new PersistentRangeMarker((DocumentEx)editor.getDocument(), range.getStartOffset(), range.getEndOffset()) : null;
return editor != null ? editor.getDocument().createRangeMarker(range.getStartOffset(), range.getEndOffset(), true) : null;
}
String resultName = InlineMethodConflictSolver.suggestNewName("result", newMethod, call);
@@ -245,13 +246,13 @@ public class GroovyMethodInliner implements InlineHandler.Inliner {
assert replaced != null;
TextRange range = replaced.getTextRange();
RangeMarker marker = editor != null ? new PersistentRangeMarker((DocumentEx)editor.getDocument(), range.getStartOffset(), range.getEndOffset()) : null;
RangeMarker marker = editor != null ? editor.getDocument().createRangeMarker(range.getStartOffset(), range.getEndOffset(), true) : null;
reformatOwner(owner);
return marker;
} else {
GrStatement stmt;
if (isTailMethodCall && enclosingExpr.getParent() instanceof GrReturnStatement) {
stmt = ((GrReturnStatement) enclosingExpr.getParent());
stmt = (GrReturnStatement) enclosingExpr.getParent();
} else {
stmt = enclosingExpr;
}
@@ -332,7 +333,7 @@ public class GroovyMethodInliner implements InlineHandler.Inliner {
if (statement instanceof GrReturnStatement) {
expr = ((GrReturnStatement) statement).getReturnValue();
} else {
expr = ((GrExpression) statement);
expr = (GrExpression) statement;
}
return expr;
}
@@ -382,7 +383,7 @@ public class GroovyMethodInliner implements InlineHandler.Inliner {
if (element == null) return;
for (PsiElement child : element.getChildren()) {
if (child instanceof GrVariable && !(child instanceof GrParameter)) {
defintions.add(((GrVariable) child));
defintions.add((GrVariable) child);
}
if (!(child instanceof GrClosableBlock)) {
collectInnerDefinitions(child, defintions);
@@ -401,10 +402,10 @@ public class GroovyMethodInliner implements InlineHandler.Inliner {
assert body != null;
GrStatement[] statements = body.getStatements();
if (statements.length == 1) {
if (statements[0] instanceof GrExpression) return ((GrExpression) statements[0]);
if (statements[0] instanceof GrExpression) return (GrExpression) statements[0];
if (statements[0] instanceof GrReturnStatement) {
GrExpression value = ((GrReturnStatement) statements[0]).getReturnValue();
if (value == null && (PsiUtil.getSmartReturnType(method) != PsiType.VOID)) {
if (value == null && PsiUtil.getSmartReturnType(method) != PsiType.VOID) {
return GroovyPsiElementFactory.getInstance(method.getProject()).createExpressionFromText("null");
}
return value;
@@ -21,6 +21,7 @@ import com.intellij.execution.configurations.ConfigurationPerRunnerSettings;
import com.intellij.execution.configurations.RunnerSettings;
import com.intellij.execution.configurations.RuntimeConfigurationException;
import com.intellij.execution.configurations.RuntimeConfigurationWarning;
import com.intellij.execution.runners.ProgramRunner;
import com.intellij.execution.testframework.SourceScope;
import com.intellij.execution.testframework.TestSearchScope;
import com.intellij.openapi.application.ApplicationManager;
@@ -49,6 +50,8 @@ import java.net.Socket;
import java.util.Collection;
public class TestPackage extends TestObject {
private static BackgroundableProcessIndicator mySearchForTestsIndicator;
public TestPackage(final Project project,
final JUnitConfiguration configuration,
RunnerSettings runnerSettings,
@@ -62,6 +65,19 @@ public class TestPackage extends TestObject {
return data.getScope().getSourceScope(myConfiguration);
}
@Override
public ExecutionResult execute(Executor executor, @NotNull ProgramRunner runner) throws ExecutionException {
try {
return super.execute(executor, runner);
}
catch (ExecutionException e) {
if (mySearchForTestsIndicator != null && !mySearchForTestsIndicator.isCanceled()) {
mySearchForTestsIndicator.cancel(); //ensure that search for tests stops anyway
}
throw e;
}
}
protected void initialize() throws ExecutionException {
super.initialize();
final Project project = myConfiguration.getProject();
@@ -244,7 +260,7 @@ public class TestPackage extends TestObject {
}
}
};
ProgressManagerImpl.runProcessWithProgressAsynchronously(task, new BackgroundableProcessIndicator(task) {
mySearchForTestsIndicator = new BackgroundableProcessIndicator(task) {
@Override
public void cancel() {
try {//ensure that serverSocket.accept was interrupted
@@ -257,7 +273,8 @@ public class TestPackage extends TestObject {
}
super.cancel();
}
});
};
ProgressManagerImpl.runProcessWithProgressAsynchronously(task, mySearchForTestsIndicator);
}
private static boolean isSyncSearch() {
@@ -41,7 +41,8 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.module.LanguageLevelUtil;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator;
import com.intellij.openapi.progress.impl.ProgressManagerImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.ex.JavaSdkUtil;
@@ -59,7 +60,10 @@ import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.util.PathUtil;
import com.intellij.util.net.NetUtils;
import com.theoryinpractice.testng.model.*;
import com.theoryinpractice.testng.model.IDEARemoteTestRunnerClient;
import com.theoryinpractice.testng.model.TestData;
import com.theoryinpractice.testng.model.TestNGRemoteListener;
import com.theoryinpractice.testng.model.TestType;
import com.theoryinpractice.testng.ui.TestNGConsoleView;
import com.theoryinpractice.testng.ui.TestNGResults;
import com.theoryinpractice.testng.ui.actions.RerunFailedTestsAction;
@@ -72,9 +76,11 @@ import org.testng.annotations.AfterClass;
import org.testng.remote.strprotocol.MessageHelper;
import javax.swing.*;
import java.io.*;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
public class TestNGRunnableState extends JavaCommandLineState {
@@ -86,6 +92,7 @@ public class TestNGRunnableState extends JavaCommandLineState {
private int port;
private String debugPort;
private File myTempFile;
private BackgroundableProcessIndicator mySearchForTestIndicator;
public TestNGRunnableState(ExecutionEnvironment environment, TestNGConfiguration config) {
super(environment);
@@ -114,9 +121,18 @@ public class TestNGRunnableState extends JavaCommandLineState {
@Override
public ExecutionResult execute(@NotNull final Executor executor, @NotNull final ProgramRunner runner) throws ExecutionException {
OSProcessHandler processHandler = null;
try {
processHandler = startProcess();
}
catch (ExecutionException e) {
if (mySearchForTestIndicator != null && !mySearchForTestIndicator.isCanceled()) {
mySearchForTestIndicator.cancel();
}
throw e;
}
final TestNGConsoleView console = new TestNGConsoleView(config, runnerSettings, myConfigurationPerRunnerSettings);
console.initUI();
OSProcessHandler processHandler = startProcess();
for (RunConfigurationExtension ext : Extensions.getExtensions(RunConfigurationExtension.EP_NAME)) {
ext.handleStartProcess(config, processHandler);
}
@@ -306,7 +322,22 @@ public class TestNGRunnableState extends JavaCommandLineState {
myTempFile = File.createTempFile("idea_testng", ".tmp");
myTempFile.deleteOnExit();
javaParameters.getProgramParametersList().add("-temp", myTempFile.getAbsolutePath());
ProgressManager.getInstance().run(new SearchingForTestsTask(serverSocket, is15, config, myTempFile));
final SearchingForTestsTask task = new SearchingForTestsTask(serverSocket, is15, config, myTempFile);
mySearchForTestIndicator = new BackgroundableProcessIndicator(task) {
@Override
public void cancel() {
try {//ensure that serverSocket.accept was interrupted
if (!serverSocket.isClosed()) {
new Socket(InetAddress.getLocalHost(), serverSocket.getLocalPort());
}
}
catch (Throwable e) {
LOG.info(e);
}
super.cancel();
}
};
ProgressManagerImpl.runProcessWithProgressAsynchronously(task, mySearchForTestIndicator);
}
catch (IOException e) {
LOG.error(e);
@@ -19,9 +19,13 @@ package com.intellij.codeInsight.template.zencoding;
* @author Eugene.Kudelevsky
*/
class NumberToken extends Token {
final int myNumber;
private final int myNumber;
NumberToken(int number) {
myNumber = number;
}
public int getNumber() {
return myNumber;
}
}
@@ -19,9 +19,13 @@ package com.intellij.codeInsight.template.zencoding;
* @author Eugene.Kudelevsky
*/
class OperationToken extends Token {
final char mySign;
private final char mySign;
OperationToken(char sign) {
mySign = sign;
}
public char getSign() {
return mySign;
}
}
@@ -17,6 +17,7 @@ package com.intellij.codeInsight.template.zencoding;
import com.intellij.codeInsight.template.impl.TemplateImpl;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.xml.XmlTag;
import java.util.List;
@@ -27,6 +28,7 @@ public class TemplateToken extends Token {
private final String myKey;
private final List<Pair<String, String>> myAttribute2Value;
private TemplateImpl myTemplate;
private XmlTag myTag;
public TemplateToken(String key, List<Pair<String, String>> attribute2value) {
myKey = key;
@@ -48,4 +50,12 @@ public class TemplateToken extends Token {
public TemplateImpl getTemplate() {
return myTemplate;
}
public XmlTag getTag() {
return myTag;
}
public void setTag(XmlTag tag) {
myTag = tag;
}
}
@@ -25,18 +25,13 @@ import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlChildRole;
import com.intellij.psi.xml.XmlTag;
import com.intellij.psi.xml.XmlToken;
import com.intellij.psi.xml.XmlTokenType;
import com.intellij.psi.xml.*;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.HashSet;
import com.intellij.util.containers.IntArrayList;
import com.intellij.xml.util.HtmlUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -79,7 +74,7 @@ class XmlZenCodingInterpreter {
PsiFile file = myCallback.parseCurrentText(StdFileTypes.XML);
PsiElement element = file.findElementAt(offset);
if (element instanceof XmlToken && ((XmlToken)element).getTokenType() == XmlTokenType.XML_END_TAG_START) {
if (offset < endOfTemplate && element instanceof XmlToken && ((XmlToken)element).getTokenType() == XmlTokenType.XML_END_TAG_START) {
return;
}
@@ -94,7 +89,9 @@ class XmlZenCodingInterpreter {
}
if (newOffset >= 0) {
myCallback.fixEndOffset();
if (offset < endOfTemplate) {
myCallback.fixEndOffset();
}
myCallback.moveToOffset(newOffset);
}
}
@@ -121,7 +118,7 @@ class XmlZenCodingInterpreter {
case OPERATION:
if (templateToken != null) {
if (token instanceof MarkerToken || token instanceof OperationToken) {
final char sign = token instanceof OperationToken ? ((OperationToken)token).mySign : ZenCodingTemplate.MARKER;
final char sign = token instanceof OperationToken ? ((OperationToken)token).getSign() : ZenCodingTemplate.MARKER;
if (sign == '+' || (mySurroundedText == null && sign == ZenCodingTemplate.MARKER)) {
final Object key = new Object();
myCallback.fixStartOfTemplate(key);
@@ -159,7 +156,7 @@ class XmlZenCodingInterpreter {
break;
case NUMBER:
if (token instanceof NumberToken) {
number = ((NumberToken)token).myNumber;
number = ((NumberToken)token).getNumber();
myState = State.AFTER_NUMBER;
}
else {
@@ -168,7 +165,7 @@ class XmlZenCodingInterpreter {
break;
case AFTER_NUMBER:
if (token instanceof MarkerToken || token instanceof OperationToken) {
char sign = token instanceof OperationToken ? ((OperationToken)token).mySign : ZenCodingTemplate.MARKER;
char sign = token instanceof OperationToken ? ((OperationToken)token).getSign() : ZenCodingTemplate.MARKER;
if (sign == '+' || (mySurroundedText == null && sign == ZenCodingTemplate.MARKER)) {
invokeTemplateSeveralTimes(templateToken, 0, number);
templateToken = null;
@@ -231,8 +228,10 @@ class XmlZenCodingInterpreter {
final Object key = new Object();
myCallback.fixStartOfTemplate(key);
for (int i = startIndex; i < count; i++) {
Object iterKey = new Object();
myCallback.fixStartOfTemplate(iterKey);
invokeTemplate(templateToken, myCallback, i);
gotoChild(key);
gotoChild(iterKey);
interpret(myTokens, tailStart, myCallback, State.WORD, mySurroundedText);
if (myCallback.getOffset() != myCallback.getEndOfTemplate(key)) {
myCallback.fixEndOffset();
@@ -242,7 +241,7 @@ class XmlZenCodingInterpreter {
finish();
}
private static boolean containsAttrsVar(TemplateImpl template) {
static boolean containsAttrsVar(TemplateImpl template) {
for (int i = 0; i < template.getVariableCount(); i++) {
String varName = template.getVariableNameAt(i);
if (ATTRS.equals(varName)) {
@@ -271,17 +270,7 @@ class XmlZenCodingInterpreter {
@Nullable
private static Map<String, String> buildPredefinedValues(List<Pair<String, String>> attribute2value, int numberInIteration) {
StringBuilder result = new StringBuilder();
for (Iterator<Pair<String, String>> it = attribute2value.iterator(); it.hasNext();) {
Pair<String, String> pair = it.next();
String name = pair.first;
String value = getValue(pair, numberInIteration);
result.append(name).append("=\"").append(value).append('"');
if (it.hasNext()) {
result.append(' ');
}
}
String attributes = result.toString();
String attributes = buildAttributesString(attribute2value, numberInIteration);
attributes = attributes.length() > 0 ? ' ' + attributes : null;
Map<String, String> predefinedValues = null;
if (attributes != null) {
@@ -291,66 +280,37 @@ class XmlZenCodingInterpreter {
return predefinedValues;
}
private static String getValue(Pair<String, String> pair, int numberInIteration) {
return pair.second.replace(NUMBER_IN_ITERATION_PLACE_HOLDER, Integer.toString(numberInIteration + 1));
@NotNull
private static String buildAttributesString(List<Pair<String, String>> attribute2value, int numberInIteration) {
StringBuilder result = new StringBuilder();
for (Iterator<Pair<String, String>> it = attribute2value.iterator(); it.hasNext();) {
Pair<String, String> pair = it.next();
String name = pair.first;
String value = getValue(pair, numberInIteration);
result.append(getAttributeString(name, value));
if (it.hasNext()) {
result.append(' ');
}
}
return result.toString();
}
@Nullable
private static String addAttrsVar(TemplateImpl modifiedTemplate, XmlTag tag) {
String text = tag.getContainingFile().getText();
PsiElement[] children = tag.getChildren();
if (children.length >= 1 &&
children[0] instanceof XmlToken &&
((XmlToken)children[0]).getTokenType() == XmlTokenType.XML_START_TAG_START) {
PsiElement beforeAttrs = children[0];
if (children.length >= 2 && children[1] instanceof XmlToken && ((XmlToken)children[1]).getTokenType() == XmlTokenType.XML_NAME) {
beforeAttrs = children[1];
}
TextRange range = beforeAttrs.getTextRange();
if (range == null) {
return null;
}
int offset = range.getEndOffset();
text = text.substring(0, offset) + " $ATTRS$" + text.substring(offset);
modifiedTemplate.addVariable(ATTRS, "", "", false);
return text;
}
return null;
private static String getAttributeString(String name, String value) {
return name + "=\"" + value + '"';
}
private static String getValue(Pair<String, String> pair, int numberInIteration) {
return pair.second.replace(NUMBER_IN_ITERATION_PLACE_HOLDER, Integer.toString(numberInIteration + 1));
}
private static void invokeTemplate(TemplateToken token,
final CustomTemplateCallback callback,
int numberInIteration) {
List<Pair<String, String>> attr2value = new ArrayList<Pair<String, String>>(token.getAttribute2Value());
if (callback.isLiveTemplateApplicable(token.getKey())) {
invokeExistingLiveTemplate(token, callback, numberInIteration, attr2value);
}
else {
TemplateImpl template = new TemplateImpl("", "");
template.addTextSegment('<' + token.getKey());
if (attr2value.size() > 0) {
template.addVariable(ATTRS, "", "", false);
template.addVariableSegment(ATTRS);
}
template.addTextSegment(">");
if (XmlZenCodingTemplate.isTrueXml(callback) || !HtmlUtil.isSingleHtmlTag(token.getKey())) {
template.addVariableSegment(TemplateImpl.END);
template.addTextSegment("</" + token.getKey() + ">");
}
template.setToReformat(true);
Map<String, String> predefinedValues = buildPredefinedValues(attr2value, numberInIteration);
callback.expandTemplate(template, predefinedValues);
}
}
private static void invokeExistingLiveTemplate(TemplateToken token,
CustomTemplateCallback callback,
int numberInIteration,
List<Pair<String, String>> attr2value) {
if (token.getTemplate() != null) {
if (attr2value.size() > 0 || XmlZenCodingTemplate.isTrueXml(callback)) {
TemplateImpl modifiedTemplate = token.getTemplate().copy();
XmlTag tag = XmlZenCodingTemplate.parseXmlTagInTemplate(token.getTemplate().getString(), callback, true);
XmlTag tag = token.getTag();
if (tag != null) {
for (Iterator<Pair<String, String>> iterator = attr2value.iterator(); iterator.hasNext();) {
Pair<String, String> pair = iterator.next();
@@ -359,27 +319,7 @@ class XmlZenCodingInterpreter {
iterator.remove();
}
}
if (XmlZenCodingTemplate.isTrueXml(callback)) {
closeUnclosingTags(tag);
}
String text = null;
if (!containsAttrsVar(modifiedTemplate) && attr2value.size() > 0) {
String textWithAttrs = addAttrsVar(modifiedTemplate, tag);
if (textWithAttrs != null) {
text = textWithAttrs;
}
else {
for (Iterator<Pair<String, String>> iterator = attr2value.iterator(); iterator.hasNext();) {
Pair<String, String> pair = iterator.next();
tag.setAttribute(pair.first, getValue(pair, numberInIteration));
iterator.remove();
}
}
}
if (text == null) {
text = tag.getContainingFile().getText();
}
modifiedTemplate.setString(text);
modifiedTemplate.setString(filter(tag, callback));
removeVariablesWhichHasNoSegment(modifiedTemplate);
Map<String, String> predefinedValues = buildPredefinedValues(attr2value, numberInIteration);
callback.expandTemplate(modifiedTemplate, predefinedValues);
@@ -389,11 +329,19 @@ class XmlZenCodingInterpreter {
callback.expandTemplate(token.getTemplate(), null);
}
else {
// for CSS
Map<String, String> predefinedValues = buildPredefinedValues(attr2value, numberInIteration);
callback.expandTemplate(token.getKey(), predefinedValues);
}
}
private static String filter(XmlTag tag, CustomTemplateCallback callback) {
if (XmlZenCodingTemplate.isTrueXml(callback)) {
closeUnclosingTags(tag);
}
return tag.getContainingFile().getText();
}
private static boolean isTagClosed(@NotNull XmlTag tag) {
ASTNode node = tag.getNode();
assert node != null;
@@ -406,15 +354,16 @@ class XmlZenCodingInterpreter {
private static void closeUnclosingTags(@NotNull XmlTag root) {
final List<SmartPsiElementPointer<XmlTag>> tagToClose = new ArrayList<SmartPsiElementPointer<XmlTag>>();
Project project = root.getProject();
final SmartPointerManager manager = SmartPointerManager.getInstance(project);
final SmartPointerManager pointerManager = SmartPointerManager.getInstance(project);
root.accept(new XmlRecursiveElementVisitor() {
@Override
public void visitXmlTag(final XmlTag tag) {
if (!isTagClosed(tag)) {
tagToClose.add(manager.createLazyPointer(tag));
tagToClose.add(pointerManager.createLazyPointer(tag));
}
}
});
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
for (final SmartPsiElementPointer<XmlTag> pointer : tagToClose) {
final XmlTag tag = pointer.getElement();
if (tag != null) {
@@ -424,6 +373,7 @@ class XmlZenCodingInterpreter {
VirtualFile file = tag.getContainingFile().getVirtualFile();
if (file != null) {
final Document document = FileDocumentManager.getInstance().getDocument(file);
documentManager.doPostponedOperationsAndUnblockDocument(document);
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
document.replaceString(offset, tag.getTextRange().getEndOffset(), "/>");
@@ -433,7 +383,7 @@ class XmlZenCodingInterpreter {
}
}
}
PsiDocumentManager.getInstance(project).commitAllDocuments();
documentManager.commitAllDocuments();
}
private static void fail() {
@@ -27,15 +27,18 @@ import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileFactory;
import com.intellij.psi.XmlElementFactory;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.*;
import com.intellij.util.LocalTimeCounter;
import com.intellij.util.containers.HashSet;
import com.intellij.xml.util.HtmlUtil;
import org.apache.xerces.util.XML11Char;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
@@ -192,19 +195,60 @@ public class XmlZenCodingTemplate extends ZenCodingTemplate {
if (useDefaultTag && token.getAttribute2Value().size() == 0) {
return null;
}
if (template != null && (token.getAttribute2Value().size() > 0 || isTrueXml(callback))) {
assert prefix.equals(token.getKey());
token.setTemplate(template);
if (token.getAttribute2Value().size() > 0) {
XmlTag tag = parseXmlTagInTemplate(template.getString(), callback, false);
if (tag == null) {
return null;
}
if (template == null) {
template = generateTagTemplate(token.getKey(), callback);
}
assert prefix.equals(token.getKey());
token.setTemplate(template);
XmlTag tag = parseXmlTagInTemplate(template.getString(), callback, true);
if (token.getAttribute2Value().size() > 0 && tag == null) {
return null;
}
if (tag != null) {
if (!XmlZenCodingInterpreter.containsAttrsVar(template) && token.getAttribute2Value().size() > 0) {
addMissingAttributes(tag, token.getAttribute2Value());
}
token.setTag(tag);
}
return token;
}
private static void addMissingAttributes(XmlTag tag, List<Pair<String, String>> value) {
List<Pair<String, String>> attr2value = new ArrayList<Pair<String, String>>(value);
for (Iterator<Pair<String, String>> iterator = attr2value.iterator(); iterator.hasNext();) {
Pair<String, String> pair = iterator.next();
if (tag.getAttribute(pair.first) != null) {
iterator.remove();
}
}
addAttributesBefore(tag, attr2value);
}
private static void addAttributesBefore(XmlTag tag, List<Pair<String, String>> attr2value) {
XmlAttribute[] attributes = tag.getAttributes();
XmlAttribute firstAttribute = attributes.length > 0 ? attributes[0] : null;
XmlElementFactory factory = XmlElementFactory.getInstance(tag.getProject());
for (Pair<String, String> pair : attr2value) {
XmlAttribute xmlAttribute = factory.createXmlAttribute(pair.first, "");
if (firstAttribute != null) {
tag.addBefore(xmlAttribute, firstAttribute);
}
else {
tag.add(xmlAttribute);
}
}
}
@NotNull
private static TemplateImpl generateTagTemplate(String tagName, CustomTemplateCallback callback) {
StringBuilder builder = new StringBuilder("<");
builder.append(tagName).append('>');
if (isTrueXml(callback) || !HtmlUtil.isSingleHtmlTag(tagName)) {
builder.append("$END$</").append(tagName).append('>');
}
return new TemplateImpl("", builder.toString(), "");
}
@Nullable
static XmlTag parseXmlTagInTemplate(String templateString, CustomTemplateCallback callback, boolean createPhysicalFile) {
XmlFile xmlFile = (XmlFile)PsiFileFactory.getInstance(callback.getProject())
@@ -96,7 +96,7 @@ public abstract class ZenCodingTemplate implements CustomLiveTemplate {
switch (state) {
case OPERATION:
if (token instanceof OperationToken) {
state = ((OperationToken)token).mySign == '*' ? State.NUMBER : State.WORD;
state = ((OperationToken)token).getSign() == '*' ? State.NUMBER : State.WORD;
}
else {
return false;
@@ -119,7 +119,7 @@ public abstract class ZenCodingTemplate implements CustomLiveTemplate {
}
break;
case AFTER_NUMBER:
if (token instanceof OperationToken && ((OperationToken)token).mySign != '*') {
if (token instanceof OperationToken && ((OperationToken)token).getSign() != '*') {
state = State.WORD;
}
else {
@@ -154,16 +154,6 @@ public abstract class ZenCodingTemplate implements CustomLiveTemplate {
protected boolean checkTemplateKey(String key, CustomTemplateCallback callback) {
List<Token> tokens = parse(key, callback);
if (tokens != null && check(tokens)) {
// !! required if Zen Coding if invoked by TemplateManagerImpl action
/*if (tokens.size() == 2) {
Token token = tokens.get(0);
if (token instanceof TemplateToken) {
if (key.equals(((TemplateToken)token).myKey) && callback.isLiveTemplateApplicable(key)) {
// do not activate only live template
return null;
}
}
}*/
return true;
}
return false;