From f301e4c08cda6095df49d197e2b029edcfa3c9d0 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Tue, 21 Apr 2015 14:02:47 +0300 Subject: [PATCH 01/15] IDEA-139441 add ellipsis to the action name --- .../platform-resources-en/src/messages/ActionsBundle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index ba924d074f3c..dff4fb6106f7 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -1167,7 +1167,7 @@ action.ChangesView.RemoveChangeList.text=Delete Changelist action.ChangesView.RemoveChangeList.description=Remove changelist and move all changes to default action.ChangesView.SetDefault.text=Set Active Changelist action.ChangesView.SetDefault.description=Set changelist to which new changes are placed by default -action.ChangesView.Move.text=Move to Another Changelist +action.ChangesView.Move.text=Move to Another Changelist... action.ChangesView.Move.description=Move selected changes to another changelist action.ChangesView.Diff.text=Show Diff action.ChangesView.Diff.description=Show diff for selected change From 6fa018d38674595fa31b632b5a52442da370d09e Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Mon, 20 Apr 2015 18:59:32 +0300 Subject: [PATCH 02/15] comment --- platform/util/src/com/intellij/util/diff/DiffTree.java | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/util/src/com/intellij/util/diff/DiffTree.java b/platform/util/src/com/intellij/util/diff/DiffTree.java index 60f0199f89e7..a5e8ff78b88e 100644 --- a/platform/util/src/com/intellij/util/diff/DiffTree.java +++ b/platform/util/src/com/intellij/util/diff/DiffTree.java @@ -263,6 +263,7 @@ public class DiffTree { if (c11 == CompareResult.DRILL_DOWN_NEEDED) { CharSequence oldText = myOldTree.toString(oldChild); CharSequence newText = myNewTree.toString(newChild); + // drill down only if node texts match, but when they do, match all the way down unconditionally c11 = StringUtil.equals(oldText, newText) ? build(oldChild, newChild, level + 1, DiffTree.emptyConsumer()) : CompareResult.NOT_EQUAL; From 1d96ffead0a19b82ea17e70fe61e5903c6b5bd41 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Mon, 20 Apr 2015 19:02:33 +0300 Subject: [PATCH 03/15] notnull --- .../reference/RefImplicitConstructorImpl.java | 4 ++-- .../codeInspection/reference/RefJavaElementImpl.java | 3 ++- .../intellij/codeInspection/reference/RefMethodImpl.java | 4 ++-- .../intellij/codeInspection/reference/RefPackageImpl.java | 3 ++- .../intellij/codeInspection/reference/RefElementImpl.java | 6 +++--- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefImplicitConstructorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefImplicitConstructorImpl.java index 7dd92822237d..1783d36d0a0b 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefImplicitConstructorImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefImplicitConstructorImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -34,7 +34,7 @@ import org.jetbrains.annotations.Nullable; public class RefImplicitConstructorImpl extends RefMethodImpl implements RefImplicitConstructor { - RefImplicitConstructorImpl(RefClass ownerClass) { + RefImplicitConstructorImpl(@NotNull RefClass ownerClass) { super(InspectionsBundle.message("inspection.reference.implicit.constructor.name", ownerClass.getName()), ownerClass); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaElementImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaElementImpl.java index 12f42b7b3f23..e0d9ff172cd8 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaElementImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaElementImpl.java @@ -47,7 +47,7 @@ public abstract class RefJavaElementImpl extends RefElementImpl implements RefJa private static final int IS_USES_DEPRECATION_MASK = 0x200; private static final int IS_SYNTHETIC_JSP_ELEMENT_MASK = 0x400; - protected RefJavaElementImpl(String name, @NotNull RefJavaElement owner) { + protected RefJavaElementImpl(@NotNull String name, @NotNull RefJavaElement owner) { super(name, owner); String am = owner.getAccessModifier(); doSetAccessModifier(am); @@ -91,6 +91,7 @@ public abstract class RefJavaElementImpl extends RefElementImpl implements RefJa myOutTypeReferences.add(refClass); } + @NotNull public static String getName(PsiElement element) { if (element instanceof PsiAnonymousClass) { PsiAnonymousClass psiAnonymousClass = (PsiAnonymousClass)element; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefMethodImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefMethodImpl.java index 6845c42fd95f..778b6b47d94a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefMethodImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefMethodImpl.java @@ -67,7 +67,7 @@ public class RefMethodImpl extends RefJavaElementImpl implements RefMethod { } // To be used only from RefImplicitConstructor. - protected RefMethodImpl(String name, @NotNull RefClass ownerClass) { + protected RefMethodImpl(@NotNull String name, @NotNull RefClass ownerClass) { super(name, ownerClass); myOwnerClass = ownerClass; ((RefClassImpl)ownerClass).add(this); @@ -79,7 +79,7 @@ public class RefMethodImpl extends RefJavaElementImpl implements RefMethod { } @Override - public void add(RefEntity child) { + public void add(@NotNull RefEntity child) { if (child instanceof RefParameter) { return; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefPackageImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefPackageImpl.java index 32649b924a25..b00f25abb82f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefPackageImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefPackageImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -45,6 +45,7 @@ public class RefPackageImpl extends RefEntityImpl implements RefPackage { return myQualifiedName; } + @NotNull private static String getPackageSuffix(@NotNull String fullName) { int dotIndex = fullName.lastIndexOf('.'); return (dotIndex >= 0) ? fullName.substring(dotIndex + 1) : fullName; diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefElementImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefElementImpl.java index 619ee155b175..947792b2df41 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefElementImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefElementImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -64,7 +64,7 @@ public abstract class RefElementImpl extends RefEntityImpl implements RefElement private final Module myModule; protected static final int IS_REACHABLE_MASK = 0x40; - protected RefElementImpl(String name, @NotNull RefElement owner) { + protected RefElementImpl(@NotNull String name, @NotNull RefElement owner) { super(name, owner.getRefManager()); myID = null; myFlags = 0; @@ -75,7 +75,7 @@ public abstract class RefElementImpl extends RefEntityImpl implements RefElement this(file.getName(), file, manager); } - protected RefElementImpl(String name, @NotNull PsiElement element, @NotNull RefManager manager) { + protected RefElementImpl(@NotNull String name, @NotNull PsiElement element, @NotNull RefManager manager) { super(name, manager); myID = SmartPointerManager.getInstance(manager.getProject()).createSmartPsiElementPointer(element); myFlags = 0; From b9fc3a6d055cd06e5b40096c8712bc209d9a3681 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Mon, 20 Apr 2015 19:06:09 +0300 Subject: [PATCH 04/15] thread safety --- .../reference/RefEntityImpl.java | 68 +++++++++++++------ .../reference/RefManagerImpl.java | 25 ++++++- .../reference/RefModuleImpl.java | 55 +++++++++++---- 3 files changed, 109 insertions(+), 39 deletions(-) diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefEntityImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefEntityImpl.java index 8fb3d4e704c8..fc631f74c246 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefEntityImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefEntityImpl.java @@ -24,8 +24,8 @@ */ package com.intellij.codeInspection.reference; -import com.intellij.codeInspection.InspectionsBundle; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Key; import com.intellij.util.BitUtil; import gnu.trove.THashMap; @@ -36,18 +36,17 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -public abstract class RefEntityImpl implements RefEntity { - private static final String NO_NAME = InspectionsBundle.message("inspection.reference.noname"); - private RefEntityImpl myOwner; - protected List myChildren; +abstract class RefEntityImpl implements RefEntity { + private RefEntityImpl myOwner; // guarded by myManager.myLock + protected List myChildren; // guarded by myManager.myLock private final String myName; private Map myUserMap; protected long myFlags; protected final RefManagerImpl myManager; - protected RefEntityImpl(String name, @NotNull RefManager manager) { + RefEntityImpl(@NotNull String name, @NotNull RefManager manager) { myManager = (RefManagerImpl)manager; - myName = name != null ? name : NO_NAME; + myName = name; myOwner = null; myChildren = null; } @@ -66,32 +65,57 @@ public abstract class RefEntityImpl implements RefEntity { @Override public List getChildren() { - return myChildren; + return myManager.doRead(new Computable>() { + @Override + public List compute() { + return myChildren; + } + }); } @Override public RefEntity getOwner() { - return myOwner; + return myManager.doRead(new Computable() { + @Override + public RefEntity compute() { + return myOwner; + } + }); } - protected void setOwner(RefEntityImpl owner) { - myOwner = owner; + protected void setOwner(final RefEntityImpl owner) { + myManager.doWrite(new Runnable() { + @Override + public void run() { + myOwner = owner; + } + }); } - public void add(RefEntity child) { - if (myChildren == null) { - myChildren = new ArrayList(1); - } + public void add(@NotNull final RefEntity child) { + myManager.doWrite(new Runnable() { + @Override + public void run() { + if (myChildren == null) { + myChildren = new ArrayList(1); + } - myChildren.add(child); - ((RefEntityImpl)child).setOwner(this); + myChildren.add(child); + ((RefEntityImpl)child).setOwner(RefEntityImpl.this); + } + }); } - protected void removeChild(RefEntity child) { - if (myChildren != null) { - myChildren.remove(child); - ((RefEntityImpl)child).setOwner(null); - } + protected void removeChild(@NotNull final RefEntity child) { + myManager.doWrite(new Runnable() { + @Override + public void run() { + if (myChildren != null) { + myChildren.remove(child); + ((RefEntityImpl)child).setOwner(null); + } + } + }); } public String toString() { diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java index 3188e593432a..d11b39cd7504 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java @@ -59,6 +59,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class RefManagerImpl extends RefManager { @@ -85,7 +86,7 @@ public class RefManagerImpl extends RefManager { private final Map myExtensions = new HashMap(); private final Map myLanguageExtensions = new HashMap(); - private final ReentrantReadWriteLock myLock = new ReentrantReadWriteLock(); + private final ReadWriteLock myLock = new ReentrantReadWriteLock(); public RefManagerImpl(@NotNull Project project, @Nullable AnalysisScope scope, @NotNull GlobalInspectionContext context) { myProject = project; @@ -533,9 +534,9 @@ public class RefManagerImpl extends RefManager { protected T getFromRefTableOrCache(final PsiElement element, @NotNull NullableFactory factory, @Nullable Consumer whenCached) { - T result; myLock.readLock().lock(); + T result; try { //noinspection unchecked result = (T)myRefTable.get(ApplicationManager.getApplication().runReadAction( @@ -624,6 +625,26 @@ public class RefManagerImpl extends RefManager { } } + public void doWrite(@NotNull Runnable runnable) { + myLock.writeLock().lock(); + try { + runnable.run(); + } + finally { + myLock.writeLock().unlock(); + } + } + + public T doRead(@NotNull Computable runnable) { + myLock.readLock().lock(); + try { + return runnable.compute(); + } + finally { + myLock.readLock().unlock(); + } + } + @Override public boolean belongsToScope(final PsiElement psiElement) { return belongsToScope(psiElement, false); diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefModuleImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefModuleImpl.java index 353052e9ccfc..10f0a501dfb3 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefModuleImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefModuleImpl.java @@ -1,3 +1,18 @@ +/* + * Copyright 2000-2015 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.codeInspection.reference; import com.intellij.openapi.application.ApplicationManager; @@ -14,32 +29,42 @@ import java.util.ArrayList; * User: anna * Date: 09-Jan-2006 */ -public class RefModuleImpl extends RefEntityImpl implements RefModule { +class RefModuleImpl extends RefEntityImpl implements RefModule { private final Module myModule; - protected RefModuleImpl(@NotNull Module module, @NotNull RefManager manager) { + RefModuleImpl(@NotNull Module module, @NotNull RefManager manager) { super(module.getName(), manager); myModule = module; ((RefProjectImpl)manager.getRefProject()).add(this); } @Override - public void add(RefEntity child) { - if (myChildren == null) { - myChildren = new ArrayList(); - } - myChildren.add(child); + public void add(@NotNull final RefEntity child) { + myManager.doWrite(new Runnable() { + @Override + public void run() { + if (myChildren == null) { + myChildren = new ArrayList(); + } + myChildren.add(child); - if (child.getOwner() == null) { - ((RefEntityImpl)child).setOwner(this); - } + if (child.getOwner() == null) { + ((RefEntityImpl)child).setOwner(RefModuleImpl.this); + } + } + }); } @Override - protected void removeChild(RefEntity child) { - if (myChildren != null) { - myChildren.remove(child); - } + protected void removeChild(@NotNull final RefEntity child) { + myManager.doWrite(new Runnable() { + @Override + public void run() { + if (myChildren != null) { + myChildren.remove(child); + } + } + }); } @Override @@ -69,7 +94,7 @@ public class RefModuleImpl extends RefEntityImpl implements RefModule { } @Nullable - public static RefEntity moduleFromName(final RefManager manager, final String name) { + static RefEntity moduleFromName(final RefManager manager, final String name) { return manager.getRefModule(ModuleManager.getInstance(manager.getProject()).findModuleByName(name)); } } From 780bd1944765659e1f82d757e538feac6e81ce2b Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Mon, 20 Apr 2015 19:06:18 +0300 Subject: [PATCH 05/15] cleanup --- .../src/com/intellij/concurrency/FinallyFuture.java | 4 ++-- .../codeInspection/actions/CodeInspectionAction.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/platform/core-api/src/com/intellij/concurrency/FinallyFuture.java b/platform/core-api/src/com/intellij/concurrency/FinallyFuture.java index c392106befaa..3533249d3a1a 100644 --- a/platform/core-api/src/com/intellij/concurrency/FinallyFuture.java +++ b/platform/core-api/src/com/intellij/concurrency/FinallyFuture.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -78,7 +78,7 @@ public class FinallyFuture implements AsyncFuture { } @Override - public void onFailure(Throwable t) { + public void onFailure(@NotNull Throwable t) { try { myFinallyBlock.execute(); } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/actions/CodeInspectionAction.java b/platform/lang-impl/src/com/intellij/codeInspection/actions/CodeInspectionAction.java index 728137b9c7eb..3c5c9ca8030e 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/actions/CodeInspectionAction.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/actions/CodeInspectionAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -46,8 +46,8 @@ import java.util.Collection; import java.util.TreeSet; public class CodeInspectionAction extends BaseAnalysisAction { - private GlobalInspectionContextImpl myGlobalInspectionContext = null; - protected InspectionProfile myExternalProfile = null; + private GlobalInspectionContextImpl myGlobalInspectionContext; + protected InspectionProfile myExternalProfile; public CodeInspectionAction() { super(InspectionsBundle.message("inspection.action.title"), InspectionsBundle.message("inspection.action.noun")); From 52c3ce11e5a5749dbcf8fe922c5e8db38351f919 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Mon, 20 Apr 2015 19:15:44 +0300 Subject: [PATCH 06/15] more sane API for avoiding leaks in tests --- .../src/com/intellij/tasks/TaskManager.java | 7 ++++++- .../intellij/tasks/impl/TaskManagerImpl.java | 8 ++++++- .../intellij/tasks/TaskManagerTestCase.java | 21 ++++++++++++++++--- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskManager.java b/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskManager.java index 685e211fa669..a6cd7e861b9d 100644 --- a/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskManager.java +++ b/plugins/tasks/tasks-api/src/com/intellij/tasks/TaskManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2010 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -15,6 +15,7 @@ */ package com.intellij.tasks; +import com.intellij.openapi.Disposable; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.AbstractVcs; @@ -136,8 +137,12 @@ public abstract class TaskManager { public abstract void removeTask(LocalTask task); + @Deprecated // use {@code com.intellij.tasks.TaskManager.addTaskListener(com.intellij.tasks.TaskListener, com.intellij.openapi.Disposable)} public abstract void addTaskListener(TaskListener listener); + public abstract void addTaskListener(@NotNull TaskListener listener, @NotNull Disposable parentDisposable); + + @Deprecated // use {@code com.intellij.tasks.TaskManager.addTaskListener(com.intellij.tasks.TaskListener, com.intellij.openapi.Disposable)} public abstract void removeTaskListener(TaskListener listener); // repositories management diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskManagerImpl.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskManagerImpl.java index 2d9919681614..92284d3688a4 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskManagerImpl.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskManagerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -16,6 +16,7 @@ package com.intellij.tasks.impl; import com.intellij.notification.*; +import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.*; import com.intellij.openapi.diagnostic.Logger; @@ -224,6 +225,11 @@ public class TaskManagerImpl extends TaskManager implements ProjectComponent, Pe myDispatcher.addListener(listener); } + @Override + public void addTaskListener(@NotNull TaskListener listener, @NotNull Disposable parentDisposable) { + myDispatcher.addListener(listener, parentDisposable); + } + @Override public void removeTaskListener(TaskListener listener) { myDispatcher.removeListener(listener); diff --git a/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskManagerTestCase.java b/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskManagerTestCase.java index 86a20c5b048f..69f6ddc610af 100644 --- a/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskManagerTestCase.java +++ b/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskManagerTestCase.java @@ -1,3 +1,18 @@ +/* + * Copyright 2000-2015 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.tasks; import com.intellij.tasks.impl.TaskManagerImpl; @@ -16,19 +31,19 @@ public abstract class TaskManagerTestCase extends LightCodeInsightFixtureTestCas IdeaTestCase.initPlatformPrefix(); } - protected TaskManagerImpl myTaskManager; + protected TaskManager myTaskManager; @Override protected void setUp() throws Exception { super.setUp(); - myTaskManager = (TaskManagerImpl)TaskManager.getManager(getProject()); + myTaskManager = TaskManager.getManager(getProject()); removeAllTasks(); } @Override protected void tearDown() throws Exception { try { - myTaskManager.setRepositories(Collections.emptyList()); + ((TaskManagerImpl)myTaskManager).setRepositories(Collections.emptyList()); removeAllTasks(); } finally { From 82d16107202c478f19f4c4b57b89886a1bf68633 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Mon, 20 Apr 2015 20:03:13 +0300 Subject: [PATCH 07/15] more sane API for avoiding leaks in tests --- .../com/intellij/tasks/TaskManagerTest.java | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskManagerTest.java b/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskManagerTest.java index 844dbd5156cb..1a2e00df99eb 100644 --- a/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskManagerTest.java +++ b/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskManagerTest.java @@ -1,3 +1,18 @@ +/* + * Copyright 2000-2015 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.tasks; import com.intellij.notification.Notification; @@ -6,6 +21,7 @@ import com.intellij.notification.NotificationsAdapter; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.util.Ref; import com.intellij.tasks.impl.LocalTaskImpl; +import com.intellij.tasks.impl.TaskManagerImpl; import com.intellij.tasks.impl.TaskProjectConfiguration; import com.intellij.tasks.youtrack.YouTrackRepository; import com.intellij.tasks.youtrack.YouTrackRepositoryType; @@ -21,19 +37,6 @@ import java.util.List; * @author Dmitry Avdeev */ public class TaskManagerTest extends TaskManagerTestCase { - - public void testAddRemoveListener() throws Exception { - - TaskListener listener = new TaskListenerAdapter() { - @Override - public void taskActivated(LocalTask task) { - - } - }; - myTaskManager.addTaskListener(listener); - myTaskManager.removeTaskListener(listener); - } - public void testTaskSwitch() throws Exception { final Ref count = Ref.create(0); @@ -43,7 +46,7 @@ public class TaskManagerTest extends TaskManagerTestCase { count.set(count.get() + 1); } }; - myTaskManager.addTaskListener(listener); + myTaskManager.addTaskListener(listener, myTestRootDisposable); LocalTask localTask = myTaskManager.createLocalTask("foo"); myTaskManager.activateTask(localTask, false); assertEquals(1, count.get().intValue()); @@ -51,8 +54,6 @@ public class TaskManagerTest extends TaskManagerTestCase { LocalTask other = myTaskManager.createLocalTask("bar"); myTaskManager.activateTask(other, false); assertEquals(2, count.get().intValue()); - - myTaskManager.removeTaskListener(listener); } public void testNotifications() throws Exception { @@ -71,7 +72,7 @@ public class TaskManagerTest extends TaskManagerTestCase { throw new Exception(); } }; - myTaskManager.setRepositories(Collections.singletonList(repository)); + ((TaskManagerImpl)myTaskManager).setRepositories(Collections.singletonList(repository)); myTaskManager.updateIssues(null); @@ -85,7 +86,7 @@ public class TaskManagerTest extends TaskManagerTestCase { public void testSharedServers() throws Exception { TaskRepository repository = new YouTrackRepository(new YouTrackRepositoryType()); repository.setShared(true); - myTaskManager.setRepositories(Collections.singletonList(repository)); + ((TaskManagerImpl)myTaskManager).setRepositories(Collections.singletonList(repository)); TaskProjectConfiguration configuration = ServiceManager.getService(getProject(), TaskProjectConfiguration.class); TaskProjectConfiguration state = configuration.getState(); @@ -94,7 +95,7 @@ public class TaskManagerTest extends TaskManagerTestCase { Element element = XmlSerializer.serialize(state); configuration.servers.clear(); - myTaskManager.setRepositories(Collections.emptyList()); + ((TaskManagerImpl)myTaskManager).setRepositories(Collections.emptyList()); configuration.loadState(XmlSerializer.deserialize(element, TaskProjectConfiguration.class)); assertEquals(1, state.servers.size()); @@ -114,7 +115,7 @@ public class TaskManagerTest extends TaskManagerTestCase { return super.getIssues(query, max, since); } }; - myTaskManager.setRepositories(Collections.singletonList(repository)); + ((TaskManagerImpl)myTaskManager).setRepositories(Collections.singletonList(repository)); List issues = myTaskManager.getIssues(""); assertEquals(1, issues.size()); @@ -126,7 +127,7 @@ public class TaskManagerTest extends TaskManagerTestCase { public void testTaskHistoryLength() throws Exception { TestRepository repository = new TestRepository(); - int historyLength = myTaskManager.getState().taskHistoryLength; + int historyLength = ((TaskManagerImpl)myTaskManager).getState().taskHistoryLength; for (int i = 0; i < historyLength + 100; i++) { myTaskManager.addTask(new TaskTestUtil.TaskBuilder(Integer.toString(i), "", repository)); } @@ -136,16 +137,17 @@ public class TaskManagerTest extends TaskManagerTestCase { public void testBranchNameSuggestion() throws Exception { TaskTestUtil.TaskBuilder task = new TaskTestUtil.TaskBuilder("IDEA-666", "Bad news", null); - assertEquals("IDEA-666", myTaskManager.suggestBranchName(task)); - String format = myTaskManager.getState().branchNameFormat; + TaskManagerImpl taskManager = (TaskManagerImpl)myTaskManager; + assertEquals("IDEA-666", taskManager.suggestBranchName(task)); + String format = taskManager.getState().branchNameFormat; try { - myTaskManager.getState().branchNameFormat = "feature/{id}"; - assertEquals("feature/IDEA-666", myTaskManager.suggestBranchName(task)); - myTaskManager.getState().branchNameFormat = "{id}_{summary}"; - assertEquals("IDEA-666_Bad-news", myTaskManager.suggestBranchName(task)); + taskManager.getState().branchNameFormat = "feature/{id}"; + assertEquals("feature/IDEA-666", taskManager.suggestBranchName(task)); + taskManager.getState().branchNameFormat = "{id}_{summary}"; + assertEquals("IDEA-666_Bad-news", taskManager.suggestBranchName(task)); } finally { - myTaskManager.getState().branchNameFormat = format; + taskManager.getState().branchNameFormat = format; } } } From 6425a016cc13c826a164e66cc9ea7cb8e45c76e2 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Mon, 20 Apr 2015 20:07:50 +0300 Subject: [PATCH 08/15] more sane API for avoiding leaks in tests --- .../test/com/intellij/tasks/TaskSettingsTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskSettingsTest.java b/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskSettingsTest.java index e5131274538c..2b8324897569 100644 --- a/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskSettingsTest.java +++ b/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskSettingsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -36,15 +36,15 @@ public class TaskSettingsTest extends TaskManagerTestCase { TaskRepository repository = new YouTrackRepository(); String format = "foo \n bar"; repository.setCommitMessageFormat(format); - myTaskManager.setRepositories(Collections.singletonList(repository)); - TaskManagerImpl.Config config = myTaskManager.getState(); + ((TaskManagerImpl)myTaskManager).setRepositories(Collections.singletonList(repository)); + TaskManagerImpl.Config config = ((TaskManagerImpl)myTaskManager).getState(); Element element = XmlSerializer.serialize(config); ByteArrayOutputStream stream = new ByteArrayOutputStream(); JDOMUtil.writeDocument(new Document(element), stream, "\n"); Element element1 = JDOMUtil.load(new ByteArrayInputStream(stream.toByteArray())); TaskManagerImpl.Config deserialize = XmlSerializer.deserialize(element1, TaskManagerImpl.Config.class); - myTaskManager.loadState(deserialize); + ((TaskManagerImpl)myTaskManager).loadState(deserialize); TaskRepository[] repositories = myTaskManager.getAllRepositories(); assertEquals(format, repositories[0].getCommitMessageFormat()); From e7bd68e07cb9577b4bec9437ccbef721aecde7ae Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Mon, 20 Apr 2015 20:48:24 +0300 Subject: [PATCH 09/15] compilation --- .../tasks-tests/test/com/intellij/tasks/TaskManagerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskManagerTest.java b/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskManagerTest.java index 1a2e00df99eb..1e79fdf0ef4b 100644 --- a/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskManagerTest.java +++ b/plugins/tasks/tasks-tests/test/com/intellij/tasks/TaskManagerTest.java @@ -100,7 +100,7 @@ public class TaskManagerTest extends TaskManagerTestCase { configuration.loadState(XmlSerializer.deserialize(element, TaskProjectConfiguration.class)); assertEquals(1, state.servers.size()); - myTaskManager.projectOpened(); + ((TaskManagerImpl)myTaskManager).projectOpened(); TaskRepository[] repositories = myTaskManager.getAllRepositories(); assertEquals(1, repositories.length); From 4ba05e2c09798fec5cd5e148fce51a305cb0b2bf Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Tue, 21 Apr 2015 13:47:01 +0200 Subject: [PATCH 10/15] ensure output is flushed in EDT --- .../ant/config/execution/AntBuildMessageView.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/plugins/ant/src/com/intellij/lang/ant/config/execution/AntBuildMessageView.java b/plugins/ant/src/com/intellij/lang/ant/config/execution/AntBuildMessageView.java index cff89bc70134..b79a1c09e7eb 100644 --- a/plugins/ant/src/com/intellij/lang/ant/config/execution/AntBuildMessageView.java +++ b/plugins/ant/src/com/intellij/lang/ant/config/execution/AntBuildMessageView.java @@ -841,11 +841,16 @@ public final class AntBuildMessageView extends JPanel implements DataProvider, O } }); //noinspection SSBasedInspection - DumbService.getInstance(myProject).runWhenSmart(new Runnable() { + SwingUtilities.invokeLater(new Runnable() { public void run() { - if (!myIsOutputPaused) { - new OutputFlusher().doFlush(); - } + DumbService.getInstance(myProject).runWhenSmart(new Runnable() { + @Override + public void run() { + if (!myIsOutputPaused) { + new OutputFlusher().doFlush(); + } + } + }); } }); } From 50a383585d445d00b82012a8159fb09b789ba04f Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Fri, 17 Apr 2015 14:40:10 +0300 Subject: [PATCH 11/15] sentence capitalization for familyName --- .../analysis-api/src/com/intellij/codeInspection/QuickFix.java | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/analysis-api/src/com/intellij/codeInspection/QuickFix.java b/platform/analysis-api/src/com/intellij/codeInspection/QuickFix.java index f8c670a98519..ae6b97a0421e 100644 --- a/platform/analysis-api/src/com/intellij/codeInspection/QuickFix.java +++ b/platform/analysis-api/src/com/intellij/codeInspection/QuickFix.java @@ -43,6 +43,7 @@ public interface QuickFix { * if the name of the quickfix is "Create template <filename>", the return value of getFamilyName() should be "Create template". * If the name of the quickfix does not depend on a specific element, simply return getName(). */ + @Nls(capitalization = Nls.Capitalization.Sentence) @NotNull String getFamilyName(); From aabeed1f3133bcb436d9e5c259bd036ce0c61b94 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Mon, 20 Apr 2015 17:36:22 +0300 Subject: [PATCH 12/15] cleanup --- .../codeInsight/completion/XmlCompletionContributor.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/xml/impl/src/com/intellij/codeInsight/completion/XmlCompletionContributor.java b/xml/impl/src/com/intellij/codeInsight/completion/XmlCompletionContributor.java index cc066a921447..7729e844ad78 100644 --- a/xml/impl/src/com/intellij/codeInsight/completion/XmlCompletionContributor.java +++ b/xml/impl/src/com/intellij/codeInsight/completion/XmlCompletionContributor.java @@ -189,10 +189,7 @@ public class XmlCompletionContributor extends CompletionContributor { if (isXmlNameCompletion(parameters) && parameters.getCompletionType() == CompletionType.BASIC) { if (FeatureUsageTracker.getInstance().isToBeAdvertisedInLookup(TAG_NAME_COMPLETION_FEATURE, parameters.getPosition().getProject())) { final String shortcut = getActionShortcut(IdeActions.ACTION_CODE_COMPLETION); - if (shortcut != null) { - return XmlBundle.message("tag.name.completion.hint", shortcut); - } - + return XmlBundle.message("tag.name.completion.hint", shortcut); } } return super.advertise(parameters); From a830bbc2e7e9c817f86c047af8fc5be4b9872498 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Mon, 20 Apr 2015 19:41:42 +0300 Subject: [PATCH 13/15] cleanup --- xml/xml-psi-api/src/com/intellij/javaee/ResourceRegistrar.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xml/xml-psi-api/src/com/intellij/javaee/ResourceRegistrar.java b/xml/xml-psi-api/src/com/intellij/javaee/ResourceRegistrar.java index 35c0be48466d..43317c9c80bb 100644 --- a/xml/xml-psi-api/src/com/intellij/javaee/ResourceRegistrar.java +++ b/xml/xml-psi-api/src/com/intellij/javaee/ResourceRegistrar.java @@ -20,7 +20,7 @@ import org.jetbrains.annotations.NonNls; /** * @author Dmitry Avdeev - * @see com.intellij.javaee.StandardResourceProvider + * @see StandardResourceProvider */ public interface ResourceRegistrar { From 59a988263e702937e4758657415c558714e72a96 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Tue, 21 Apr 2015 14:53:18 +0300 Subject: [PATCH 14/15] cleanup --- .../src/com/intellij/html/impl/util/MicrodataUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xml/xml-psi-impl/src/com/intellij/html/impl/util/MicrodataUtil.java b/xml/xml-psi-impl/src/com/intellij/html/impl/util/MicrodataUtil.java index 8fae0a1b2057..b34326477307 100644 --- a/xml/xml-psi-impl/src/com/intellij/html/impl/util/MicrodataUtil.java +++ b/xml/xml-psi-impl/src/com/intellij/html/impl/util/MicrodataUtil.java @@ -136,7 +136,7 @@ public class MicrodataUtil { return PsiReference.EMPTY_ARRAY; } String text = element.getText(); - String urls = StringUtil.stripQuotesAroundValue(text); + String urls = StringUtil.unquoteString(text); StringTokenizer tokenizer = new StringTokenizer(urls); List result = new ArrayList(); while (tokenizer.hasMoreTokens()) { @@ -153,7 +153,7 @@ public class MicrodataUtil { @Nullable public static String getStripedAttributeValue(@Nullable XmlTag tag, @Nls String attributeName) { String value = tag != null ? tag.getAttributeValue(attributeName) : null; - return value != null ? StringUtil.stripQuotesAroundValue(value) : null; + return value != null ? StringUtil.unquoteString(value) : null; } private static class CollectNamesVisitor extends XmlRecursiveElementVisitor { From fd1e74519885a1551ac7e415ecb0e45f455c2455 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Tue, 21 Apr 2015 14:54:57 +0300 Subject: [PATCH 15/15] IDEA-42074 Code completion for 's attributes should base on the attribute: schemaLocation support --- .../src/META-INF/XmlPlugin.xml | 1 + .../XmlLocationCompletionContributor.java | 49 +++++++++++++++++++ .../completion/XmlCompletionTest.java | 7 +++ .../testData/completion/SchemaLocation.xml | 6 +++ .../testData/completion/SchemaLocation2.xml | 6 +++ .../javaee/CoreExternalResourceManager.java | 6 +++ .../javaee/ExternalResourceManagerEx.java | 3 ++ .../javaee/ExternalResourceManagerExImpl.java | 35 +++++++++++++ .../impl/providers/DependentNSReference.java | 15 ++++-- .../impl/providers/URIReferenceProvider.java | 38 ++++++++++++-- .../intellij/xml/index/XmlNamespaceIndex.java | 28 +++++++---- 11 files changed, 176 insertions(+), 18 deletions(-) create mode 100644 xml/impl/src/com/intellij/codeInsight/completion/XmlLocationCompletionContributor.java create mode 100644 xml/tests/testData/completion/SchemaLocation.xml create mode 100644 xml/tests/testData/completion/SchemaLocation2.xml diff --git a/platform/platform-resources/src/META-INF/XmlPlugin.xml b/platform/platform-resources/src/META-INF/XmlPlugin.xml index 9970af827d61..87eed975a9bc 100644 --- a/platform/platform-resources/src/META-INF/XmlPlugin.xml +++ b/platform/platform-resources/src/META-INF/XmlPlugin.xml @@ -130,6 +130,7 @@ + diff --git a/xml/impl/src/com/intellij/codeInsight/completion/XmlLocationCompletionContributor.java b/xml/impl/src/com/intellij/codeInsight/completion/XmlLocationCompletionContributor.java new file mode 100644 index 000000000000..3022d9e9e699 --- /dev/null +++ b/xml/impl/src/com/intellij/codeInsight/completion/XmlLocationCompletionContributor.java @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2015 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.codeInsight.completion; + +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.javaee.ExternalResourceManagerEx; +import com.intellij.psi.PsiReference; +import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference; +import com.intellij.psi.impl.source.resolve.reference.impl.providers.DependentNSReference; +import com.intellij.util.containers.MultiMap; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +/** + * @author Dmitry Avdeev + */ +public class XmlLocationCompletionContributor extends CompletionContributor { + + @Override + public void fillCompletionVariants(@NotNull CompletionParameters parameters, @NotNull CompletionResultSet result) { + PsiReference reference = parameters.getPosition().getContainingFile().findReferenceAt(parameters.getOffset()); + if (reference instanceof PsiMultiReference) reference = ((PsiMultiReference)reference).getReferences()[0]; + if (reference instanceof DependentNSReference) { + MultiMap map = ExternalResourceManagerEx.getInstanceEx().getUrlsByNamespace(parameters.getOriginalFile().getProject()); + String namespace = ((DependentNSReference)reference).getNamespaceReference().getCanonicalText(); + Collection strings = map.get(namespace); + for (String string : strings) { + if (!namespace.equals(string)) { // exclude namespaces from location urls + result.consume(PrioritizedLookupElement.withPriority(LookupElementBuilder.create(string), 100)); + } + } + if (!strings.isEmpty()) result.stopHere(); + } + } +} diff --git a/xml/tests/src/com/intellij/codeInsight/completion/XmlCompletionTest.java b/xml/tests/src/com/intellij/codeInsight/completion/XmlCompletionTest.java index 2b6b36c44197..68df40c14942 100644 --- a/xml/tests/src/com/intellij/codeInsight/completion/XmlCompletionTest.java +++ b/xml/tests/src/com/intellij/codeInsight/completion/XmlCompletionTest.java @@ -712,5 +712,12 @@ public class XmlCompletionTest extends LightCodeInsightFixtureTestCase { "xml:lang", "xml:space"); } + + public void testSchemaLocation() throws Exception { + myFixture.configureByFiles("spring-beans.xsd"); + myFixture.testCompletionVariants("SchemaLocation.xml", "http://www.springframework.org/schema/beans ", + "http://www.w3.org/2001/XMLSchema ", "http://www.w3.org/2001/XMLSchema-instance "); + myFixture.testCompletionVariants("SchemaLocation2.xml", "http://www.w3.org/2001/XMLSchema.xsd"); + } } diff --git a/xml/tests/testData/completion/SchemaLocation.xml b/xml/tests/testData/completion/SchemaLocation.xml new file mode 100644 index 000000000000..e753d15f8af6 --- /dev/null +++ b/xml/tests/testData/completion/SchemaLocation.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/xml/tests/testData/completion/SchemaLocation2.xml b/xml/tests/testData/completion/SchemaLocation2.xml new file mode 100644 index 000000000000..b6b2a642ce95 --- /dev/null +++ b/xml/tests/testData/completion/SchemaLocation2.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/xml/xml-psi-impl/src/com/intellij/javaee/CoreExternalResourceManager.java b/xml/xml-psi-impl/src/com/intellij/javaee/CoreExternalResourceManager.java index c3ebfe17f070..8c86d5f84679 100644 --- a/xml/xml-psi-impl/src/com/intellij/javaee/CoreExternalResourceManager.java +++ b/xml/xml-psi-impl/src/com/intellij/javaee/CoreExternalResourceManager.java @@ -19,6 +19,7 @@ import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; +import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -133,6 +134,11 @@ public class CoreExternalResourceManager extends ExternalResourceManagerEx { return 0; } + @Override + public MultiMap getUrlsByNamespace(Project project) { + throw new UnsupportedOperationException(); + } + @Override public void addResource(@NotNull @NonNls String url, @NonNls String location) { throw new UnsupportedOperationException(); diff --git a/xml/xml-psi-impl/src/com/intellij/javaee/ExternalResourceManagerEx.java b/xml/xml-psi-impl/src/com/intellij/javaee/ExternalResourceManagerEx.java index e8b7ee614536..dbd4f2fe5fb5 100644 --- a/xml/xml-psi-impl/src/com/intellij/javaee/ExternalResourceManagerEx.java +++ b/xml/xml-psi-impl/src/com/intellij/javaee/ExternalResourceManagerEx.java @@ -17,6 +17,7 @@ package com.intellij.javaee; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -72,4 +73,6 @@ public abstract class ExternalResourceManagerEx extends ExternalResourceManager public abstract void setCatalogPropertiesFile(@Nullable String filePath); public abstract long getModificationCount(@NotNull Project project); + + public abstract MultiMap getUrlsByNamespace(Project project); } diff --git a/xml/xml-psi-impl/src/com/intellij/javaee/ExternalResourceManagerExImpl.java b/xml/xml-psi-impl/src/com/intellij/javaee/ExternalResourceManagerExImpl.java index 93829e962d63..fe532b04ca7a 100644 --- a/xml/xml-psi-impl/src/com/intellij/javaee/ExternalResourceManagerExImpl.java +++ b/xml/xml-psi-impl/src/com/intellij/javaee/ExternalResourceManagerExImpl.java @@ -26,13 +26,18 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.*; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; +import com.intellij.psi.util.CachedValueProvider; +import com.intellij.psi.util.CachedValuesManager; import com.intellij.psi.xml.XmlFile; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MultiMap; import com.intellij.xml.Html5SchemaProvider; import com.intellij.xml.XmlSchemaProvider; +import com.intellij.xml.index.XmlNamespaceIndex; import com.intellij.xml.util.XmlUtil; import gnu.trove.THashMap; import gnu.trove.THashSet; @@ -73,6 +78,31 @@ public class ExternalResourceManagerExImpl extends ExternalResourceManagerEx imp } }; + private final CachedValueProvider> myUrlByNamespaceProvider = new CachedValueProvider>() { + @Nullable + @Override + public CachedValueProvider.Result> compute() { + MultiMap result = new MultiMap(); + + Collection> values = myStandardResources.getValue().values(); + for (Map map : values) { + for (Map.Entry entry : map.entrySet()) { + String url = entry.getValue().getResourceUrl(); + if (url != null) { + VirtualFile file = VfsUtilCore.findRelativeFile(url, null); + if (file != null) { + String namespace = XmlNamespaceIndex.computeNamespace(file); + if (namespace != null) { + result.putValue(namespace, entry.getKey()); + } + } + } + } + } + return CachedValueProvider.Result.create(result, ExternalResourceManagerExImpl.this); + } + }; + private String myDefaultHtmlDoctype = HTML5_DOCTYPE_ELEMENT; private String myCatalogPropertiesFile; @@ -535,6 +565,11 @@ public class ExternalResourceManagerExImpl extends ExternalResourceManagerEx imp incModificationCount(); } + @Override + public MultiMap getUrlsByNamespace(Project project) { + return CachedValuesManager.getManager(project).getCachedValue(project, myUrlByNamespaceProvider); + } + @Nullable private XMLCatalogManager getCatalogManager() { if (myCatalogManager == null && myCatalogPropertiesFile != null) { diff --git a/xml/xml-psi-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/DependentNSReference.java b/xml/xml-psi-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/DependentNSReference.java index 944ca40e0563..882a325cebd3 100644 --- a/xml/xml-psi-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/DependentNSReference.java +++ b/xml/xml-psi-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/DependentNSReference.java @@ -20,20 +20,22 @@ import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.util.ArrayUtil; +import com.intellij.xml.util.XmlUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class DependentNSReference extends BasicAttributeValueReference { + @NotNull private final URLReference myReference; private final boolean myForceFetchResultValid; - public DependentNSReference(final PsiElement element, TextRange range, URLReference ref) { + public DependentNSReference(final PsiElement element, TextRange range, @NotNull URLReference ref) { this(element, range, ref, false); } public DependentNSReference(final PsiElement element, TextRange range, - URLReference ref, + @NotNull URLReference ref, boolean valid) { super(element, range); myReference = ref; @@ -46,7 +48,9 @@ public class DependentNSReference extends BasicAttributeValueReference { final String canonicalText = getCanonicalText(); final PsiFile file = ExternalResourceManager.getInstance().getResourceLocation(canonicalText, myElement.getContainingFile(), null); if (file != null) return file; - return myReference.resolve(); + PsiElement element = myReference.resolve(); + if (element == null && !myForceFetchResultValid && !XmlUtil.isUrlText(canonicalText, myElement.getProject())) return myElement; // file reference will highlight it + return element; } @Override @@ -63,4 +67,9 @@ public class DependentNSReference extends BasicAttributeValueReference { public boolean isForceFetchResultValid() { return myForceFetchResultValid; } + + @NotNull + public URLReference getNamespaceReference() { + return myReference; + } } diff --git a/xml/xml-psi-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/URIReferenceProvider.java b/xml/xml-psi-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/URIReferenceProvider.java index 3f712e0cff0c..a6e65745484e 100644 --- a/xml/xml-psi-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/URIReferenceProvider.java +++ b/xml/xml-psi-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/URIReferenceProvider.java @@ -15,6 +15,7 @@ */ package com.intellij.psi.impl.source.resolve.reference.impl.providers; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; @@ -22,6 +23,9 @@ import com.intellij.psi.PsiReference; import com.intellij.psi.PsiReferenceProvider; import com.intellij.psi.filters.ElementFilter; import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.ArrayUtil; +import com.intellij.util.Function; import com.intellij.util.ProcessingContext; import com.intellij.util.containers.ContainerUtil; import com.intellij.xml.util.XmlUtil; @@ -60,7 +64,7 @@ public class URIReferenceProvider extends PsiReferenceProvider { @NotNull public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull final ProcessingContext context) { final String text = element.getText(); - String s = StringUtil.stripQuotesAroundValue(text); + String s = StringUtil.unquoteString(text); final PsiElement parent = element.getParent(); if (parent instanceof XmlAttribute && @@ -72,14 +76,37 @@ public class URIReferenceProvider extends PsiReferenceProvider { while(tokenizer.hasMoreElements()) { final String namespace = tokenizer.nextToken(); int offset = text.indexOf(namespace); - final URLReference urlReference = new URLReference(element, new TextRange(offset, offset + namespace.length()), true); + TextRange range = new TextRange(offset, offset + namespace.length()); + final URLReference urlReference = new URLReference(element, range, true) { + @NotNull + @Override + public Object[] getVariants() { + XmlTag tag = (XmlTag)parent.getParent(); + XmlAttribute[] attributes = tag.getAttributes(); + + return ContainerUtil.mapNotNull(attributes, new Function() { + @Override + public Object fun(final XmlAttribute attribute) { + final String attributeValue = attribute.getValue(); + return attributeValue != null && + attribute.isNamespaceDeclaration() && + ContainerUtil.find(refs, new Condition() { + @Override + public boolean value(PsiReference ref) { + return ref.getCanonicalText().equals(attributeValue); + } + }) == null ? attributeValue + " " : null; + } + }, ArrayUtil.EMPTY_OBJECT_ARRAY); + } + }; refs.add(urlReference); if (!tokenizer.hasMoreElements()) break; String url = tokenizer.nextToken(); offset = text.indexOf(url); - if (XmlUtil.isUrlText(url, element.getProject())) refs.add(new DependentNSReference(element, new TextRange(offset,offset + url.length()), urlReference)); - else { + refs.add(new DependentNSReference(element, new TextRange(offset,offset + url.length()), urlReference)); + if (!XmlUtil.isUrlText(url, element.getProject())) { ContainerUtil.addAll(refs, new FileReferenceSet(url, element, offset, this, false).getAllReferences()); } } @@ -94,6 +121,7 @@ public class URIReferenceProvider extends PsiReferenceProvider { return new FileReferenceSet(s,element,text.indexOf(s), this,true).getAllReferences(); } + static PsiReference getUrlReference(PsiElement element, String s) { PsiElement parent = element.getParent(); if (XmlUtil.isUrlText(s, element.getProject()) || @@ -106,7 +134,7 @@ public class URIReferenceProvider extends PsiReferenceProvider { if (!s.startsWith(XmlUtil.TAG_DIR_NS_PREFIX)) { boolean namespaceSoftRef = parent instanceof XmlAttribute && NAMESPACE_ATTR_NAME.equals(((XmlAttribute)parent).getName()) && - ((XmlAttribute)parent).getParent().getAttributeValue("schemaLocation") != null; + ((XmlAttribute)parent).getParent().getAttributeValue(XmlUtil.SCHEMA_LOCATION_ATT) != null; if (!namespaceSoftRef && parent instanceof XmlAttribute && ((XmlAttribute)parent).isNamespaceDeclaration()) { namespaceSoftRef = parent.getContainingFile().getContext() != null; } diff --git a/xml/xml-psi-impl/src/com/intellij/xml/index/XmlNamespaceIndex.java b/xml/xml-psi-impl/src/com/intellij/xml/index/XmlNamespaceIndex.java index 2248c41da6ac..8f1ee85139a0 100644 --- a/xml/xml-psi-impl/src/com/intellij/xml/index/XmlNamespaceIndex.java +++ b/xml/xml-psi-impl/src/com/intellij/xml/index/XmlNamespaceIndex.java @@ -20,6 +20,7 @@ import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.io.StreamUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.psi.xml.XmlFile; @@ -36,10 +37,7 @@ import com.intellij.xml.util.XmlUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.DataInput; -import java.io.DataOutput; -import java.io.File; -import java.io.IOException; +import java.io.*; import java.util.*; /** @@ -50,17 +48,27 @@ public class XmlNamespaceIndex extends XmlIndex { @Nullable public static String getNamespace(@NotNull VirtualFile file, final Project project, PsiFile context) { if (DumbService.isDumb(project) || (context != null && XmlUtil.isStubBuilding())) { - try { - return XsdNamespaceBuilder.computeNamespace(file.getInputStream()); - } - catch (IOException e) { - return null; - } + return computeNamespace(file); } final List list = FileBasedIndex.getInstance().getValues(NAME, file.getUrl(), createFilter(project)); return list.size() == 0 ? null : list.get(0).getNamespace(); } + @Nullable + public static String computeNamespace(@NotNull VirtualFile file) { + InputStream stream = null; + try { + stream = file.getInputStream(); + return XsdNamespaceBuilder.computeNamespace(stream); + } + catch (IOException e) { + return null; + } + finally { + StreamUtil.closeStream(stream); + } + } + public static List> getResourcesByNamespace(String namespace, @NotNull Project project, @Nullable Module module) {