diff --git a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/GeneralHighlightingPass.java b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/GeneralHighlightingPass.java index cfddc716f0a3..c271e27debc1 100644 --- a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/GeneralHighlightingPass.java +++ b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/GeneralHighlightingPass.java @@ -23,7 +23,6 @@ import com.intellij.codeInsight.daemon.impl.analysis.CustomHighlightInfoHolder; import com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder; import com.intellij.codeInsight.daemon.impl.analysis.HighlightingLevelManager; import com.intellij.codeInsight.problems.ProblemImpl; -import com.intellij.concurrency.JobScheduler; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; @@ -50,6 +49,7 @@ import com.intellij.psi.search.TodoItem; import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.NotNullProducer; import com.intellij.util.SmartList; +import com.intellij.util.concurrency.EdtExecutorService; import com.intellij.util.containers.Stack; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; @@ -428,18 +428,10 @@ public class GeneralHighlightingPass extends ProgressableTextEditorHighlightingP private static void cancelAndRestartDaemonLater(@NotNull ProgressIndicator progress, @NotNull final Project project) throws ProcessCanceledException { progress.cancel(); - JobScheduler.getScheduler().schedule(new Runnable() { - @Override - public void run() { - Application application = ApplicationManager.getApplication(); - if (!project.isDisposed() && !application.isDisposed() && !application.isUnitTestMode()) { - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - DaemonCodeAnalyzer.getInstance(project).restart(); - } - }, project.getDisposed()); - } + EdtExecutorService.getScheduledExecutorInstance().schedule((Runnable)() -> { + Application application = ApplicationManager.getApplication(); + if (!project.isDisposed() && !application.isDisposed() && !application.isUnitTestMode()) { + DaemonCodeAnalyzer.getInstance(project).restart(); } }, RESTART_DAEMON_RANDOM.nextInt(100), TimeUnit.MILLISECONDS); throw new ProcessCanceledException(); diff --git a/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java b/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java index 423684a31408..bef6ce81aabd 100644 --- a/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java +++ b/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java @@ -94,7 +94,20 @@ public abstract class TransactionGuard { * @param transaction code to execute inside a transaction. */ public static void submitTransaction(@NotNull Runnable transaction) { - getInstance().submitMergeableTransaction(TransactionKind.NO_MERGE, transaction); + getInstance().submitMergeableTransaction(TransactionKind.ANY_CHANGE, transaction); + } + + /** + * Runs the given code synchronously inside a transaction. Fails if transactions of given kind are not allowed at this moment. + * @see #startSynchronousTransaction(TransactionKind) + */ + public static void syncTransaction(@NotNull TransactionKind kind, @NotNull Runnable transaction) { + AccessToken token = getInstance().startSynchronousTransaction(kind); + try { + transaction.run(); + } finally { + token.finish(); + } } /** diff --git a/platform/core-api/src/com/intellij/openapi/application/TransactionKind.java b/platform/core-api/src/com/intellij/openapi/application/TransactionKind.java index bc7d3be0da3b..6a666b98b0ad 100644 --- a/platform/core-api/src/com/intellij/openapi/application/TransactionKind.java +++ b/platform/core-api/src/com/intellij/openapi/application/TransactionKind.java @@ -27,11 +27,6 @@ public interface TransactionKind { */ TransactionKind TEXT_EDITING = Common.TEXT_EDITING; - /** - * Same as {@link Common#NO_MERGE} - */ - TransactionKind NO_MERGE = Common.NO_MERGE; - /** * Same as {@link Common#ANY_CHANGE} */ @@ -56,11 +51,7 @@ public interface TransactionKind { *
  • Project root set change *
  • Dumb mode (reindexing) start/finish, (see {@link com.intellij.openapi.project.DumbService}). */ - ANY_CHANGE, + ANY_CHANGE - /** - * Transactions of this kind won't be merged into other transactions - */ - NO_MERGE } } diff --git a/platform/core-api/src/com/intellij/openapi/application/WrapInTransaction.java b/platform/core-api/src/com/intellij/openapi/application/WrapInTransaction.java index b42f547b326f..63a839a91460 100644 --- a/platform/core-api/src/com/intellij/openapi/application/WrapInTransaction.java +++ b/platform/core-api/src/com/intellij/openapi/application/WrapInTransaction.java @@ -18,5 +18,5 @@ public @interface WrapInTransaction { /** * @return the kind of transaction to wrap the action into. By default, it's {@link TransactionKind#NO_MERGE}. */ - TransactionKind.Common value() default TransactionKind.Common.NO_MERGE; + TransactionKind.Common value() default TransactionKind.Common.ANY_CHANGE; } diff --git a/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java b/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java index 43ca3fb43d63..0f2456282efd 100644 --- a/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java @@ -41,7 +41,7 @@ public class TransactionGuardImpl extends TransactionGuard { @NotNull public AccessToken startSynchronousTransaction(@NotNull TransactionKind kind) throws IllegalStateException { ApplicationManager.getApplication().assertIsDispatchThread(); - if (kind != TransactionKind.NO_MERGE && myMergeableKinds.contains(kind)) { + if (myMergeableKinds.contains(kind)) { return AccessToken.EMPTY_ACCESS_TOKEN; } if (myTransactionStartTrace != null) { @@ -71,7 +71,7 @@ public class TransactionGuardImpl extends TransactionGuard { Runnable next = myQueue.poll(); if (next != null) { - runSyncTransaction(TransactionKind.NO_MERGE, next); + runSyncTransaction(TransactionKind.ANY_CHANGE, next); } } }, app.getDisposed()); @@ -118,7 +118,7 @@ public class TransactionGuardImpl extends TransactionGuard { } protected boolean canRunTransactionNow(@NotNull TransactionKind kind) { - return !isInsideTransaction() || kind != TransactionKind.NO_MERGE && myMergeableKinds.contains(kind); + return !isInsideTransaction() || myMergeableKinds.contains(kind); } @Override diff --git a/platform/core-impl/src/com/intellij/pom/core/impl/PomModelImpl.java b/platform/core-impl/src/com/intellij/pom/core/impl/PomModelImpl.java index a45b88b060e9..d28b1c3241ed 100644 --- a/platform/core-impl/src/com/intellij/pom/core/impl/PomModelImpl.java +++ b/platform/core-impl/src/com/intellij/pom/core/impl/PomModelImpl.java @@ -251,7 +251,7 @@ public class PomModelImpl extends UserDataHolderBase implements PomModel { } if (containingFileByTree != null) { boolean isFromCommit = ApplicationManager.getApplication().isDispatchThread() && - ApplicationManager.getApplication().hasWriteAction(CommitToPsiFileAction.class); + ((PsiDocumentManagerBase)PsiDocumentManager.getInstance(myProject)).isCommitInProgress(); if (!isFromCommit && !synchronizer.isIgnorePsiEvents()) { reparseParallelTrees(containingFileByTree); if (docSynced) { diff --git a/platform/core-impl/src/com/intellij/psi/impl/CommitToPsiFileAction.java b/platform/core-impl/src/com/intellij/psi/impl/CommitToPsiFileAction.java deleted file mode 100644 index f3966bdbb38d..000000000000 --- a/platform/core-impl/src/com/intellij/psi/impl/CommitToPsiFileAction.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.intellij.psi.impl; - -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.DocumentRunnable; -import com.intellij.openapi.project.Project; -import com.intellij.psi.IgnorePsiEventsMarker; - - -public abstract class CommitToPsiFileAction extends DocumentRunnable implements IgnorePsiEventsMarker { - protected CommitToPsiFileAction(Document document, Project project) { - super(document,project); - } -} - - - - diff --git a/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitThread.java b/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitThread.java index 881e5a29bf49..cd50b88fb056 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitThread.java +++ b/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitThread.java @@ -19,9 +19,7 @@ import com.intellij.diagnostic.ThreadDumper; import com.intellij.lang.ASTNode; import com.intellij.lang.FileASTNode; import com.intellij.openapi.Disposable; -import com.intellij.openapi.application.ApplicationAdapter; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.*; import com.intellij.openapi.application.ex.ApplicationEx; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Attachment; @@ -75,6 +73,8 @@ import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; public class DocumentCommitThread implements Runnable, Disposable, DocumentCommitProcessor { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.DocumentCommitThread"); @@ -471,14 +471,29 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi throw new RuntimeException(s); } - CommitTask task = createNewTaskAndCancelSimilar(project, document, getAllFileNodes(psiFile), "Sync commit", ModalityState.current()); - assert !task.indicator.isCanceled(); - Pair result = commitUnderProgress(task, true); - Runnable finish = result.first; - log(project, "Committed sync", task, finish, task.indicator); - assert finish != null; + List> allFileNodes = getAllFileNodes(psiFile); - finish.run(); + Lock documentLock = getDocumentLock(document); + + CommitTask task; + synchronized (lock) { + // synchronized to ensure no new similar tasks can start before we hold the document's lock + task = createNewTaskAndCancelSimilar(project, document, allFileNodes, "Sync commit", ModalityState.current()); + documentLock.lock(); + } + + try { + assert !task.indicator.isCanceled(); + Pair result = commitUnderProgress(task, true); + Runnable finish = result.first; + log(project, "Committed sync", task, finish, task.indicator); + assert finish != null; + + finish.run(); + } + finally { + documentLock.unlock(); + } // will wake itself up on write action end } @@ -525,29 +540,40 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi myApplication.assertReadAccessAllowed(); if (project.isDisposed()) return; - if (documentManager.isCommitted(document)) return; - - if (!task.isStillValid()) { - task.cancel("Task invalidated", DocumentCommitThread.this); + Lock lock = getDocumentLock(document); + if (!lock.tryLock()) { + task.cancel("Can't obtain document lock", DocumentCommitThread.this); return; } - FileViewProvider viewProvider = documentManager.getCachedViewProvider(document); - if (viewProvider == null) { - finishProcessors.add(handleCommitWithoutPsi(documentManager, task)); - return; - } + try { + if (documentManager.isCommitted(document)) return; - for (Pair pair : task.myOldFileNodes) { - PsiFileImpl file = pair.first; - if (file.isValid()) { - FileASTNode oldFileNode = pair.second; - Processor finishProcessor = doCommit(task, file, oldFileNode); - if (finishProcessor != null) { - finishProcessors.add(finishProcessor); + if (!task.isStillValid()) { + task.cancel("Task invalidated", DocumentCommitThread.this); + return; + } + + FileViewProvider viewProvider = documentManager.getCachedViewProvider(document); + if (viewProvider == null) { + finishProcessors.add(handleCommitWithoutPsi(documentManager, task)); + return; + } + + for (Pair pair : task.myOldFileNodes) { + PsiFileImpl file = pair.first; + if (file.isValid()) { + FileASTNode oldFileNode = pair.second; + Processor finishProcessor = doCommit(task, file, oldFileNode); + if (finishProcessor != null) { + finishProcessors.add(finishProcessor); + } } } } + finally { + lock.unlock(); + } } }; if (synchronously) { @@ -742,7 +768,9 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi return new Processor() { @Override public boolean process(Document document) { - ApplicationManager.getApplication().assertWriteAccessAllowed(); + if (file.isPhysical()) { + ApplicationManager.getApplication().assertWriteAccessAllowed(); + } if (!task.isStillValid() || ((PsiDocumentManagerBase)PsiDocumentManager.getInstance(file.getProject())).getCachedViewProvider(document) != file.getViewProvider()) { return false; // optimistic locking failed @@ -887,4 +915,13 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi } } } + + /** + * @return an internal lock object to prevent read & write phases of commit from running simultaneously for free-threaded PSI + */ + private static Lock getDocumentLock(Document document) { + Lock lock = document.getUserData(DOCUMENT_LOCK); + return lock != null ? lock : ((UserDataHolderEx)document).putUserDataIfAbsent(DOCUMENT_LOCK, new ReentrantLock()); + } + private static final Key DOCUMENT_LOCK = Key.create("DOCUMENT_LOCK"); } diff --git a/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java b/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java index b896505433a3..dbfb1a2909ce 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java +++ b/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java @@ -27,6 +27,7 @@ import com.intellij.openapi.application.impl.ApplicationInfoImpl; import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.DocumentRunnable; import com.intellij.openapi.editor.event.DocumentAdapter; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.event.DocumentListener; @@ -153,6 +154,7 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen return ((PsiManagerEx)myPsiManager).getFileManager().findCachedViewProvider(virtualFile); } + @Nullable private static VirtualFile getVirtualFile(@NotNull Document document) { final VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document); if (virtualFile == null || !virtualFile.isValid()) return null; @@ -261,7 +263,7 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen return true; } if (myUncommittedDocuments.isEmpty()) { - if (!ApplicationManager.getApplication().hasWriteAction(CommitToPsiFileAction.class)) { + if (!isCommitInProgress()) { // in case of fireWriteActionFinished() we didn't execute 'actionsWhenAllDocumentsAreCommitted' yet assert actionsWhenAllDocumentsAreCommitted.isEmpty() : actionsWhenAllDocumentsAreCommitted; } @@ -300,12 +302,18 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen @NotNull final Object reason) { assert !myProject.isDisposed() : "Already disposed"; final boolean[] ok = {true}; - ApplicationManager.getApplication().runWriteAction(new CommitToPsiFileAction(document, myProject) { + Runnable runnable = new DocumentRunnable(document, myProject) { @Override public void run() { ok[0] = finishCommitInWriteAction(document, finishProcessors, synchronously); } - }); + }; + if (synchronously) { + runnable.run(); + } + else { + ApplicationManager.getApplication().runWriteAction(runnable); + } if (ok[0]) { // otherwise changes maybe not synced to the document yet, and injectors will crash @@ -408,7 +416,7 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen } }; - if (Boolean.TRUE.equals(psiFile.getViewProvider().getVirtualFile().getUserData(SingleRootFileViewProvider.FREE_THREADED))) { + if (isFreeThreaded(psiFile.getViewProvider().getVirtualFile())) { runnable.run(); } else { @@ -416,6 +424,14 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen } } + private static boolean isFreeThreaded(@NotNull VirtualFile file) { + return Boolean.TRUE.equals(file.getUserData(SingleRootFileViewProvider.FREE_THREADED)); + } + + public boolean isCommitInProgress() { + return myIsCommitInProgress; + } + @Override public T commitAndRunReadAction(@NotNull final Computable computation) { final Ref ref = Ref.create(null); diff --git a/platform/core-impl/src/com/intellij/psi/impl/PsiToDocumentSynchronizer.java b/platform/core-impl/src/com/intellij/psi/impl/PsiToDocumentSynchronizer.java index 5c34ed2c4b95..391df3ab683d 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/PsiToDocumentSynchronizer.java +++ b/platform/core-impl/src/com/intellij/psi/impl/PsiToDocumentSynchronizer.java @@ -209,7 +209,7 @@ public class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter { } public boolean toProcessPsiEvent() { - return !myIgnorePsiEvents && !ApplicationManager.getApplication().hasWriteAction(IgnorePsiEventsMarker.class); + return !myIgnorePsiEvents && !myPsiDocumentManager.isCommitInProgress() && !ApplicationManager.getApplication().hasWriteAction(IgnorePsiEventsMarker.class); } @TestOnly diff --git a/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/ex/DefaultColorSchemesManager.java b/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/ex/DefaultColorSchemesManager.java index acfd41c2e9c4..55f0c1fa07ff 100644 --- a/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/ex/DefaultColorSchemesManager.java +++ b/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/ex/DefaultColorSchemesManager.java @@ -18,6 +18,9 @@ package com.intellij.openapi.editor.colors.ex; import com.intellij.openapi.components.*; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.colors.impl.DefaultColorsScheme; +import com.intellij.openapi.editor.colors.impl.EmptyColorScheme; +import com.intellij.openapi.util.text.StringUtil; +import org.jdom.Attribute; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -26,6 +29,8 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; +import static com.intellij.openapi.editor.colors.impl.AbstractColorsScheme.NAME_ATTR; + @State( name = "DefaultColorSchemesManager", defaultStateAsResource = true, @@ -51,25 +56,24 @@ public class DefaultColorSchemesManager implements PersistentStateComponent> content = presentation.getContent(); Map problems = presentation.getProblemElements(); - appendToolNodeContent(context, toolNode, parentNode, showStructure, content, problems, null); + appendToolNodeContent(context, toolNode, parentNode, showStructure, content, problems); } public abstract void appendToolNodeContent(@NotNull GlobalInspectionContextImpl context, @@ -118,8 +118,7 @@ public abstract class InspectionRVContentProvider { @NotNull InspectionTreeNode parentNode, final boolean showStructure, @NotNull Map> contents, - @NotNull Map problems, - @Nullable final DefaultTreeModel model); + @NotNull Map problems); protected abstract void appendDescriptor(@NotNull GlobalInspectionContextImpl context, @NotNull InspectionToolWrapper toolWrapper, @@ -315,7 +314,7 @@ public abstract class InspectionRVContentProvider { } @SuppressWarnings({"ConstantConditions"}) //class cast suppression - protected static void merge(@Nullable DefaultTreeModel model, InspectionTreeNode child, InspectionTreeNode parent, boolean merge) { + protected static void merge(InspectionTreeNode child, InspectionTreeNode parent, boolean merge) { if (merge) { for (int i = 0; i < parent.getChildCount(); i++) { InspectionTreeNode current = (InspectionTreeNode)parent.getChildAt(i); @@ -324,35 +323,31 @@ public abstract class InspectionRVContentProvider { } if (current instanceof InspectionPackageNode) { if (((InspectionPackageNode)current).getPackageName().compareTo(((InspectionPackageNode)child).getPackageName()) == 0) { - processDepth(model, child, current); + processDepth(child, current); return; } } else if (current instanceof RefElementNode) { if (((RefElementNode)current).getElement().getName().compareTo(((RefElementNode)child).getElement().getName()) == 0 && ((RefElementNode)current).getElement().getQualifiedName().compareTo(((RefElementNode)child).getElement().getQualifiedName()) == 0) { - processDepth(model, child, current); + processDepth(child, current); return; } } else if (current instanceof InspectionNode) { if (((InspectionNode)current).getToolWrapper().getShortName().compareTo(((InspectionNode)child).getToolWrapper().getShortName()) == 0) { - processDepth(model, child, current); + processDepth(child, current); return; } } else if (current instanceof InspectionModuleNode) { if (((InspectionModuleNode)current).getName().compareTo(((InspectionModuleNode)child).getName()) == 0) { - processDepth(model, child, current); + processDepth(child, current); return; } } } } - add(model, child, parent); - } - - protected static void add(@Nullable final DefaultTreeModel model, final InspectionTreeNode child, final InspectionTreeNode parent) { insertByIndex(child, parent); } @@ -367,13 +362,13 @@ public abstract class InspectionRVContentProvider { parent.insert(child, -i -1); } - private static void processDepth(@Nullable DefaultTreeModel model, final InspectionTreeNode child, final InspectionTreeNode current) { + private static void processDepth(final InspectionTreeNode child, final InspectionTreeNode current) { InspectionTreeNode[] children = new InspectionTreeNode[child.getChildCount()]; for (int i = 0; i < children.length; i++) { children[i] = (InspectionTreeNode)child.getChildAt(i); } for (InspectionTreeNode node : children) { - merge(model, node, current, true); + merge(node, current, true); } } } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProviderImpl.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProviderImpl.java index ccbe14779db1..221af1f72403 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProviderImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProviderImpl.java @@ -93,8 +93,7 @@ public class InspectionRVContentProviderImpl extends InspectionRVContentProvider @NotNull final InspectionTreeNode parentNode, final boolean showStructure, @NotNull final Map> contents, - @NotNull final Map problems, - DefaultTreeModel model) { + @NotNull final Map problems) { final InspectionToolWrapper toolWrapper = toolNode.getToolWrapper(); Function> computeContainer = new Function>() { @@ -114,7 +113,7 @@ public class InspectionRVContentProviderImpl extends InspectionRVContentProvider entities.addAll(moduleProblems); } buildTree(context, contents, false, toolWrapper, computeContainer, showStructure, node -> { - merge(model, node, toolNode, true); + merge(node, toolNode, true); }); if (presentation.isOldProblemsIncluded()) { @@ -127,10 +126,10 @@ public class InspectionRVContentProviderImpl extends InspectionRVContentProvider }; buildTree(context, presentation.getOldContent(), true, toolWrapper, computeContainer, showStructure, node -> { - merge(model, node, toolNode, true); + merge(node, toolNode, true); }); } - merge(model, toolNode, parentNode, false); + merge(toolNode, parentNode, false); } @Override diff --git a/platform/lang-impl/src/com/intellij/codeInspection/offlineViewer/OfflineInspectionRVContentProvider.java b/platform/lang-impl/src/com/intellij/codeInspection/offlineViewer/OfflineInspectionRVContentProvider.java index b76f021f48c8..c074f366a8c4 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/offlineViewer/OfflineInspectionRVContentProvider.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/offlineViewer/OfflineInspectionRVContentProvider.java @@ -113,8 +113,7 @@ public class OfflineInspectionRVContentProvider extends InspectionRVContentProvi @NotNull final InspectionTreeNode parentNode, final boolean showStructure, @NotNull final Map> contents, - @NotNull final Map problems, - final DefaultTreeModel model) { + @NotNull final Map problems) { InspectionToolWrapper toolWrapper = toolNode.getToolWrapper(); final Map> filteredContent = getFilteredContent(context, toolWrapper); if (filteredContent != null && !filteredContent.values().isEmpty()) { diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/DefaultInspectionToolPresentation.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/DefaultInspectionToolPresentation.java index 089651efa15d..91a447042951 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/DefaultInspectionToolPresentation.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/DefaultInspectionToolPresentation.java @@ -52,7 +52,6 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.tree.DefaultTreeModel; import java.io.*; import java.util.*; import java.util.regex.Matcher; @@ -262,8 +261,8 @@ public class DefaultInspectionToolPresentation implements ProblemDescriptionsPro content.add(refElement); view.getProvider().appendToolNodeContent(context, toolNode, - (InspectionTreeNode)toolNode.getParent(), context.getUIOptions().SHOW_STRUCTURE, - contents, problems, (DefaultTreeModel)view.getTree().getModel()); + (InspectionTreeNode)toolNode.getParent(), context.getUIOptions().SHOW_STRUCTURE, + contents, problems); } } diff --git a/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleManagerComponent.java b/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleManagerComponent.java index 29bc57a56508..be5f57eb7291 100644 --- a/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleManagerComponent.java +++ b/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleManagerComponent.java @@ -19,8 +19,7 @@ import com.intellij.ProjectTopics; import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; -import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.*; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.openapi.diagnostic.Logger; @@ -126,26 +125,15 @@ public class ModuleManagerComponent extends ModuleManagerImpl { return; } - Runnable runnableWithProgress = new Runnable() { - @Override - public void run() { - for (final Module module : myModuleModel.myModules.values()) { - final Application app = ApplicationManager.getApplication(); - final Runnable swingRunnable = new Runnable() { - @Override - public void run() { - fireModuleAddedInWriteAction(module); - } - }; - ProgressIndicator pi = ProgressManager.getInstance().getProgressIndicator(); - app.invokeAndWait(swingRunnable, pi.getModalityState()); - } + Runnable runnableWithProgress = () -> { + for (final Module module : myModuleModel.myModules.values()) { + TransactionGuard.getInstance().submitTransactionAndWait(TransactionKind.ANY_CHANGE, () -> fireModuleAddedInWriteAction(module)); } }; ProgressIndicator progressIndicator = myProgressManager.getProgressIndicator(); if (progressIndicator == null) { - myProgressManager.runProcessWithProgressSynchronously(runnableWithProgress, "Initializing modules...", false, myProject); + myProgressManager.runProcessWithProgressSynchronously(runnableWithProgress, "Initializing Modules...", false, myProject); } else { runnableWithProgress.run(); diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/PushedFilePropertiesUpdaterImpl.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/PushedFilePropertiesUpdaterImpl.java index 9ae229776484..47f392063e59 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/PushedFilePropertiesUpdaterImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/PushedFilePropertiesUpdaterImpl.java @@ -22,6 +22,7 @@ package com.intellij.openapi.roots.impl; import com.intellij.ProjectTopics; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.TransactionGuard; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.ExtensionException; import com.intellij.openapi.extensions.Extensions; @@ -49,7 +50,6 @@ import com.intellij.ui.GuiUtils; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.indexing.FileBasedIndex; import com.intellij.util.indexing.FileBasedIndexProjectHandler; -import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -408,20 +408,17 @@ public class PushedFilePropertiesUpdaterImpl extends PushedFilePropertiesUpdater private static void reloadPsi(final VirtualFile file, final Project project) { final FileManagerImpl fileManager = (FileManagerImpl)((PsiManagerEx)PsiManager.getInstance(project)).getFileManager(); if (fileManager.findCachedViewProvider(file) != null) { - UIUtil.invokeLaterIfNeeded(new Runnable() { - @Override - public void run() { - if (project.isDisposed()) { - return; - } - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - fileManager.forceReload(file); - } - }); + Runnable runnable = () -> { + if (project.isDisposed()) { + return; } - }); + ApplicationManager.getApplication().runWriteAction(() -> fileManager.forceReload(file)); + }; + if (ApplicationManager.getApplication().isDispatchThread()) { + runnable.run(); + } else { + TransactionGuard.submitTransaction(runnable); + } } } } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java index d60418926487..51c34633c526 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java @@ -136,7 +136,9 @@ public class PsiDocumentManagerImpl extends PsiDocumentManagerBase implements Se protected boolean finishCommitInWriteAction(@NotNull Document document, @NotNull List> finishProcessors, boolean synchronously) { - EditorWindowImpl.disposeInvalidEditors(); // in write action + if (ApplicationManager.getApplication().isWriteAccessAllowed()) { // can be false for non-physical PSI + EditorWindowImpl.disposeInvalidEditors(); + } return super.finishCommitInWriteAction(document, finishProcessors, synchronously); } diff --git a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexProjectHandler.java b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexProjectHandler.java index 7ae45e2d7b05..10ba085f3e92 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexProjectHandler.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexProjectHandler.java @@ -23,6 +23,7 @@ import com.intellij.ide.IdeBundle; import com.intellij.ide.caches.FileContent; import com.intellij.ide.startup.StartupManagerEx; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.TransactionGuard; import com.intellij.openapi.components.AbstractProjectComponent; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileTypes.FileTypeManager; @@ -80,15 +81,10 @@ public class FileBasedIndexProjectHandler extends AbstractProjectComponent imple public void run() { PushedFilePropertiesUpdater.getInstance(project).initializeProperties(); - // dumb mode should start before post-startup activities - // only when queueTask is called from UI thread, we can guarantee that - // when the method returns, the application has entered dumb mode - UIUtil.invokeLaterIfNeeded(new Runnable() { - @Override - public void run() { - if (!project.isDisposed() && FileBasedIndex.getInstance() instanceof FileBasedIndexImpl) { - DumbService.getInstance(project).queueTask(new UnindexedFilesUpdater(project, true)); - } + // schedule dumb mode start after the read action we're currently in + TransactionGuard.submitTransaction(() -> { + if (!project.isDisposed() && FileBasedIndex.getInstance() instanceof FileBasedIndexImpl) { + DumbService.getInstance(project).queueTask(new UnindexedFilesUpdater(project, true)); } }); diff --git a/platform/platform-api/src/com/intellij/util/ui/Animator.java b/platform/platform-api/src/com/intellij/util/ui/Animator.java index 67156c25fe57..6259307949d0 100644 --- a/platform/platform-api/src/com/intellij/util/ui/Animator.java +++ b/platform/platform-api/src/com/intellij/util/ui/Animator.java @@ -19,20 +19,15 @@ package com.intellij.util.ui; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.util.concurrency.AppExecutorUtil; +import com.intellij.util.concurrency.EdtExecutorService; import org.jetbrains.annotations.NonNls; import javax.swing.*; import java.awt.*; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; public abstract class Animator implements Disposable { - // allow only one animation run at a time - private final ScheduledExecutorService scheduler = AppExecutorUtil.createBoundedScheduledExecutorService(1); - private final String myName; private final int myTotalFrames; private final int myCycleDuration; @@ -147,20 +142,10 @@ public abstract class Animator implements Disposable { animationDone(); } else if (myTicker == null) { - myTicker = scheduler.scheduleWithFixedDelay(new Runnable() { - private final AtomicBoolean scheduled = new AtomicBoolean(false); - + myTicker = EdtExecutorService.getScheduledExecutorInstance().scheduleWithFixedDelay(new Runnable() { @Override public void run() { - if (scheduled.compareAndSet(false, true) && !isDisposed()) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - scheduled.set(false); - onTick(); - } - }); - } + onTick(); } @Override @@ -183,8 +168,8 @@ public abstract class Animator implements Disposable { @Override public void dispose() { - myDisposed = true; stopTicker(); + myDisposed = true; } public boolean isRunning() { diff --git a/platform/platform-impl/src/com/intellij/internal/ToggleDumbModeAction.java b/platform/platform-impl/src/com/intellij/internal/ToggleDumbModeAction.java index c03f12f5e6b3..c18de5266b3a 100644 --- a/platform/platform-impl/src/com/intellij/internal/ToggleDumbModeAction.java +++ b/platform/platform-impl/src/com/intellij/internal/ToggleDumbModeAction.java @@ -15,12 +15,12 @@ */ package com.intellij.internal; -import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.application.WrapInTransaction; import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.DumbModeTask; import com.intellij.openapi.project.DumbServiceImpl; import com.intellij.openapi.project.Project; @@ -30,7 +30,8 @@ import org.jetbrains.annotations.NotNull; /** * @author peter */ -public class ToggleDumbModeAction extends AnAction implements DumbAware { +@WrapInTransaction +public class ToggleDumbModeAction extends DumbAwareAction { private volatile boolean myDumb = false; public void actionPerformed(final AnActionEvent e) { diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java index 1f9f0fcd1ded..235164eef87a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java @@ -308,12 +308,7 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App } } } - runWriteAction(new Runnable() { - @Override - public void run() { - Disposer.dispose(ApplicationImpl.this); - } - }); + TransactionGuard.syncTransaction(TransactionKind.ANY_CHANGE, () -> runWriteAction(() -> Disposer.dispose(this))); Disposer.assertIsEmpty(); return true; @@ -376,6 +371,11 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App @Override public Future executeOnPooledThread(@NotNull final Runnable action) { return ourThreadExecutorsService.submit(new Runnable() { + @Override + public String toString() { + return action.toString(); + } + @Override public void run() { assert !isReadAccessAllowed(): describe(Thread.currentThread()); @@ -864,7 +864,7 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App } private boolean doExit(boolean allowListenersToCancel, boolean restart) { - saveSettings(); + TransactionGuard.syncTransaction(TransactionKind.ANY_CHANGE, this::saveSettings); if (allowListenersToCancel && !canExit()) { return false; @@ -1230,8 +1230,8 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App assertIsDispatchThread(getStatus(), "Write access is allowed from event dispatch thread only"); HeavyProcessLatch.INSTANCE.stopThreadPrioritizing(); // let non-cancellable read actions complete faster, if present if (!TransactionGuard.getInstance().isInsideTransaction() && Registry.is("ide.require.transaction.for.model.changes", false)) { + // please assign exceptions that occur here to Peter LOG.error("Write access is allowed from model transactions only, see TransactionGuard documentation for details"); - //todo throw new IllegalStateException("Write access is allowed from model transactions only, see TransactionGuard documentation for details"); } boolean writeActionPending = myWriteActionPending; myWriteActionPending = true; diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java index 229be3ac32a7..16f53f44e9f2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java @@ -51,9 +51,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.net.URL; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; +import java.util.*; @State( name = "EditorColorsManagerImpl", @@ -233,8 +231,7 @@ public class EditorColorsManagerImpl extends EditorColorsManager implements Pers @NotNull @Override public EditorColorsScheme[] getAllSchemes() { - List schemes = mySchemeManager.getAllSchemes(); - EditorColorsScheme[] result = schemes.toArray(new EditorColorsScheme[schemes.size()]); + EditorColorsScheme[] result = getAllVisibleSchemes(mySchemeManager.getAllSchemes()); Arrays.sort(result, new Comparator() { @Override public int compare(@NotNull EditorColorsScheme s1, @NotNull EditorColorsScheme s2) { @@ -248,6 +245,16 @@ public class EditorColorsManagerImpl extends EditorColorsManager implements Pers return result; } + private static EditorColorsScheme[] getAllVisibleSchemes(@NotNull Collection schemes) { + List visibleSchemes = new ArrayList<>(schemes.size() - 1); + for (EditorColorsScheme scheme : schemes) { + if (!(scheme instanceof EmptyColorScheme)) { + visibleSchemes.add(scheme); + } + } + return visibleSchemes.toArray(new EditorColorsScheme[visibleSchemes.size()]); + } + @Override public void setGlobalScheme(@Nullable EditorColorsScheme scheme) { mySchemeManager.setCurrent(scheme == null ? getDefaultScheme() : scheme); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java index 24f9196ea939..aa8ed9369eda 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java @@ -31,6 +31,7 @@ import com.intellij.ide.IdeEventQueue; import com.intellij.ide.dnd.*; import com.intellij.ide.ui.customization.CustomActionsSchema; import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.actionSystem.ex.ActionUtil; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.impl.ApplicationImpl; import com.intellij.openapi.diagnostic.Logger; @@ -1570,13 +1571,8 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse : renderer.getClickAction(); } if (clickAction != null) { - if (checkDumbAware(clickAction)) { - performAction(clickAction, e, "ICON_NAVIGATION", myEditor.getDataContext()); - repaint(); - } - else { - notifyNotDumbAware(); - } + performAction(clickAction, e, "ICON_NAVIGATION", myEditor.getDataContext()); + repaint(); e.consume(); } else { @@ -1608,7 +1604,9 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse private static void performAction(@NotNull AnAction action, @NotNull InputEvent e, @NotNull String place, @NotNull DataContext context) { AnActionEvent actionEvent = AnActionEvent.createFromAnAction(action, e, place, context); action.update(actionEvent); - if (actionEvent.getPresentation().isEnabledAndVisible()) action.actionPerformed(actionEvent); + if (actionEvent.getPresentation().isEnabledAndVisible()) { + ActionUtil.performActionDumbAware(action, actionEvent); + } } @Nullable @@ -1778,11 +1776,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse else { AnAction rightButtonAction = renderer.getRightButtonClickAction(); if (rightButtonAction != null) { - if (checkDumbAware(rightButtonAction)) { - performAction(rightButtonAction, e, "ICON_NAVIGATION_SECONDARY_BUTTON", myEditor.getDataContext()); - } else { - notifyNotDumbAware(); - } + performAction(rightButtonAction, e, "ICON_NAVIGATION_SECONDARY_BUTTON", myEditor.getDataContext()); e.consume(); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index d56a23d4e629..a93c5b26c189 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -20,7 +20,6 @@ import com.intellij.codeInsight.hint.DocumentFragmentTooltipRenderer; import com.intellij.codeInsight.hint.EditorFragmentComponent; import com.intellij.codeInsight.hint.TooltipController; import com.intellij.codeInsight.hint.TooltipGroup; -import com.intellij.concurrency.JobScheduler; import com.intellij.diagnostic.Dumpable; import com.intellij.diagnostic.LogMessageEx; import com.intellij.ide.*; @@ -31,8 +30,7 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ActionManagerEx; import com.intellij.openapi.actionSystem.impl.MouseGestureManager; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.*; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.UndoConfirmationPolicy; import com.intellij.openapi.diagnostic.Logger; @@ -75,6 +73,7 @@ import com.intellij.ui.components.JBLayeredPane; import com.intellij.ui.components.JBScrollBar; import com.intellij.ui.components.JBScrollPane; import com.intellij.util.*; +import com.intellij.util.concurrency.EdtExecutorService; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtilRt; import com.intellij.util.messages.MessageBusConnection; @@ -990,8 +989,10 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi if (event.isConsumed()) { return; } - if (processKeyTyped(event)) { - event.consume(); + try (AccessToken ignored = TransactionGuard.getInstance().startSynchronousTransaction(TransactionKind.TEXT_EDITING)) { + if (processKeyTyped(event)) { + event.consume(); + } } } @@ -4685,7 +4686,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi if (mySchedulerHandle != null) { mySchedulerHandle.cancel(false); } - mySchedulerHandle = JobScheduler.getScheduler().scheduleWithFixedDelay(this, mySleepTime, mySleepTime, TimeUnit.MILLISECONDS); + mySchedulerHandle = EdtExecutorService.getScheduledExecutorInstance().scheduleWithFixedDelay(this, mySleepTime, mySleepTime, TimeUnit.MILLISECONDS); } private void setBlinkPeriod(int blinkPeriod) { @@ -4716,7 +4717,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } if (toRepaint) { - SwingUtilities.invokeLater(myRepaintRunnable); + activeCursor.repaint(); } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/ActionsTreeUtil.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/ActionsTreeUtil.java index f6f39c1fcd08..3deb5359fdb1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/ActionsTreeUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/ActionsTreeUtil.java @@ -556,7 +556,7 @@ public class ActionsTreeUtil { final Shortcut[] actionShortcuts = keymap.getShortcuts(action instanceof ActionStub ? ((ActionStub)action).getId() : actionManager.getId(action)); for (Shortcut actionShortcut : actionShortcuts) { - if (shortcut.equals(actionShortcut)) { + if (actionShortcut != null && actionShortcut.startsWith(shortcut)) { return true; } } diff --git a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java index a1d1cc15b81e..a91b046ae8e6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java @@ -18,10 +18,7 @@ package com.intellij.openapi.project; import com.intellij.ide.IdeBundle; import com.intellij.ide.startup.StartupManagerEx; import com.intellij.openapi.Disposable; -import com.intellij.openapi.application.AccessToken; -import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.*; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; @@ -188,7 +185,7 @@ public class DumbServiceImpl extends DumbService implements Disposable, Modifica return; } - UIUtil.invokeLaterIfNeeded(new Runnable() { + Runnable runnable = new Runnable() { @Override public void run() { if (myProject.isDisposed()) { @@ -251,7 +248,13 @@ public class DumbServiceImpl extends DumbService implements Disposable, Modifica }, ModalityState.any(), myProject.getDisposed()); } } - }); + }; + if (application.isDispatchThread()) { + runnable.run(); + } else { + //noinspection SSBasedInspection + SwingUtilities.invokeLater(() -> TransactionGuard.submitTransaction(runnable)); + } } @Nullable @@ -530,16 +533,25 @@ public class DumbServiceImpl extends DumbService implements Disposable, Modifica private static void invokeAndWaitIfNeeded(Runnable runnable) { if (ApplicationManager.getApplication().isDispatchThread()) { runnable.run(); + return; } - else { - try { - SwingUtilities.invokeAndWait(runnable); - } - catch (InterruptedException ignore) { - } - catch (Exception e) { - LOG.error(e); - } + + Semaphore semaphore = new Semaphore(); + semaphore.down(); + //todo remove invokeLater when transactions are executed in "any" modality state + //noinspection SSBasedInspection + SwingUtilities.invokeLater( + () -> TransactionGuard.getInstance().submitMergeableTransaction(TransactionKind.ANY_CHANGE, () -> { + try { + runnable.run(); + } finally { + semaphore.up(); + } + })); + try { + semaphore.waitFor(); + } + catch (ProcessCanceledException ignore) { } } diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java index 03fd13d40ca8..0ad16a520d7e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java @@ -28,10 +28,7 @@ import com.intellij.notification.NotificationListener; import com.intellij.notification.NotificationType; import com.intellij.notification.NotificationsManager; import com.intellij.openapi.Disposable; -import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ApplicationNamesInfo; -import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.*; import com.intellij.openapi.components.impl.stores.StorageUtil; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileEditor.FileDocumentManager; @@ -351,9 +348,8 @@ public class ProjectManagerImpl extends ProjectManagerEx implements Disposable { } fireProjectOpened(project); - DumbService.allowStartingDumbModeInside(DumbModePermission.MAY_START_BACKGROUND, new Runnable() { - @Override - public void run() { + try (AccessToken ignored = TransactionGuard.getInstance().startSynchronousTransaction(TransactionKind.ANY_CHANGE)) { + DumbService.allowStartingDumbModeInside(DumbModePermission.MAY_START_BACKGROUND, () -> DumbService.getInstance(project).queueTask(new DumbModeTask() { @Override public void performInDumbMode(@NotNull ProgressIndicator indicator) { @@ -364,9 +360,9 @@ public class ProjectManagerImpl extends ProjectManagerEx implements Disposable { public String toString() { return "wait for file watcher"; } - }); - } - }); + }) + ); + } final StartupManagerImpl startupManager = (StartupManagerImpl)StartupManager.getInstance(project); boolean ok = myProgressManager.runProcessWithProgressSynchronously(new Runnable() { @@ -374,15 +370,10 @@ public class ProjectManagerImpl extends ProjectManagerEx implements Disposable { public void run() { startupManager.runStartupActivities(); - // dumb mode should start before post-startup activities - // only when startCacheUpdate is called from UI thread, we can guarantee that - // when the method returns, the application has entered dumb mode - UIUtil.invokeAndWaitIfNeeded(new Runnable() { - @Override - public void run() { - startupManager.startCacheUpdate(); - } - }); + // Startup activities (e.g. the one in FileBasedIndexProjectHandler) have scheduled dumb mode to begin "later" + // Now we schedule-and-wait to the same event queue to guarantee that the dumb mode really begins now: + // Post-startup activities should not ever see unindexed and at the same time non-dumb state + TransactionGuard.getInstance().submitTransactionAndWait(TransactionKind.ANY_CHANGE, startupManager::startCacheUpdate); startupManager.runPostStartupActivitiesFromExtensions(); @@ -615,7 +606,7 @@ public class ProjectManagerImpl extends ProjectManagerEx implements Disposable { if (checkCanClose && !canClose(project)) return false; final ShutDownTracker shutDownTracker = ShutDownTracker.getInstance(); shutDownTracker.registerStopperThread(Thread.currentThread()); - try { + try (AccessToken ignored = TransactionGuard.getInstance().startSynchronousTransaction(TransactionKind.ANY_CHANGE)) { if (save) { FileDocumentManager.getInstance().saveAllDocuments(); project.save(); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java index 3e50443a24cd..1217a56b7ca1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java @@ -37,6 +37,7 @@ import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy; import com.intellij.openapi.wm.ex.LayoutFocusTraversalPolicyExt; import com.intellij.reference.SoftReference; import com.intellij.ui.FocusTrackback; +import com.intellij.util.concurrency.EdtExecutorService; import com.intellij.util.containers.WeakValueHashMap; import com.intellij.util.ui.UIUtil; import gnu.trove.TIntIntHashMap; @@ -56,6 +57,7 @@ import java.lang.ref.WeakReference; import java.text.SimpleDateFormat; import java.util.*; import java.util.List; +import java.util.concurrent.TimeUnit; public class FocusManagerImpl extends IdeFocusManager implements Disposable { private static final Logger LOG = Logger.getInstance(FocusManagerImpl.class); @@ -85,8 +87,6 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { private final EdtAlarm myFocusedComponentAlarm; private final EdtAlarm myForcedFocusRequestsAlarm; - private final SimpleTimer myTimer = SimpleTimer.newInstance("FocusManager timer"); - private final EdtAlarm myIdleAlarm; private final Set myIdleRequests = new LinkedHashSet(); @@ -1040,7 +1040,7 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { } - class EdtAlarm { + static class EdtAlarm { private final Set myRequests = new HashSet(); public void cancelAllRequests() { @@ -1052,7 +1052,7 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { public void addRequest(@NotNull EdtRunnable runnable, int delay) { myRequests.add(runnable); - myTimer.setUp(runnable, delay); + EdtExecutorService.getScheduledExecutorInstance().schedule(runnable, delay, TimeUnit.MILLISECONDS); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ClockPanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ClockPanel.java index 2378df42bc5d..1d109f616757 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ClockPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ClockPanel.java @@ -15,8 +15,7 @@ */ package com.intellij.openapi.wm.impl.status; -import com.intellij.concurrency.JobScheduler; -import com.intellij.util.ui.UIUtil; +import com.intellij.util.concurrency.EdtExecutorService; import javax.swing.*; import java.awt.*; @@ -71,12 +70,7 @@ public class ClockPanel extends JComponent { myScheduledFuture.cancel(false); } myCalendar.setTimeInMillis(System.currentTimeMillis()); - myScheduledFuture = JobScheduler.getScheduler().schedule(new Runnable() { - @Override - public void run() { - UIUtil.invokeLaterIfNeeded(myRepaintRunnable); - } - }, 60 - myCalendar.get(SECOND), TimeUnit.SECONDS); + myScheduledFuture = EdtExecutorService.getScheduledExecutorInstance().schedule(myRepaintRunnable, 60 - myCalendar.get(SECOND), TimeUnit.SECONDS); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/MemoryUsagePanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/MemoryUsagePanel.java index 50fc3c30e69c..724e2f43496e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/MemoryUsagePanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/MemoryUsagePanel.java @@ -15,7 +15,6 @@ */ package com.intellij.openapi.wm.impl.status; -import com.intellij.concurrency.JobScheduler; import com.intellij.ide.ui.UISettings; import com.intellij.openapi.wm.CustomStatusBarWidget; import com.intellij.openapi.wm.StatusBar; @@ -23,6 +22,7 @@ import com.intellij.openapi.wm.StatusBarWidget; import com.intellij.ui.Gray; import com.intellij.ui.JBColor; import com.intellij.ui.UIBundle; +import com.intellij.util.concurrency.EdtExecutorService; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.update.Activatable; @@ -75,13 +75,8 @@ public class MemoryUsagePanel extends JButton implements CustomStatusBarWidget { @Override public void showNotify() { - myFuture = JobScheduler.getScheduler().scheduleWithFixedDelay(new Runnable() { - public void run() { - if (isDisplayable()) { - updateState(); - } - } - }, 1, 5, TimeUnit.SECONDS); + myFuture = EdtExecutorService.getScheduledExecutorInstance().scheduleWithFixedDelay(MemoryUsagePanel.this::updateState, + 1, 5, TimeUnit.SECONDS); } @Override @@ -231,12 +226,9 @@ public class MemoryUsagePanel extends JButton implements CustomStatusBarWidget { if (total != myLastTotal || used != myLastUsed) { myLastTotal = total; myLastUsed = used; - //noinspection SSBasedInspection - SwingUtilities.invokeLater(new Runnable() { - public void run() { - myBufferedImage = null; - repaint(); - } + UIUtil.invokeLaterIfNeeded(() -> { + myBufferedImage = null; + repaint(); }); setToolTipText(UIBundle.message("memory.usage.panel.statistics.message", total, used)); diff --git a/platform/platform-tests/testSrc/com/intellij/psi/impl/PsiDocumentManagerImplTest.java b/platform/platform-tests/testSrc/com/intellij/psi/impl/PsiDocumentManagerImplTest.java index e9f5629de441..89cb0d6f4b77 100644 --- a/platform/platform-tests/testSrc/com/intellij/psi/impl/PsiDocumentManagerImplTest.java +++ b/platform/platform-tests/testSrc/com/intellij/psi/impl/PsiDocumentManagerImplTest.java @@ -660,4 +660,43 @@ public class PsiDocumentManagerImplTest extends PlatformTestCase { System.out.println("i = " + i); } } + + public void testCommitNonPhysicalPsiWithoutWriteAction() throws IOException { + assertFalse(ApplicationManager.getApplication().isWriteAccessAllowed()); + + PsiFile original = getPsiManager().findFile(getVirtualFile(createTempFile("X.txt", ""))); + assertNotNull(original); + assertTrue(original.getViewProvider().isEventSystemEnabled()); + + long modCount = getPsiManager().getModificationTracker().getModificationCount(); + + PsiFile copy = (PsiFile)original.copy(); + assertFalse(copy.getViewProvider().isEventSystemEnabled()); + + Document document = copy.getViewProvider().getDocument(); + assertNotNull(document); + document.setText("class A{}"); + + PsiDocumentManager.getInstance(myProject).commitDocument(document); + assertEquals(modCount, getPsiManager().getModificationTracker().getModificationCount()); + assertEquals(document.getText(), copy.getText()); + assertTrue(PsiDocumentManager.getInstance(myProject).isCommitted(document)); + } + + public void testCommitNonPhysicalCopyOnPerformWhenAllCommitted() throws Exception { + assertFalse(ApplicationManager.getApplication().isWriteAccessAllowed()); + + PsiFile original = getPsiManager().findFile(getVirtualFile(createTempFile("X.txt", ""))); + assertNotNull(original); + PsiFile copy = (PsiFile)original.copy(); + assertEquals("", copy.getText()); + Document document = copy.getViewProvider().getDocument(); + assertNotNull(document); + + document.setText("class A{}"); + PsiDocumentManager.getInstance(myProject).performWhenAllCommitted(() -> assertEquals(document.getText(), copy.getText())); + DocumentCommitThread.getInstance().waitForAllCommits(); + assertTrue(PsiDocumentManager.getInstance(myProject).isCommitted(document)); + } + } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/conflicts/ChangelistConflictTracker.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/conflicts/ChangelistConflictTracker.java index 6dcdac914ac1..37608fe89e8d 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/conflicts/ChangelistConflictTracker.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/conflicts/ChangelistConflictTracker.java @@ -26,7 +26,6 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectUtil; import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.ZipperUpdater; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.FileStatusManager; @@ -34,6 +33,7 @@ import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.EditorNotifications; +import com.intellij.util.Alarm; import com.intellij.util.NullableFunction; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.hash.HashSet; @@ -79,26 +79,18 @@ public class ChangelistConflictTracker { myCheckSet = new HashSet(); final Application application = ApplicationManager.getApplication(); - final ZipperUpdater zipperUpdater = new ZipperUpdater(300, myProject); - final Runnable runnable = new Runnable() { - @Override - public void run() { - if (application.runReadAction(new Computable() { - @Override - public Boolean compute() { - return application.isDisposed() || myProject.isDisposed() || !myProject.isOpen(); - } - })) { - return; - } - final Set localSet; - synchronized (myCheckSetLock) { - localSet = new HashSet(); - localSet.addAll(myCheckSet); - myCheckSet.clear(); - } - checkFiles(localSet); + final ZipperUpdater zipperUpdater = new ZipperUpdater(300, Alarm.ThreadToUse.SWING_THREAD, myProject); + final Runnable runnable = () -> { + if (application.isDisposed() || myProject.isDisposed() || !myProject.isOpen()) { + return; } + final Set localSet; + synchronized (myCheckSetLock) { + localSet = new HashSet(); + localSet.addAll(myCheckSet); + myCheckSet.clear(); + } + checkFiles(localSet); }; myDocumentListener = new DocumentAdapter() { @Override diff --git a/xml/tests/testData/completion/9.xml b/xml/tests/testData/completion/9.xml deleted file mode 100644 index d7bca4b35741..000000000000 --- a/xml/tests/testData/completion/9.xml +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file