0 or less in custom implementation to skip '}' insertion in the preprocessEnter call
+ * and switch to default implementation.
+ *
+ * @param file target PSI file
+ * @param editor target editor
+ * @param caretOffset target caret offset
+ * @return maximum number of '}' that can be inserted by handler, 0 or less to switch to default implementation
+ */
+ protected int getMaxRBraceCount(@NotNull final PsiFile file, @NotNull final Editor editor, int caretOffset) {
+ if (!CodeInsightSettings.getInstance().INSERT_BRACE_ON_ENTER) {
+ return 0;
+ }
+ return Math.max(0, getUnmatchedLBracesNumberBefore(editor, caretOffset, file.getFileType()));
+ }
+
+ /**
+ * Calculates the precise number of '}' that have to be inserted by handler.
+ *
+ * @param editor target editor
+ * @param caretOffset target caret offset
+ * @param maxRBraceCount the maximum number of '}' for insert at position, it always positive
+ * @return number of '}' that has to be inserted by handler, it has to positive
+ */
+ protected int adjustRBraceCountForPosition(@NotNull final Editor editor, int caretOffset, int maxRBraceCount) {
+ assert maxRBraceCount > 0;
+
+ CharSequence text = editor.getDocument().getCharsSequence();
+ int bracesToInsert = 0;
+ for (int i = caretOffset - 1; i >= 0 && bracesToInsert < maxRBraceCount; --i) {
+ final char c = text.charAt(i);
+ if (c == '{') {
+ ++bracesToInsert;
+ }
+ else if (isStopChar(c)) {
+ break;
}
}
- offset = Math.min(offset, document.getTextLength());
+ return Math.max(bracesToInsert, 1);
+ }
+ /**
+ * Checks the character before the inserted '}' to reduce the count of inserted '}'.
+ * The number of inserted '}' will increase for each found '{'.
+ *
+ * @param c character to check
+ * @return true, to stop back iteration
+ */
+ protected boolean isStopChar(char c) {
+ return " \n\t".indexOf(c) < 0;
+ }
+
+ /**
+ * Calculates the position for insertion of one or more '}'.
+ *
+ * @param file target PSI file
+ * @param editor target editor
+ * @param caretOffset target caret offset
+ * @return the position between caretOffset and the end of file
+ */
+ protected int getRBraceOffset(@NotNull final PsiFile file, @NotNull final Editor editor, int caretOffset) {
+ CharSequence text = editor.getDocument().getCharsSequence();
+ int offset = CharArrayUtil.shiftForward(text, caretOffset, " \t");
+ final int fileLength = text.length();
+ if (offset < fileLength && ")];,%".indexOf(text.charAt(offset)) < 0) {
+ offset = calculateOffsetToInsertClosingBrace(file, text, offset).second;
+ //offset = CharArrayUtil.shiftForwardUntil(text, caretOffset, "\n");
+ }
+ return Math.min(offset, fileLength);
+ }
+
+ /**
+ * Inserts the rBracesCount of '}' at the rBracesInsertOffset position and formats the code block.
+ *
+ * @param file target PSI file
+ * @param editor target editor
+ * @param caretOffset target caret offset
+ * @param rBracesInsertOffset target position to insert
+ * @param rBracesCount count of '}' to insert
+ */
+ protected void insertRBraces(@NotNull PsiFile file,
+ @NotNull Editor editor,
+ int caretOffset,
+ int rBracesInsertOffset,
+ int rBracesCount) {
+ final Document document = editor.getDocument();
+ insertRBracesAtPosition(document, caretOffset, rBracesInsertOffset, rBracesCount);
+ formatCodeFragmentBetweenBraces(file, document, caretOffset, rBracesInsertOffset, rBracesCount);
+ }
+
+ /**
+ * Inserts the rBracesCount of '}' at the rBracesInsertOffset position.
+ *
+ * @param document target document
+ * @param caretOffset target caret offset
+ * @param rBracesInsertOffset target position to insert
+ * @param rBracesCount count of '}' to insert
+ */
+ protected void insertRBracesAtPosition(Document document, int caretOffset, int rBracesInsertOffset, int rBracesCount) {
+ document.insertString(rBracesInsertOffset, "\n" + StringUtil.repeatSymbol('}', rBracesCount));
// We need to adjust indents of the text that will be moved, hence, need to insert preliminary line feed.
// Example:
// if (test1()) {
@@ -85,43 +200,47 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter
// That is formatted incorrectly because line feed between 'else' and 'if' is not inserted yet (whole 'if' block is indent anchor
// to 'if' code block('{}')). So, we insert temporary line feed between 'if' and 'else', correct indent and remove that temporary
// line feed.
- int bracesToInsert = 0;
- outer:
- for (int i = caretOffset - 1; unmatchedLBracesNumber > 0 && i >= 0 && bracesToInsert < unmatchedLBracesNumber; i--) {
- char c = text.charAt(i);
- switch (c) {
- case ' ':
- case '\n':
- case '\t':
- continue;
- case '{': bracesToInsert++; break;
- default: break outer;
- }
- }
- bracesToInsert = Math.max(bracesToInsert, 1);
- document.insertString(offset, "\n" + StringUtil.repeatSymbol('}', bracesToInsert));
document.insertString(caretOffset, "\n");
- PsiDocumentManager.getInstance(project).commitDocument(document);
+ }
+
+ /**
+ * Formats the code block between caret and inserted braces.
+ *
+ * @param file target PSI file
+ * @param document target document
+ * @param caretOffset target caret offset
+ * @param rBracesInsertOffset target position to insert
+ * @param rBracesCount count of '}' to insert
+ */
+ protected void formatCodeFragmentBetweenBraces(@NotNull PsiFile file,
+ @NotNull Document document,
+ int caretOffset,
+ int rBracesInsertOffset,
+ int rBracesCount) {
+ Project project = file.getProject();
long stamp = document.getModificationStamp();
boolean closingBraceIndentAdjusted;
try {
- CodeStyleManager.getInstance(project).adjustLineIndent(file, new TextRange(caretOffset, offset + 2));
+ PsiDocumentManager.getInstance(project).commitDocument(document);
+ CodeStyleManager.getInstance(project).adjustLineIndent(file, new TextRange(caretOffset, rBracesInsertOffset + 2));
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
finally {
closingBraceIndentAdjusted = stamp != document.getModificationStamp();
+ // do you remember that we insert the '\n'? here we take it back!
document.deleteString(caretOffset, caretOffset + 1);
}
// There is a possible case that formatter was unable to adjust line indent for the closing brace (that is the case for plain text
// document for example). Hence, we're trying to do the manually.
if (!closingBraceIndentAdjusted) {
- int line = document.getLineNumber(offset);
+ int line = document.getLineNumber(rBracesInsertOffset);
StringBuilder buffer = new StringBuilder();
int start = document.getLineStartOffset(line);
int end = document.getLineEndOffset(line);
+ final CharSequence text = document.getCharsSequence();
for (int i = start; i < end; i++) {
char c = text.charAt(i);
if (c != ' ' && c != '\t') {
@@ -132,18 +251,16 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter
}
}
if (buffer.length() > 0) {
- document.insertString(offset + 1, buffer);
+ document.insertString(rBracesInsertOffset + 1, buffer);
}
}
-
- return Result.DefaultForceIndent;
}
/**
* Current handler inserts closing curly brace (right brace) if necessary. There is a possible case that it should be located
* more than one line forward.
*
- * Example
+ * Example
*
* if (test1()) {
* } else { if (test2()) {
@@ -165,17 +282,17 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter
*
* In essence it inspects PSI structure and finds PSE elements with the max length that starts at caret offset. End offset
* of that element is used as an insertion point.
- *
- * @param file target PSI file
- * @param text text from the given file
- * @param offset target offset where line feed will be inserted
- * @return offset to use for inserting closing brace
+ *
+ * @param file target PSI file
+ * @param text text from the given file
+ * @param offset target offset where line feed will be inserted
+ * @return pair of (element, offset). The element is the '}' owner, if applicable; the offset is the position for inserting closing brace
*/
- protected int calculateOffsetToInsertClosingBrace(PsiFile file, CharSequence text, final int offset) {
+ protected Pair calculateOffsetToInsertClosingBrace(@NotNull PsiFile file, @NotNull CharSequence text, final int offset) {
PsiElement element = PsiUtilCore.getElementAtOffset(file, offset);
ASTNode node = element.getNode();
if (node != null && node.getElementType() == TokenType.WHITE_SPACE) {
- return CharArrayUtil.shiftForwardUntil(text, offset, "\n");
+ return Pair.create(null, CharArrayUtil.shiftForwardUntil(text, offset, "\n"));
}
for (PsiElement parent = element.getParent(); parent != null; parent = parent.getParent()) {
ASTNode parentNode = parent.getNode();
@@ -185,26 +302,24 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter
element = parent;
}
if (element.getTextOffset() != offset) {
- return CharArrayUtil.shiftForwardUntil(text, offset, "\n");
- }
- else {
- return element.getTextRange().getEndOffset();
+ return Pair.create(null, CharArrayUtil.shiftForwardUntil(text, offset, "\n"));
}
+ return Pair.create(element, element.getTextRange().getEndOffset());
}
-
+
public static boolean isAfterUnmatchedLBrace(Editor editor, int offset, FileType fileType) {
return getUnmatchedLBracesNumberBefore(editor, offset, fileType) > 0;
}
/**
* Calculates number of unmatched left braces before the given offset.
- *
- * @param editor target editor
- * @param offset target offset
- * @param fileType target file type
- * @return number of unmatched braces before the given offset;
- * negative value if it's not possible to perform the calculation or if there are no unmatched left braces before
- * the given offset
+ *
+ * @param editor target editor
+ * @param offset target offset
+ * @param fileType target file type
+ * @return number of unmatched braces before the given offset;
+ * negative value if it's not possible to perform the calculation or if there are no unmatched left braces before
+ * the given offset
*/
protected static int getUnmatchedLBracesNumberBefore(Editor editor, int offset, FileType fileType) {
if (offset == 0) {
@@ -237,7 +352,7 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter
}
boolean beforeOffset = iterator.getStart() < offset;
-
+
if (braceMatcher.isLBraceToken(iterator, chars, fileType)) {
if (beforeOffset) {
lBracesBeforeOffset++;
@@ -255,7 +370,7 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter
}
}
}
-
+
return lBracesBeforeOffset - rBracesBeforeOffset - (rBracesAfterOffset - lBracesAfterOffset);
}
}
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/QuickEditHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/QuickEditHandler.java
index 556b821b3569..54b85bbaab06 100644
--- a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/QuickEditHandler.java
+++ b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/QuickEditHandler.java
@@ -96,7 +96,6 @@ public class QuickEditHandler extends DocumentAdapter implements Disposable {
private EditorWindow mySplittedWindow;
private boolean myCommittingToOriginal;
- @Nullable
private final PsiFile myInjectedFile;
private final List> myMarkers = ContainerUtil.newLinkedList();
@@ -199,7 +198,9 @@ public class QuickEditHandler extends DocumentAdapter implements Disposable {
}
public boolean isValid() {
- boolean valid = myNewVirtualFile.isValid() && (myAltFullRange == null && myInjectedFile.isValid() || myAltFullRange.isValid());
+ boolean valid = myNewVirtualFile.isValid() &&
+ (myAltFullRange == null && myInjectedFile.isValid() ||
+ myAltFullRange != null && myAltFullRange.isValid());
if (valid) {
for (Trinity t : myMarkers) {
if (!t.first.isValid() || !t.second.isValid() || t.third.getElement() == null) {
diff --git a/platform/lang-impl/src/com/intellij/execution/ExecutorRegistryImpl.java b/platform/lang-impl/src/com/intellij/execution/ExecutorRegistryImpl.java
index 2e1bf628e5bf..e479c5c94a25 100644
--- a/platform/lang-impl/src/com/intellij/execution/ExecutorRegistryImpl.java
+++ b/platform/lang-impl/src/com/intellij/execution/ExecutorRegistryImpl.java
@@ -47,7 +47,7 @@ public class ExecutorRegistryImpl extends ExecutorRegistry {
private static final Logger LOG = Logger.getInstance(ExecutorRegistryImpl.class);
@NonNls public static final String RUNNERS_GROUP = "RunnerActions";
- @NonNls public static final String RUN_CONTEXT_GROUP = "RunContextGroup";
+ @NonNls public static final String RUN_CONTEXT_GROUP = "RunContextGroupInner";
private List myExecutors = new ArrayList();
private ActionManager myActionManager;
diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java
index 63a11eda9f76..ad841c1d1c98 100644
--- a/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java
+++ b/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java
@@ -574,7 +574,10 @@ public class ExecutionManagerImpl extends ExecutionManager implements Disposable
myProject.getMessageBus().syncPublisher(EXECUTION_TOPIC).processTerminated(myProfile, myProcessHandler);
- SaveAndSyncHandler.getInstance().scheduleRefresh();
+ SaveAndSyncHandler saveAndSyncHandler = SaveAndSyncHandler.getInstance();
+ if (saveAndSyncHandler != null) {
+ saveAndSyncHandler.scheduleRefresh();
+ }
}
@Override
diff --git a/platform/lang-impl/src/com/intellij/formatting/CoreFormatterUtil.java b/platform/lang-impl/src/com/intellij/formatting/CoreFormatterUtil.java
index b9b8bbf3eb83..1af833a35194 100644
--- a/platform/lang-impl/src/com/intellij/formatting/CoreFormatterUtil.java
+++ b/platform/lang-impl/src/com/intellij/formatting/CoreFormatterUtil.java
@@ -248,7 +248,7 @@ public class CoreFormatterUtil {
}
if (indent.getType() == Indent.Type.LABEL) return new IndentData(options.LABEL_INDENT_SIZE);
if (indent.getType() == Indent.Type.NONE) return new IndentData(0);
- if (indent.getType() == Indent.Type.SPACES) return new IndentData(0, indent.getSpaces());
+ if (indent.getType() == Indent.Type.SPACES) return new IndentData(indent.getSpaces(), 0);
return new IndentData(options.INDENT_SIZE);
}
diff --git a/platform/lang-impl/src/com/intellij/ide/actions/CreateDirectoryOrPackageHandler.java b/platform/lang-impl/src/com/intellij/ide/actions/CreateDirectoryOrPackageHandler.java
index ec89d0930e1b..ff616e497f5c 100644
--- a/platform/lang-impl/src/com/intellij/ide/actions/CreateDirectoryOrPackageHandler.java
+++ b/platform/lang-impl/src/com/intellij/ide/actions/CreateDirectoryOrPackageHandler.java
@@ -207,15 +207,13 @@ public class CreateDirectoryOrPackageHandler implements InputValidatorEx {
final Runnable run = new Runnable() {
@Override
public void run() {
- LocalHistoryAction action = LocalHistoryAction.NULL;
+ String dirPath = myDirectory.getVirtualFile().getPresentableUrl();
+ String actionName = IdeBundle.message("progress.creating.directory", dirPath, File.separator, subDirName);
+ LocalHistoryAction action = LocalHistory.getInstance().startAction(actionName);
try {
- String actionName;
- String dirPath = myDirectory.getVirtualFile().getPresentableUrl();
- actionName = IdeBundle.message("progress.creating.directory", dirPath, File.separator, subDirName);
- action = LocalHistory.getInstance().startAction(actionName);
-
if (createFile) {
- myCreatedElement = myDirectory.createFile(subDirName);
+ CreateFileAction.MkDirs mkdirs = new CreateFileAction.MkDirs(subDirName, myDirectory);
+ myCreatedElement = mkdirs.directory.createFile(mkdirs.newName);
} else {
createDirectories(subDirName);
}
diff --git a/platform/lang-impl/src/com/intellij/ide/extensionResources/ExtensionsRootType.java b/platform/lang-impl/src/com/intellij/ide/extensionResources/ExtensionsRootType.java
index ffa1d04199f4..7f8fa57f153e 100644
--- a/platform/lang-impl/src/com/intellij/ide/extensionResources/ExtensionsRootType.java
+++ b/platform/lang-impl/src/com/intellij/ide/extensionResources/ExtensionsRootType.java
@@ -56,11 +56,11 @@ import java.util.Set;
* Bundled resources are updated automatically upon plugin version change. For bundled plugins, application version is used.
*/
public class ExtensionsRootType extends RootType {
- public static final String EXTENSIONS_PATH = "extensions";
- public static final String BACKUP_FILE_EXTENSION = "old";
-
static final Logger LOG = Logger.getInstance(ExtensionsRootType.class);
+
private static final String HASH_ALGORITHM = "MD5";
+ private static final String EXTENSIONS_PATH = "extensions";
+ private static final String BACKUP_FILE_EXTENSION = "old";
ExtensionsRootType() {
super(EXTENSIONS_PATH, "Extensions");
@@ -129,6 +129,11 @@ public class ExtensionsRootType extends RootType {
return super.substituteName(project, file);
}
+ public boolean isBackupFile(@NotNull VirtualFile file) {
+ String extension = file.getExtension();
+ return !file.isDirectory() && extension != null && extension.startsWith(BACKUP_FILE_EXTENSION);
+ }
+
@Nullable
String getPath(@Nullable VirtualFile resource) {
VirtualFile pluginResourcesDir = getPluginResourcesDirectoryFor(resource);
diff --git a/platform/lang-impl/src/com/intellij/ide/script/IdeStartupScripts.java b/platform/lang-impl/src/com/intellij/ide/script/IdeStartupScripts.java
index 086a9a23f48c..b8cecbe387c0 100644
--- a/platform/lang-impl/src/com/intellij/ide/script/IdeStartupScripts.java
+++ b/platform/lang-impl/src/com/intellij/ide/script/IdeStartupScripts.java
@@ -17,6 +17,7 @@ package com.intellij.ide.script;
import com.intellij.ide.extensionResources.ExtensionsRootType;
import com.intellij.ide.plugins.PluginManagerCore;
+import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.PluginId;
@@ -51,12 +52,15 @@ import java.util.concurrent.atomic.AtomicBoolean;
class IdeStartupScripts extends ApplicationComponent.Adapter {
@SuppressWarnings("FieldCanBeLocal")
private static String SCRIPT_DIR_NAME = "startup";
- private static String README_FILE_NAME = "readme.txt";
private static Logger LOG = Logger.getInstance(IdeStartupScripts.class);
@Override
public void initComponent() {
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ return;
+ }
+
List scripts = getScripts();
if (scripts.isEmpty()) {
LOG.debug("No startup scripts detected");
@@ -170,9 +174,10 @@ class IdeStartupScripts extends ApplicationComponent.Adapter {
}
scripts = ContainerUtil.filter(scripts, new Condition() {
+ private final ExtensionsRootType myExtensionsRootType = ExtensionsRootType.getInstance();
@Override
public boolean value(VirtualFile file) {
- return !file.isDirectory() && !README_FILE_NAME.equals(file.getName());
+ return !file.isDirectory() && !myExtensionsRootType.isBackupFile(file);
}
});
diff --git a/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleImpl.java b/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleImpl.java
index d2a3076dcb3e..c3530848b03f 100644
--- a/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleImpl.java
+++ b/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleImpl.java
@@ -22,7 +22,7 @@ import com.intellij.openapi.application.impl.ApplicationInfoImpl;
import com.intellij.openapi.components.*;
import com.intellij.openapi.components.impl.ModuleServiceManagerImpl;
import com.intellij.openapi.components.impl.PlatformComponentManagerImpl;
-import com.intellij.openapi.components.impl.stores.FileBasedStorage;
+import com.intellij.openapi.components.impl.stores.FileStorage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.AreaInstance;
import com.intellij.openapi.extensions.ExtensionPointName;
@@ -83,8 +83,8 @@ public class ModuleImpl extends PlatformComponentManagerImpl implements ModuleEx
}
@NotNull
- private static FileBasedStorage getMainStorage(@NotNull Module module) {
- return (FileBasedStorage)ComponentsPackage.getStateStore(module).getStateStorageManager().getStateStorage(StoragePathMacros.MODULE_FILE, RoamingType.PER_USER);
+ private static FileStorage getMainStorage(@NotNull Module module) {
+ return (FileStorage)ComponentsPackage.getStateStore(module).getStateStorageManager().getStateStorage(StoragePathMacros.MODULE_FILE, RoamingType.PER_USER);
}
@Override
@@ -150,7 +150,7 @@ public class ModuleImpl extends PlatformComponentManagerImpl implements ModuleEx
@Override
@NotNull
public String getModuleFilePath() {
- return getMainStorage(this).getFilePath();
+ return ComponentsPackage.getStateStore(this).getStateStorageManager().expandMacros(StoragePathMacros.MODULE_FILE);
}
@Override
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 4f27cb072c8d..804bf10d41ea 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
@@ -29,10 +29,7 @@ import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.util.ProgressWrapper;
-import com.intellij.openapi.project.DumbModeTask;
-import com.intellij.openapi.project.DumbService;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.project.*;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.util.Computable;
@@ -114,7 +111,7 @@ public class PushedFilePropertiesUpdaterImpl extends PushedFilePropertiesUpdater
// avoid dumb mode for just one file
doPushRecursively(file, pushers, ProjectRootManager.getInstance(myProject).getFileIndex());
}
- else {
+ else if (!ProjectCoreUtil.isProjectOrWorkspaceFile(file)) {
ContainerUtil.addIfNotNull(delayedTasks, createRecursivePushTask(file, pushers));
}
} else if (event instanceof VFileMoveEvent) {
diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/storage/ClasspathStorage.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/storage/ClasspathStorage.java
index ca5e1cfa4754..d12449f476fe 100644
--- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/storage/ClasspathStorage.java
+++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/storage/ClasspathStorage.java
@@ -17,8 +17,11 @@ package com.intellij.openapi.roots.impl.storage;
import com.intellij.application.options.PathMacrosCollector;
import com.intellij.ide.highlighter.ModuleFileType;
+import com.intellij.openapi.application.AccessToken;
+import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.components.ComponentManager;
import com.intellij.openapi.components.StateStorage;
+import com.intellij.openapi.components.TrackingPathMacroSubstitutor;
import com.intellij.openapi.components.impl.stores.StateStorageBase;
import com.intellij.openapi.components.impl.stores.StateStorageManager;
import com.intellij.openapi.components.impl.stores.StorageDataBase;
@@ -57,8 +60,10 @@ public class ClasspathStorage extends StateStorageBase componentNames) {
// if some file changed, so, changed
componentNames.add("NewModuleRootManager");
- if (myStorageData != null) {
- myStorageData.loaded = false;
+ MyStorageData storageData = storageDataRef.get();
+ if (storageData != null) {
+ storageData.loaded = false;
}
}
diff --git a/platform/lang-impl/src/com/intellij/psi/codeStyle/autodetect/DetectedIndentOptionsNotificationProvider.java b/platform/lang-impl/src/com/intellij/psi/codeStyle/autodetect/DetectedIndentOptionsNotificationProvider.java
index f8a8915f3159..a1e7a3788ceb 100644
--- a/platform/lang-impl/src/com/intellij/psi/codeStyle/autodetect/DetectedIndentOptionsNotificationProvider.java
+++ b/platform/lang-impl/src/com/intellij/psi/codeStyle/autodetect/DetectedIndentOptionsNotificationProvider.java
@@ -116,12 +116,14 @@ public class DetectedIndentOptionsNotificationProvider extends EditorNotificatio
if (!ApplicationManager.getApplication().isHeadlessEnvironment()
|| ApplicationManager.getApplication().isUnitTestMode() && myShowNotificationInTest)
{
- FileEditor fileEditor = FileEditorManager.getInstance(file.getProject()).getSelectedEditor(vFile);
+ Project project = file.getProject();
+ FileEditorManager fileEditorManager = FileEditorManager.getInstance(project);
+ FileEditor fileEditor = fileEditorManager.getSelectedEditor(vFile);
if (fileEditor != null) {
Boolean notifiedFlag = fileEditor.getUserData(NOTIFIED_FLAG);
if (notifiedFlag == null || enforce) {
fileEditor.putUserData(NOTIFIED_FLAG, Boolean.TRUE);
- EditorNotifications.getInstance(file.getProject()).updateNotifications(vFile);
+ EditorNotifications.getInstance(project).updateNotifications(vFile);
}
}
}
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 f69bd2974ae4..198d50920a5c 100644
--- a/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java
+++ b/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java
@@ -79,7 +79,7 @@ public class PsiDocumentManagerImpl extends PsiDocumentManagerBase implements Se
busConnection.subscribe(DocumentBulkUpdateListener.TOPIC, new DocumentBulkUpdateListener.Adapter() {
@Override
public void updateFinished(@NotNull Document doc) {
- documentCommitThread.queueCommit(project, doc, "Bulk update finished");
+ documentCommitThread.queueCommit(project, doc, "Bulk update finished", ApplicationManager.getApplication().getCurrentModalityState());
}
});
}
diff --git a/platform/lang-impl/src/com/intellij/psi/impl/file/impl/PsiVFSListener.java b/platform/lang-impl/src/com/intellij/psi/impl/file/impl/PsiVFSListener.java
index 91fc23c52f69..5a0fc06a21cb 100644
--- a/platform/lang-impl/src/com/intellij/psi/impl/file/impl/PsiVFSListener.java
+++ b/platform/lang-impl/src/com/intellij/psi/impl/file/impl/PsiVFSListener.java
@@ -42,7 +42,6 @@ import com.intellij.psi.*;
import com.intellij.psi.impl.PsiManagerImpl;
import com.intellij.psi.impl.PsiTreeChangeEventImpl;
import com.intellij.psi.impl.smartPointers.SmartPointerManagerImpl;
-import com.intellij.psi.impl.source.PsiFileImpl;
import com.intellij.util.FileContentUtilCore;
import com.intellij.util.messages.MessageBusConnection;
import org.jetbrains.annotations.NotNull;
@@ -365,7 +364,7 @@ public class PsiVFSListener extends VirtualFileAdapter {
}
}
- ((SmartPointerManagerImpl)SmartPointerManager.getInstance(myManager.getProject())).fastenBelts(vFile, 0, null);
+ ((SmartPointerManagerImpl)SmartPointerManager.getInstance(myManager.getProject())).fastenBelts(vFile);
ApplicationManager.getApplication().runWriteAction(
new ExternalChangeAction() {
@Override
diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedFileViewProvider.java b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedFileViewProvider.java
index ff7fa6c865ed..1dec8e119186 100644
--- a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedFileViewProvider.java
+++ b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedFileViewProvider.java
@@ -22,7 +22,6 @@ import com.intellij.injected.editor.VirtualFileWindow;
import com.intellij.lang.Language;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
@@ -32,8 +31,6 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.FreeThreadedFileViewProvider;
-import com.intellij.psi.impl.source.tree.MarkersHolderFileViewProvider;
-import com.intellij.util.SmartList;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -42,8 +39,7 @@ import java.util.List;
/**
* @author cdr
*/
-public class InjectedFileViewProvider extends SingleRootFileViewProvider implements FreeThreadedFileViewProvider,
- MarkersHolderFileViewProvider {
+public class InjectedFileViewProvider extends SingleRootFileViewProvider implements FreeThreadedFileViewProvider {
private Project myProject;
private final Object myLock = new Object();
private final DocumentWindowImpl myDocumentWindow;
@@ -205,16 +201,4 @@ public class InjectedFileViewProvider extends SingleRootFileViewProvider impleme
myPatchingLeaves = patchingLeaves;
}
- @Override
- @NotNull
- public RangeMarker[] getCachedMarkers() {
- List markers = new SmartList();
- for (PsiLanguageInjectionHost.Shred shred : myDocumentWindow.getShreds()) {
- RangeMarker marker = (RangeMarker)shred.getHostRangeMarker();
- if (marker != null) {
- markers.add(marker);
- }
- }
- return markers.toArray(new RangeMarker[markers.size()]);
- }
}
diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ComboBoxAction.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ComboBoxAction.java
index 7ebe3c5951e2..344e67dddb4c 100644
--- a/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ComboBoxAction.java
+++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ComboBoxAction.java
@@ -65,7 +65,7 @@ public abstract class ComboBoxAction extends AnAction implements CustomComponent
JRootPane rootPane = UIUtil.getParentOfType(JRootPane.class, contextComponent);
if (rootPane != null) {
button = (ComboBoxButton)
- JBSwingUtilities.uiTraverser().bfsTraversal(rootPane).filter(new Condition() {
+ JBSwingUtilities.uiTraverser().withRoot(rootPane).bfsTraversal().filter(new Condition() {
@Override
public boolean value(Component component) {
return component instanceof ComboBoxButton && ((ComboBoxButton)component).getMyAction() == ComboBoxAction.this;
diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ToggleToolbarAction.java b/platform/platform-impl/src/com/intellij/ide/actions/ToggleToolbarAction.java
index 33168df48d50..1baf2a13af62 100644
--- a/platform/platform-impl/src/com/intellij/ide/actions/ToggleToolbarAction.java
+++ b/platform/platform-impl/src/com/intellij/ide/actions/ToggleToolbarAction.java
@@ -107,7 +107,7 @@ public class ToggleToolbarAction extends ToggleAction implements DumbAware {
@NotNull
private static Iterable iterateToolbars(JComponent root) {
- return JBSwingUtilities.uiTraverser().preOrderDfsTraversal(root).filter(ActionToolbar.class);
+ return JBSwingUtilities.uiTraverser().withRoot(root).preOrderDfsTraversal().filter(ActionToolbar.class);
}
private static class OptionsGroup extends ActionGroup implements DumbAware {
diff --git a/platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.java b/platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.java
index bbb103d6c9e4..17cf08ba0fab 100644
--- a/platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.java
+++ b/platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.java
@@ -34,8 +34,6 @@ import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
-import java.awt.event.ItemEvent;
-import java.awt.event.ItemListener;
import java.util.Dictionary;
import java.util.Hashtable;
@@ -199,6 +197,13 @@ public class AppearanceConfigurable extends BaseConfigurable implements Searchab
update |= settings.NAVIGATE_TO_PREVIEW != (myComponent.myNavigateToPreviewCheckBox.isVisible() && myComponent.myNavigateToPreviewCheckBox.isSelected());
settings.NAVIGATE_TO_PREVIEW = myComponent.myNavigateToPreviewCheckBox.isSelected();
+ ColorBlindness blindness = myComponent.myColorBlindnessPanel.getColorBlindness();
+ if (settings.COLOR_BLINDNESS != blindness) {
+ settings.COLOR_BLINDNESS = blindness;
+ update = true;
+ //TODO:SAM: reload
+ }
+
update |= settings.DISABLE_MNEMONICS_IN_CONTROLS != myComponent.myDisableMnemonicInControlsCheckBox.isSelected();
settings.DISABLE_MNEMONICS_IN_CONTROLS = myComponent.myDisableMnemonicInControlsCheckBox.isSelected();
@@ -308,6 +313,7 @@ public class AppearanceConfigurable extends BaseConfigurable implements Searchab
myComponent.myRightLayoutCheckBox.setSelected(settings.RIGHT_HORIZONTAL_SPLIT);
myComponent.myNavigateToPreviewCheckBox.setSelected(settings.NAVIGATE_TO_PREVIEW);
myComponent.myNavigateToPreviewCheckBox.setVisible(false);//disabled for a while
+ myComponent.myColorBlindnessPanel.setColorBlindness(settings.COLOR_BLINDNESS);
myComponent.myDisableMnemonicInControlsCheckBox.setSelected(settings.DISABLE_MNEMONICS_IN_CONTROLS);
boolean alphaModeEnabled = WindowManagerEx.getInstanceEx().isAlphaModeSupported();
@@ -355,6 +361,7 @@ public class AppearanceConfigurable extends BaseConfigurable implements Searchab
isModified |= myComponent.myLeftLayoutCheckBox.isSelected() != settings.LEFT_HORIZONTAL_SPLIT;
isModified |= myComponent.myRightLayoutCheckBox.isSelected() != settings.RIGHT_HORIZONTAL_SPLIT;
isModified |= myComponent.myNavigateToPreviewCheckBox.isSelected() != settings.NAVIGATE_TO_PREVIEW;
+ isModified |= myComponent.myColorBlindnessPanel.getColorBlindness() != settings.COLOR_BLINDNESS;
isModified |= myComponent.myHideIconsInQuickNavigation.isSelected() != settings.SHOW_ICONS_IN_QUICK_NAVIGATION;
@@ -429,9 +436,7 @@ public class AppearanceConfigurable extends BaseConfigurable implements Searchab
private JSlider myInitialTooltipDelaySlider;
private ComboBox myPresentationModeFontSize;
private JCheckBox myNavigateToPreviewCheckBox;
- private JCheckBox myAllowStatusBar;
- private JCheckBox myAllowLineNumbers;
- private JCheckBox myAllowAnnotations;
+ private ColorBlindnessPanel myColorBlindnessPanel;
public MyComponent() {
myOverrideLAFFonts.addActionListener( new ActionListener() {
diff --git a/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form b/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form
index 548ce66676e7..2f74192e7759 100644
--- a/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form
+++ b/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form
@@ -3,7 +3,7 @@
-
+
@@ -16,7 +16,7 @@
-
+
@@ -226,6 +226,12 @@
+
+
+
+
+
+
diff --git a/platform/platform-impl/src/com/intellij/ide/ui/ColorBlindnessPanel.java b/platform/platform-impl/src/com/intellij/ide/ui/ColorBlindnessPanel.java
new file mode 100644
index 000000000000..36acab939e15
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/ide/ui/ColorBlindnessPanel.java
@@ -0,0 +1,104 @@
+/*
+ * 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.ide.ui;
+
+import com.intellij.ide.IdeBundle;
+import com.intellij.openapi.ui.ComboBox;
+import com.intellij.ui.components.panels.HorizontalLayout;
+import com.intellij.util.ui.JBUI;
+
+import javax.swing.*;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+/**
+ * @author Sergey.Malenkov
+ */
+final class ColorBlindnessPanel extends JPanel implements ChangeListener {
+ private final JCheckBox myCheckBox = new JCheckBox();
+ private final JComboBox myComboBox = new ComboBox();
+
+ public ColorBlindnessPanel() {
+ super(new HorizontalLayout(JBUI.scale(10)));
+ add(HorizontalLayout.LEFT, myCheckBox);
+ add(HorizontalLayout.LEFT, myComboBox);
+ myCheckBox.setSelected(false);
+ myCheckBox.addChangeListener(this);
+ myCheckBox.setText(IdeBundle.message("checkbox.color.blindness"));
+ int count = 0;
+ for (ColorBlindness blindness : ColorBlindness.values()) {
+ String name = IdeBundle.message("combobox.color.blindness." + blindness.name());
+ if (!name.isEmpty()) {
+ myComboBox.addItem(new Item(blindness, name));
+ count++;
+ }
+ }
+ myComboBox.setEnabled(false);
+ myComboBox.setVisible(count > 1);
+ setVisible(count > 0);
+ }
+
+ @Override
+ public void stateChanged(ChangeEvent event) {
+ myComboBox.setEnabled(myCheckBox.isSelected());
+ }
+
+ public ColorBlindness getColorBlindness() {
+ if (myCheckBox.isSelected()) {
+ Object object = myComboBox.getSelectedItem();
+ if (object instanceof Item) {
+ Item item = (Item)object;
+ return item.myBlindness;
+ }
+ }
+ return null;
+ }
+
+ public void setColorBlindness(ColorBlindness blindness) {
+ Item item = null;
+ if (blindness != null) {
+ int count = myComboBox.getItemCount();
+ for (int i = 0; i < count && item == null; i++) {
+ Object object = myComboBox.getItemAt(i);
+ if (object instanceof Item) {
+ item = (Item)object;
+ if (item.myBlindness != blindness) {
+ item = null;
+ }
+ }
+ }
+ }
+ myCheckBox.setSelected(item != null);
+ if (item != null) {
+ myComboBox.setSelectedItem(item);
+ }
+ }
+
+ private static final class Item {
+ private final ColorBlindness myBlindness;
+ private final String myName;
+
+ private Item(ColorBlindness blindness, String name) {
+ myBlindness = blindness;
+ myName = name;
+ }
+
+ @Override
+ public String toString() {
+ return myName;
+ }
+ }
+}
diff --git a/platform/platform-impl/src/com/intellij/ide/util/TipUIUtil.java b/platform/platform-impl/src/com/intellij/ide/util/TipUIUtil.java
index 7ebf2a57a998..9ff495b63a61 100644
--- a/platform/platform-impl/src/com/intellij/ide/util/TipUIUtil.java
+++ b/platform/platform-impl/src/com/intellij/ide/util/TipUIUtil.java
@@ -20,10 +20,12 @@ import com.intellij.ide.BrowserUtil;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManagerCore;
+import com.intellij.ide.ui.LafManager;
import com.intellij.openapi.actionSystem.KeyboardShortcut;
import com.intellij.openapi.actionSystem.Shortcut;
import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.application.ApplicationNamesInfo;
+import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.PluginDescriptor;
import com.intellij.openapi.keymap.Keymap;
import com.intellij.openapi.keymap.KeymapManager;
@@ -33,7 +35,6 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.ResourceUtil;
import com.intellij.util.ui.UIUtil;
-import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -53,7 +54,8 @@ import java.net.URL;
* @author Konstantin Bulenkov
*/
public class TipUIUtil {
- @NonNls private static final String SHORTCUT_ENTITY = "&shortcut:";
+ private static final Logger LOG = Logger.getInstance(TipUIUtil.class);
+ private static final String SHORTCUT_ENTITY = "&shortcut:";
private TipUIUtil() {
}
@@ -102,6 +104,14 @@ public class TipUIUtil {
if (UIUtil.isUnderDarcula()) {
replaced = replaced.replace("css/tips.css", "css/tips_darcula.css");
}
+ if (browser.getUI() == null) {
+ browser.updateUI();
+ boolean succeed = browser.getUI() != null;
+ String message = "reinit JEditorPane.ui: " + (succeed ? "OK" : "FAIL") +
+ ", laf=" + LafManager.getInstance().getCurrentLookAndFeel();
+ if (succeed) LOG.warn(message);
+ else LOG.error(message);
+ }
browser.read(new StringReader(replaced), url);
}
catch (IOException e) {
diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ToolbarUpdater.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ToolbarUpdater.java
index 877d502e2815..77aeb2332163 100644
--- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ToolbarUpdater.java
+++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ToolbarUpdater.java
@@ -122,7 +122,7 @@ public abstract class ToolbarUpdater implements Activatable {
protected abstract void updateActionsImpl(boolean transparentOnly, boolean forced);
protected void updateActionTooltips() {
- for (ActionButton actionButton : JBSwingUtilities.uiTraverser().preOrderDfsTraversal(myComponent).filter(ActionButton.class)) {
+ for (ActionButton actionButton : JBSwingUtilities.uiTraverser().withRoot(myComponent).preOrderDfsTraversal().filter(ActionButton.class)) {
actionButton.updateToolTipText();
}
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java
index 10db5de0a74c..1faa0919f336 100644
--- a/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java
@@ -18,7 +18,6 @@ package com.intellij.openapi.components.impl;
import com.intellij.ide.plugins.PluginManager;
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.ReadAction;
import com.intellij.openapi.components.BaseComponent;
@@ -289,7 +288,6 @@ public class ServiceManagerImpl implements BaseComponent {
}
public static boolean isUseReadActionToInitService() {
- Application app = ApplicationManager.getApplication();
- return Registry.is("use.read.action.to.init.service", !(app.isUnitTestMode() || app.isInternal()));
+ return Registry.is("use.read.action.to.init.service", true);
}
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java
index e78af518779c..dd18182739d1 100644
--- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java
+++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java
@@ -25,9 +25,9 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.LineSeparator;
+import com.intellij.util.PairConsumer;
import com.intellij.util.SystemProperties;
import com.intellij.util.containers.SmartHashSet;
-import gnu.trove.TObjectObjectProcedure;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -43,8 +43,10 @@ public class DirectoryBasedStorage extends StateStorageBase componentNames) {
// todo reload only changed file, compute diff
- DirectoryStorageData oldData = myStorageData;
+ DirectoryStorageData oldData = storageDataRef.get();
DirectoryStorageData newData = loadData();
- myStorageData = newData;
+ storageDataRef.set(newData);
if (oldData == null) {
componentNames.addAll(newData.getComponentNames());
}
@@ -155,7 +157,7 @@ public class DirectoryBasedStorage extends StateStorageBase() {
+ copiedStorageData.processComponent(componentName, new PairConsumer() {
@Override
- public boolean execute(String fileName, Object state) {
+ public void consume(String fileName, Object state) {
if (!dirtyFileNames.contains(fileName)) {
- return true;
+ return;
}
Element element = null;
@@ -192,7 +194,7 @@ public class DirectoryBasedStorage extends StateStorageBase changedComponentNames, boolean deleted) {
- if (myRoamingType == RoamingType.DISABLED) {
- // storage roaming was changed to DISABLED, but settings repository has old state
- return;
- }
-
- try {
- Element newElement = deleted ? null : loadDataFromStreamProvider();
- if (newElement == null) {
- StorageUtil.deleteFile(myFile, this, myCachedVirtualFile);
- // if data was loaded, mark as changed all loaded components
- if (myStorageData != null) {
- changedComponentNames.addAll(myStorageData.getComponentNames());
- myStorageData = null;
- }
- }
- else if (myStorageData != null) {
- StorageData newStorageData = createStorageData();
- loadState(newStorageData, newElement);
- changedComponentNames.addAll(myStorageData.getChangedComponentNames(newStorageData, myPathMacroSubstitutor));
- myStorageData = newStorageData;
- }
- }
- catch (Throwable e) {
- LOG.error(e);
- }
- }
-
- @Override
- public String toString() {
- return getFilePath();
- }
-}
diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/tree/MarkersHolderFileViewProvider.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/FileStorage.java
similarity index 54%
rename from platform/core-impl/src/com/intellij/psi/impl/source/tree/MarkersHolderFileViewProvider.java
rename to platform/platform-impl/src/com/intellij/openapi/components/impl/stores/FileStorage.java
index 03f7e62fca3a..ce74a828eb32 100644
--- a/platform/core-impl/src/com/intellij/psi/impl/source/tree/MarkersHolderFileViewProvider.java
+++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/FileStorage.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2013 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.
@@ -13,13 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.intellij.psi.impl.source.tree;
+package com.intellij.openapi.components.impl.stores;
-import com.intellij.openapi.editor.RangeMarker;
+import com.intellij.openapi.components.StateStorage;
+import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
-public interface MarkersHolderFileViewProvider {
+import java.io.File;
+
+public interface FileStorage extends StateStorage {
+ @Nullable
+ VirtualFile getVirtualFile();
@NotNull
- RangeMarker[] getCachedMarkers();
-}
+ File getFile();
+
+ // todo remove
+ StorageDataBase getStorageData();
+
+ // todo remove
+ void setFile(@Nullable VirtualFile file, @Nullable File ioFileIfChanged);
+}
\ No newline at end of file
diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/SaveSessionBase.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/SaveSessionBase.java
index 63e5b3e85b8f..344d589102aa 100644
--- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/SaveSessionBase.java
+++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/SaveSessionBase.java
@@ -26,7 +26,7 @@ import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-abstract class SaveSessionBase implements StateStorage.SaveSession, StateStorage.ExternalizationSession, SafeWriteRequestor {
+public abstract class SaveSessionBase implements StateStorage.SaveSession, StateStorage.ExternalizationSession, SafeWriteRequestor {
private SkipDefaultsSerializationFilter serializationFilter;
@SuppressWarnings("deprecation")
diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageBase.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageBase.java
index 19ebb15813c4..4e212a3b3e8f 100644
--- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageBase.java
+++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageBase.java
@@ -16,23 +16,19 @@
package com.intellij.openapi.components.impl.stores;
import com.intellij.openapi.components.StateStorage;
-import com.intellij.openapi.components.TrackingPathMacroSubstitutor;
import com.intellij.openapi.diagnostic.Logger;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.util.concurrent.atomic.AtomicReference;
+
public abstract class StateStorageBase implements StateStorage {
protected static final Logger LOG = Logger.getInstance(StateStorageBase.class);
private boolean mySavingDisabled = false;
- protected final TrackingPathMacroSubstitutor myPathMacroSubstitutor;
- protected T myStorageData;
-
- protected StateStorageBase(@Nullable TrackingPathMacroSubstitutor trackingPathMacroSubstitutor) {
- myPathMacroSubstitutor = trackingPathMacroSubstitutor;
- }
+ protected final AtomicReference storageDataRef = new AtomicReference();
@Override
@Nullable
@@ -60,12 +56,18 @@ public abstract class StateStorageBase implements Sta
@NotNull
protected final T getStorageData(boolean reload) {
- if (myStorageData != null && !reload) {
- return myStorageData;
+ final T storageData = storageDataRef.get();
+ if (storageData != null && !reload) {
+ return storageData;
}
- myStorageData = loadData();
- return myStorageData;
+ T newStorageData = loadData();
+ if (storageDataRef.compareAndSet(storageData, newStorageData)) {
+ return newStorageData;
+ }
+ else {
+ return getStorageData(false);
+ }
}
protected abstract T loadData();
diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java
index b9f50f389b0e..dc631ec0a3f2 100644
--- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java
+++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java
@@ -38,7 +38,7 @@ public interface StateStorageManager {
StateStorage getStateStorage(@NotNull String fileSpec, @NotNull RoamingType roamingType);
@NotNull
- Couple> getCachedFileStateStorages(@NotNull Collection changed, @NotNull Collection deleted);
+ Couple> getCachedFileStateStorages(@NotNull Collection changed, @NotNull Collection deleted);
/**
* Rename file
diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java
index 77a753d80ded..428783737fb8 100644
--- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java
+++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java
@@ -349,15 +349,6 @@ public class StorageUtil {
}
}
- /**
- * You must call {@link StreamProvider#isApplicable(String, com.intellij.openapi.components.RoamingType)} before
- */
- public static void sendContent(@NotNull StreamProvider provider, @NotNull String fileSpec, @NotNull Element element, @NotNull RoamingType type) throws IOException {
- // we should use standard line-separator (\n) - stream provider can share file content on any OS
- BufferExposingByteArrayOutputStream content = writeToBytes(element, "\n");
- provider.saveContent(fileSpec, content.getInternalBuffer(), content.size(), type);
- }
-
public static boolean isProjectOrModuleFile(@NotNull String fileSpec) {
return StoragePathMacros.PROJECT_FILE.equals(fileSpec) || fileSpec.startsWith(StoragePathMacros.PROJECT_CONFIG_DIR) || fileSpec.equals(StoragePathMacros.MODULE_FILE);
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.kt b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.kt
index 9482b774c358..6767af9599cb 100644
--- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.kt
+++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.kt
@@ -23,16 +23,11 @@ public interface StreamProvider {
public open val enabled: Boolean
get() = true
- /**
- * fileSpec Only main fileSpec, not version
- */
public open fun isApplicable(fileSpec: String, roamingType: RoamingType): Boolean = true
/**
* @param fileSpec
- * *
* @param content bytes of content, size of array is not actual size of data, you must use `size`
- * *
* @param size actual size of data
*/
public fun saveContent(fileSpec: String, content: ByteArray, size: Int, roamingType: RoamingType)
diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java
deleted file mode 100644
index 52fa97fea64a..000000000000
--- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * 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.openapi.components.impl.stores;
-
-import com.intellij.openapi.components.RoamingType;
-import com.intellij.openapi.components.TrackingPathMacroSubstitutor;
-import com.intellij.openapi.util.JDOMUtil;
-import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream;
-import com.intellij.util.containers.ContainerUtil;
-import gnu.trove.THashMap;
-import org.jdom.Element;
-import org.jdom.JDOMException;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.io.IOException;
-import java.util.Map;
-import java.util.Set;
-
-public abstract class XmlElementStorage extends StateStorageBase {
- @NotNull protected final String myRootElementName;
- protected final StreamProvider myStreamProvider;
- protected final String myFileSpec;
- protected boolean myBlockSavingTheContent = false;
-
- protected final RoamingType myRoamingType;
-
- protected XmlElementStorage(@NotNull String fileSpec,
- @Nullable RoamingType roamingType,
- @Nullable TrackingPathMacroSubstitutor pathMacroSubstitutor,
- @NotNull String rootElementName,
- @Nullable StreamProvider streamProvider) {
- super(pathMacroSubstitutor);
-
- myFileSpec = fileSpec;
- myRoamingType = roamingType == null ? RoamingType.PER_USER : roamingType;
- myRootElementName = rootElementName;
- myStreamProvider = myRoamingType == RoamingType.DISABLED ? null : streamProvider;
- }
-
- @Nullable
- protected abstract Element loadLocalData();
-
- @Nullable
- @Override
- protected Element getStateAndArchive(@NotNull StorageData storageData, Object component, @NotNull String componentName) {
- return storageData.getStateAndArchive(componentName);
- }
-
- @Override
- @NotNull
- protected StorageData loadData() {
- StorageData result = createStorageData();
- Element element;
- // we don't use local data if has stream provider
- if (myStreamProvider != null && myStreamProvider.getEnabled()) {
- try {
- element = loadDataFromStreamProvider();
- if (element != null) {
- loadState(result, element);
- }
- }
- catch (Exception e) {
- LOG.error(e);
- element = null;
- }
- }
- else {
- element = loadLocalData();
- }
-
- if (element != null) {
- loadState(result, element);
- }
- return result;
- }
-
- @Nullable
- protected final Element loadDataFromStreamProvider() throws IOException, JDOMException {
- assert myStreamProvider != null;
- return JDOMUtil.load(myStreamProvider.loadContent(myFileSpec, myRoamingType));
- }
-
- protected final void loadState(@NotNull StorageData result, @NotNull Element element) {
- result.load(element, myPathMacroSubstitutor, true);
- }
-
- @NotNull
- protected StorageData createStorageData() {
- return new StorageData(myRootElementName);
- }
-
- public void setDefaultState(@NotNull Element element) {
- myStorageData = createStorageData();
- loadState(myStorageData, element);
- }
-
- @Override
- @Nullable
- public final XmlElementStorageSaveSession startExternalization() {
- return checkIsSavingDisabled() ? null : createSaveSession(getStorageData());
- }
-
- @NotNull
- protected abstract XmlElementStorageSaveSession createSaveSession(@NotNull StorageData storageData);
-
- @Nullable
- protected final Element getElement(@NotNull StorageData data, @NotNull Map newLiveStates) throws IOException {
- Element element = data.save(newLiveStates);
- if (element == null || JDOMUtil.isEmpty(element)) {
- return null;
- }
-
- if (myPathMacroSubstitutor != null) {
- try {
- myPathMacroSubstitutor.collapsePaths(element);
- }
- finally {
- myPathMacroSubstitutor.reset();
- }
- }
-
- return element;
- }
-
- @Override
- public void analyzeExternalChangesAndUpdateIfNeed(@NotNull Set componentNames) {
- StorageData oldData = myStorageData;
- StorageData newData = getStorageData(true);
- if (oldData == null) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("analyzeExternalChangesAndUpdateIfNeed: old data null, load new for " + toString());
- }
- componentNames.addAll(newData.getComponentNames());
- }
- else {
- Set changedComponentNames = oldData.getChangedComponentNames(newData, myPathMacroSubstitutor);
- if (LOG.isDebugEnabled()) {
- LOG.debug("analyzeExternalChangesAndUpdateIfNeed: changedComponentNames + " + changedComponentNames + " for " + toString());
- }
- if (!ContainerUtil.isEmpty(changedComponentNames)) {
- componentNames.addAll(changedComponentNames);
- }
- }
- }
-
- public abstract class XmlElementStorageSaveSession extends SaveSessionBase {
- private final StorageData myOriginalStorageData;
- @Nullable
- private StorageData myCopiedStorageData;
-
- private final Map myNewLiveStates = new THashMap();
-
- public XmlElementStorageSaveSession(@NotNull StorageData storageData) {
- myOriginalStorageData = storageData;
- }
-
- @Nullable
- @Override
- public final SaveSession createSaveSession() {
- return checkIsSavingDisabled() || (myCopiedStorageData == null && !myOriginalStorageData.isDirty()) ? null : this;
- }
-
- @Override
- protected void setSerializedState(@NotNull Object component, @NotNull String componentName, @Nullable Element element) {
- if (myCopiedStorageData == null) {
- myCopiedStorageData = StorageData.setStateAndCloneIfNeed(componentName, element, myOriginalStorageData, myNewLiveStates);
- }
- else {
- myCopiedStorageData.setState(componentName, element, myNewLiveStates);
- }
- }
-
- @Override
- public final void save() throws IOException {
- if (myBlockSavingTheContent) {
- return;
- }
-
- StorageData storageData = myCopiedStorageData;
- if (storageData == null) {
- storageData = myOriginalStorageData;
- if (!storageData.isDirty()) {
- LOG.warn("Copied storage data must be not null because original storage data is not dirty");
- }
- }
-
- doSave(getElement(storageData, myNewLiveStates));
- myStorageData = storageData;
- }
-
- protected abstract void doSave(@Nullable Element element) throws IOException;
-
- protected final void saveForProvider(@Nullable BufferExposingByteArrayOutputStream content, @Nullable Element element) throws IOException {
- if (!myStreamProvider.isApplicable(myFileSpec, myRoamingType)) {
- return;
- }
-
- if (element == null) {
- myStreamProvider.delete(myFileSpec, myRoamingType);
- }
- else {
- doSaveForProvider(element, myRoamingType, content);
- }
- }
-
- private void doSaveForProvider(@NotNull Element element, @NotNull RoamingType roamingType, @Nullable BufferExposingByteArrayOutputStream content) throws IOException {
- if (content == null) {
- StorageUtil.sendContent(myStreamProvider, myFileSpec, element, roamingType);
- }
- else {
- myStreamProvider.saveContent(myFileSpec, content.getInternalBuffer(), content.size(), myRoamingType);
- }
- }
- }
-}
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 3aa26b6f66e7..e68fddb7745e 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
@@ -144,6 +144,8 @@ public class EditorColorsManagerImpl extends EditorColorsManager implements Pers
loadAdditionalTextAttributes();
+ upgradeSchemesFromPreviousVersion();
+
String wizardEditorScheme = WelcomeWizardUtil.getWizardEditorScheme();
EditorColorsScheme scheme = null;
if (wizardEditorScheme != null) {
@@ -153,6 +155,14 @@ public class EditorColorsManagerImpl extends EditorColorsManager implements Pers
setGlobalSchemeInner(scheme == null ? getDefaultScheme() : scheme);
}
+ private void upgradeSchemesFromPreviousVersion() {
+ for (EditorColorsScheme scheme : mySchemeManager.getAllSchemes()) {
+ if (scheme instanceof AbstractColorsScheme && !(scheme instanceof ReadOnlyColorsScheme)) {
+ ((AbstractColorsScheme)scheme).upgradeSchemeFromPreviousVersion();
+ }
+ }
+ }
+
public static void schemeChangedOrSwitched() {
EditorFactory.getInstance().refreshAllEditors();
// refreshAllEditors is not enough - for example, change "Errors and warnings -> Typo" from green (default) to red
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 ac2f73eba5af..0cbe383b83e5 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
@@ -44,8 +44,10 @@ import com.intellij.openapi.editor.colors.*;
import com.intellij.openapi.editor.colors.impl.DelegateColorScheme;
import com.intellij.openapi.editor.event.*;
import com.intellij.openapi.editor.ex.*;
+import com.intellij.openapi.editor.ex.util.EditorUIUtil;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.editor.ex.util.EmptyEditorHighlighter;
+import com.intellij.openapi.editor.ex.util.LexerEditorHighlighter;
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
import com.intellij.openapi.editor.highlighter.HighlighterClient;
import com.intellij.openapi.editor.impl.event.MarkupModelListener;
@@ -318,6 +320,16 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
private boolean myDocumentChangeInProgress;
private boolean myErrorStripeNeedsRepaint;
+ // Characters that excluded from zero-latency painting after key typing
+ private static final Set KEY_CHARS_TO_SKIP = new HashSet(Arrays.asList('\n', '\t', '(', ')', '[', ']', '{', '}', '"', '\''));
+
+ // Characters that excluded from zero-latency painting after document update
+ private static final Set DOCUMENT_CHARS_TO_SKIP = new HashSet(Arrays.asList(')', ']', '}', '"', '\''));
+
+ private Rectangle myOldArea = new Rectangle(0, 0, 0, 0);
+ private Rectangle myOldTailArea = new Rectangle(0, 0, 0, 0);
+ private boolean myEventPaintedAsChar;
+
static {
ourCaretBlinkingCommand.start();
}
@@ -1135,6 +1147,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
ActionManagerEx actionManager = ActionManagerEx.getInstanceEx();
DataContext dataContext = getDataContext();
+
+ if (isZeroLatencyTypingEnabled() && myDocument.isWritable() && !isViewer() && canPaintImmediately(c)) {
+ paintImmediately(myCaretModel.getOffset(), c);
+ myEventPaintedAsChar = true;
+ }
+
actionManager.fireBeforeEditorTyping(c, dataContext);
MacUIUtil.hideCursor();
EditorActionManager.getInstance().getTypedAction().actionPerformed(this, c, dataContext);
@@ -1881,12 +1899,31 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
if (!mySoftWrapModel.isSoftWrappingEnabled() && !myUseNewRendering) {
mySizeContainer.beforeChange(e);
}
+
+ if (isZeroLatencyTypingEnabled() && !myEventPaintedAsChar && canPaintImmediately(e)) {
+ int offset = e.getOffset();
+ int length = e.getOldLength();
+
+ myOldArea = lineRectangleBetween(offset, offset + length);
+
+ myOldTailArea = lineRectangleBetween(offset + length, myDocument.getLineEndOffset(myDocument.getLineNumber(offset)));
+ if (myOldTailArea.isEmpty()) {
+ myOldTailArea.width += EditorUtil.getSpaceWidth(Font.PLAIN, this); // include possible caret
+ }
+ }
}
private void changedUpdate(DocumentEvent e) {
myDocumentChangeInProgress = false;
if (myDocument.isInBulkUpdate()) return;
+ if (isZeroLatencyTypingEnabled()) {
+ if (!myEventPaintedAsChar && canPaintImmediately(e)) {
+ paintImmediately(e);
+ }
+ myEventPaintedAsChar = false;
+ }
+
if (myErrorStripeNeedsRepaint) {
myMarkupModel.repaint(e.getOffset(), e.getOffset() + e.getNewLength());
myErrorStripeNeedsRepaint = false;
@@ -2100,6 +2137,146 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
putUserData(BUFFER, image);
}
+ private static boolean isZeroLatencyTypingEnabled() {
+ return Registry.is("editor.zero.latency.typing");
+ }
+
+ private boolean canPaintImmediately(char c) {
+ return myDocument instanceof DocumentImpl &&
+ myHighlighter instanceof LexerEditorHighlighter &&
+ myDocument.getTextLength() > 0 &&
+ myCaretModel.getCaretCount() == 1 &&
+ !mySelectionModel.hasSelection() &&
+ !KEY_CHARS_TO_SKIP.contains(c);
+ }
+
+ // Called to display a single character insertion before starting a write action and the general painting routine.
+ // Bypasses RepaintManager (c.repaint, c.paintComponent) and double buffering (g.paintImmediately) to minimize visual lag.
+ // TODO Should be replaced with the generic paintImmediately(event) call when we implement typing without starting write actions.
+ private void paintImmediately(int offset, char c) {
+ Graphics g = myEditorComponent.getGraphics();
+
+ if (g == null) return; // editor component is currently not displayable
+
+ TextAttributes attributes = ((LexerEditorHighlighter)myHighlighter).getAttributes((DocumentImpl)myDocument, offset, c);
+
+ int fontType = attributes.getFontType();
+ Font font = fontFor(fontType);
+
+ int charWidth = g.getFontMetrics(font).charWidth(c);
+
+ Rectangle tailArea = lineRectangleBetween(offset, myDocument.getLineEndOffset(offsetToLogicalLine(offset)));
+ if (tailArea.isEmpty()) {
+ tailArea.width += EditorUtil.getSpaceWidth(fontType, this); // include caret
+ }
+
+ Color lineColor = getCaretRowBackground();
+
+ Rectangle newArea = lineRectangleBetween(offset, offset);
+ newArea.width += charWidth;
+
+ String newText = Character.toString(c);
+ Point point = newArea.getLocation();
+ int ascent = getAscent();
+ Color color = attributes.getForegroundColor();
+
+ EditorUIUtil.setupAntialiasing(g);
+
+ // pre-compute all the arguments beforehand to minimize delays between the calls (as there's no double-buffering)
+ shift(g, tailArea, charWidth);
+ fill(g, newArea, lineColor);
+ print(g, newText, point, ascent, font, color);
+ }
+
+ private static boolean canPaintImmediately(@NotNull DocumentEvent e) {
+ return !contains(e.getOldFragment(), '\n') &&
+ !contains(e.getNewFragment(), '\n') &&
+ !(e.getNewLength() == 1 && DOCUMENT_CHARS_TO_SKIP.contains(e.getNewFragment().charAt(0)));
+ }
+
+ private static boolean contains(@NotNull CharSequence chars, char c) {
+ for (int i = 0; i < chars.length(); i++) {
+ if (chars.charAt(i) == c) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Called to display insertion / deletion / replacement within a single line before the general painting routine.
+ // Bypasses RepaintManager (c.repaint, c.paintComponent) and double buffering (g.paintImmediately) to minimize visual lag.
+ private void paintImmediately(@NotNull DocumentEvent e) {
+ Graphics g = myEditorComponent.getGraphics();
+
+ if (g == null) return; // editor component is currently not displayable
+
+ int offset = e.getOffset();
+ String newText = e.getNewFragment().toString();
+ Rectangle newArea = lineRectangleBetween(offset, offset + newText.length());
+ int delta = newArea.width - myOldArea.width;
+ Color lineColor = getCaretRowBackground();
+
+ if (delta != 0) {
+ shift(g, myOldTailArea, delta);
+
+ if (delta < 0) {
+ Rectangle remainingArea = new Rectangle(myOldTailArea.x + myOldTailArea.width + delta,
+ myOldTailArea.y, -delta, myOldTailArea.height);
+ fill(g, remainingArea, lineColor);
+ }
+ }
+
+ if (!newArea.isEmpty()) {
+ TextAttributes attributes = myHighlighter.createIterator(offset).getTextAttributes();
+
+ Point point = newArea.getLocation();
+ int ascent = getAscent();
+ Font font = fontFor(attributes.getFontType());
+ Color color = attributes.getForegroundColor();
+
+ EditorUIUtil.setupAntialiasing(g);
+
+ // pre-compute all the arguments beforehand to minimize delay between the calls (as there's no double-buffering)
+ fill(g, newArea, lineColor);
+ print(g, newText, point, ascent, font, color);
+ }
+ }
+
+ @NotNull
+ private Rectangle lineRectangleBetween(int begin, int end) {
+ Point p1 = offsetToXY(begin, false);
+ Point p2 = offsetToXY(end, false);
+ LOG.assertTrue(p1.y == p2.y);
+ return new Rectangle(p1.x, p1.y, p2.x - p1.x, getLineHeight());
+ }
+
+ @NotNull
+ private Color getCaretRowBackground() {
+ Color color = myScheme.getColor(EditorColors.CARET_ROW_COLOR);
+ return color == null ? myScheme.getDefaultBackground() : color;
+ }
+
+ @NotNull
+ private Font fontFor(int fontType) {
+ return myScheme.getFont(EditorFontType.values()[fontType]);
+ }
+
+ private static void shift(@NotNull Graphics g, @NotNull Rectangle r, int delta) {
+ g.copyArea(r.x, r.y, r.width, r.height, delta, 0);
+ }
+
+ private static void fill(@NotNull Graphics g, @NotNull Rectangle r, @NotNull Color color) {
+ g.setColor(color);
+ g.fillRect(r.x, r.y, r.width, r.height);
+ }
+
+ private static void print(@NotNull Graphics g, @NotNull String text, @NotNull Point point,
+ int ascent, @NotNull Font font, @NotNull Color color) {
+ g.setFont(font);
+ g.setColor(color);
+ g.drawString(text, point.x, point.y + ascent);
+ }
+
void paint(@NotNull Graphics2D g) {
Rectangle clip = g.getClipBounds();
diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorComposite.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorComposite.java
index 6a343a2049dd..5491f1cdef55 100644
--- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorComposite.java
+++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorComposite.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.
@@ -171,9 +171,11 @@ public abstract class EditorComposite implements Disposable {
});
wrapper.getTabs().getComponent().setBorder(new EmptyBorder(0, 0, 1, 0));
+ boolean firstEditor = true;
for (FileEditor editor : editors) {
- JComponent component = myEditors.length == 1 && editor == myEditors[0] ? (JComponent)myComponent.getComponent(0) : createEditorComponent(editor);
+ JComponent component = firstEditor && myComponent != null ? (JComponent)myComponent.getComponent(0) : createEditorComponent(editor);
wrapper.addTab(editor.getName(), component);
+ firstEditor = false;
}
wrapper.addChangeListener(new MyChangeListener());
@@ -458,9 +460,9 @@ public abstract class EditorComposite implements Disposable {
void addEditor(@NotNull FileEditor editor) {
ApplicationManager.getApplication().assertIsDispatchThread();
- FileEditor[] editors = ArrayUtil.append(myEditors, editor);
+ myEditors = ArrayUtil.append(myEditors, editor);
if (myTabbedPaneWrapper == null) {
- myTabbedPaneWrapper = createTabbedPaneWrapper(editors);
+ myTabbedPaneWrapper = createTabbedPaneWrapper(myEditors);
myComponent.setComponent(myTabbedPaneWrapper.getComponent());
}
else {
@@ -469,7 +471,6 @@ public abstract class EditorComposite implements Disposable {
}
myFocusWatcher.deinstall(myFocusWatcher.getTopComponent());
myFocusWatcher.install(myComponent);
- myEditors = editors;
}
private static class TopBottomPanel extends JPanel {
diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWithProviderComposite.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWithProviderComposite.java
index 63677ed684c1..7e6f4d371014 100644
--- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWithProviderComposite.java
+++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWithProviderComposite.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2013 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.
diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsDialog.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsDialog.java
index 17264b5cfc42..915cd2d85031 100644
--- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsDialog.java
+++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsDialog.java
@@ -21,8 +21,6 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.help.HelpManager;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurableGroup;
-import com.intellij.openapi.project.DumbModePermission;
-import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import org.jetbrains.annotations.NonNls;
@@ -143,12 +141,7 @@ public class SettingsDialog extends DialogWrapper implements DataProvider {
@Override
public void doOKAction() {
if (myEditor.apply()) {
- DumbService.allowStartingDumbModeInside(DumbModePermission.MAY_START_BACKGROUND, new Runnable() {
- @Override
- public void run() {
- ApplicationManager.getApplication().saveAll();
- }
- });
+ ApplicationManager.getApplication().saveAll();
super.doOKAction();
}
}
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 bdbf1b57368a..17117b22af2a 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
@@ -695,8 +695,8 @@ public class ProjectManagerImpl extends ProjectManagerEx implements Disposable {
public void saveChangedProjectFile(@NotNull VirtualFile file, @NotNull Project project) {
StateStorageManager storageManager = ComponentsPackage.getStateStore(project).getStateStorageManager();
String fileSpec = storageManager.collapseMacros(file.getPath());
- Couple> storages = storageManager.getCachedFileStateStorages(Collections.singletonList(fileSpec), Collections.emptyList());
- FileBasedStorage storage = ContainerUtil.getFirstItem(storages.first);
+ Couple> storages = storageManager.getCachedFileStateStorages(Collections.singletonList(fileSpec), Collections.emptyList());
+ FileStorage storage = ContainerUtil.getFirstItem(storages.first);
// if empty, so, storage is not yet loaded, so, we don't have to reload
if (storage != null) {
registerProjectToReload(project, storage);
diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java
index ca3547ceea2f..6b64443d3acf 100644
--- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java
+++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java
@@ -212,7 +212,7 @@ public abstract class ToolWindowHeader extends JPanel implements Disposable, UIS
@Override
protected void updateActionTooltips() {
- for (ActionButton actionButton : JBSwingUtilities.uiTraverser().preOrderDfsTraversal(myButtonPanel).filter(ActionButton.class)) {
+ for (ActionButton actionButton : JBSwingUtilities.uiTraverser().withRoot(myButtonPanel).preOrderDfsTraversal().filter(ActionButton.class)) {
actionButton.updateTooltip();
}
}
diff --git a/platform/platform-impl/src/com/intellij/ui/mac/MacFileChooserDialogImpl.java b/platform/platform-impl/src/com/intellij/ui/mac/MacFileChooserDialogImpl.java
index 623fe0e2ba1e..ba54fd98bab4 100644
--- a/platform/platform-impl/src/com/intellij/ui/mac/MacFileChooserDialogImpl.java
+++ b/platform/platform-impl/src/com/intellij/ui/mac/MacFileChooserDialogImpl.java
@@ -17,6 +17,7 @@ package com.intellij.ui.mac;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ApplicationNamesInfo;
+import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
@@ -62,6 +63,7 @@ public class MacFileChooserDialogImpl implements PathChooserDialog {
private final FileChooserDescriptor myChooserDescriptor;
private final Project myProject;
+ private ModalityState myModalityState;
private Consumer> myCallback;
private static final Callback SHOULD_ENABLE_CALLBACK = new Callback() {
@@ -160,14 +162,14 @@ public class MacFileChooserDialogImpl implements PathChooserDialog {
}
});
}
- });
+ }, impl.myModalityState);
} else if (impl.myCallback instanceof FileChooser.FileChooserConsumer) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
((FileChooser.FileChooserConsumer)impl.myCallback).cancelled();
}
- });
+ }, impl.myModalityState);
}
}
finally {
@@ -324,6 +326,7 @@ public class MacFileChooserDialogImpl implements PathChooserDialog {
ExtensionsInitializer.initialize();
myCallback = callback;
+ myModalityState = ModalityState.current();
final VirtualFile lastOpenedFile = FileChooserUtil.getLastOpenedFile(myProject);
final VirtualFile selectFile = FileChooserUtil.getFileToSelect(myChooserDescriptor, myProject, toSelect, lastOpenedFile);
diff --git a/platform/platform-impl/src/com/intellij/util/IJSwingUtilities.java b/platform/platform-impl/src/com/intellij/util/IJSwingUtilities.java
index c6b0c044ad8a..7f1750de8701 100644
--- a/platform/platform-impl/src/com/intellij/util/IJSwingUtilities.java
+++ b/platform/platform-impl/src/com/intellij/util/IJSwingUtilities.java
@@ -231,7 +231,7 @@ public class IJSwingUtilities extends JBSwingUtilities {
*/
public static void updateComponentTreeUI(@Nullable Component c) {
if (c == null) return;
- for (Component component : uiTraverser().postOrderDfsTraversal(c)) {
+ for (Component component : uiTraverser().withRoot(c).postOrderDfsTraversal()) {
if (component instanceof JComponent) ((JComponent)component).updateUI();
}
c.invalidate();
diff --git a/platform/platform-resources-en/src/messages/DiagnosticBundle.properties b/platform/platform-resources-en/src/messages/DiagnosticBundle.properties
index 9b044dc411e7..4dedc81be079 100644
--- a/platform/platform-resources-en/src/messages/DiagnosticBundle.properties
+++ b/platform/platform-resources-en/src/messages/DiagnosticBundle.properties
@@ -67,7 +67,7 @@ log.monitor.edit.aliases.show.all.checkbox.title=&Show all files coverable by pa
log.console.filter.show.errors=errors
log.console.filter.show.errors.and.warnings=warnings
log.console.filter.show.all=all
-title.submitting.error.report=Submitting error report
+title.submitting.error.report=Submitting Error Report
log.console.filter.show.errors.warnings.and.infos=infos
error.comments.tab.title=Comments
error.dialog.error.prompt=&Error message
diff --git a/platform/platform-resources-en/src/messages/IdeBundle.properties b/platform/platform-resources-en/src/messages/IdeBundle.properties
index 524ab5648139..f1695f65c1e6 100644
--- a/platform/platform-resources-en/src/messages/IdeBundle.properties
+++ b/platform/platform-resources-en/src/messages/IdeBundle.properties
@@ -1182,3 +1182,11 @@ new.dir.project.error.buck=Project directory name must not contain the ''$'' cha
go.to.file.toolwindow.title=Files matching pattern
go.to.class.toolwindow.title=Classes matching pattern
go.to.class.dumb.mode.message=Go To Class action is not available until indices are built, using Go To File instead
+
+checkbox.color.blindness=Adjust for red-green color deficiency (restart required)
+# define at least one name to show checkbox
+# define more that one name to show combobox
+combobox.color.blindness.protanopia=Protanopia
+combobox.color.blindness.deuteranopia=
+combobox.color.blindness.tritanopia=
+combobox.color.blindness.achromatopsia=
diff --git a/platform/platform-resources/src/brokenPlugins.txt b/platform/platform-resources/src/brokenPlugins.txt
index 42907a5adba7..2f8d7013f0b5 100644
--- a/platform/platform-resources/src/brokenPlugins.txt
+++ b/platform/platform-resources/src/brokenPlugins.txt
@@ -1,14 +1,14 @@
// This file contains list of broken plugins.
// Each line contains plugin ID and list of versions that are broken.
// If plugin name or version contains a space you can quote it like in command line.
-NodeJS 142.2492 142.2481 142.2064 141.1108 140.2045 140.1669 140.642 139.173 139.105 139.496 139.1 139.8 138.2196 138.2254 138.1684 138.1744 138.1879 138.2051 138.1367 138.1495 138.1189 138.1145 138.937 138.1013 138.921 138.447 138.172 138.317 138.21 138.35 138.96 138.85 136.1205 134.1276 134.1163 134.1145 134.1081 134.1039 134.985 134.680 134.31 134.307 134.262 134.198 134.125 136.1141
+NodeJS 142.3224 142.2650 142.2492 142.2481 142.2064 141.1108 140.2045 140.1669 140.642 139.173 139.105 139.496 139.1 139.8 138.2196 138.2254 138.1684 138.1744 138.1879 138.2051 138.1367 138.1495 138.1189 138.1145 138.937 138.1013 138.921 138.447 138.172 138.317 138.21 138.35 138.96 138.85 136.1205 134.1276 134.1163 134.1145 134.1081 134.1039 134.985 134.680 134.31 134.307 134.262 134.198 134.125 136.1141
com.jetbrains.php 140.2765 141.332 139.732 139.659 139.496 139.173 139.105 138.2502 138.2000.2262 138.1751 138.1806 138.1505 138.1161 138.826 136.1768 136.1672 134.1456 133.982 133.679 133.51 133.326 131.98 131.374 131.332 131.235 131.205 130.1639 130.1481 130.1176 129.91 129.814 129.672 129.362 127.67 127.100 126.334 123.66 122.875 121.62 121.390 121.215 121.12
com.jetbrains.lang.ejs 131.17 131.12
com.jetbrains.twig 133.51 130.1639
-org.jetbrains.plugins.ruby 7.0.0.20150216 7.0.0.20150204 7.0.0.20141010 7.0.0.20140915 7.0.0.20140901 7.0.0.20140807 6.0.0.20140207 6.5.2.20140512 7.0.0.20140704 7.0.0.20140707 7.0.0.20140724
-Pythonid "4.1 141.4 EAP" 3.1 4.0.25 4.0.26
+org.jetbrains.plugins.ruby 8.0.0.20150624 8.0.0.20150616 7.0.0.20150216 7.0.0.20150204 7.0.0.20141010 7.0.0.20140915 7.0.0.20140901 7.0.0.20140807 6.0.0.20140207 6.5.2.20140512 7.0.0.20140704 7.0.0.20140707 7.0.0.20140724
+Pythonid "142.176 v4.5" 4.1 "4.1 141.4 EAP" 3.1 4.0.25 4.0.26
Karma 140.2109 138.1684 138.1879 138.317 138.21 134.1163 134.1039 134.686 134.31
-org.intellij.scala 1.6.0 1.2.67.3.EAP 1.3.2-14.1 1.3.3-14.1 1.0.93.10.EAP 1.0.93.4.EAP 1.0.93.9.EAP 1.0.58 1.0.16 1.0.14 1.0.13 0.42.28 0.42.23 0.40.20 0.40.18 0.40.16 0.32.593 0.32.562 0.32.558 0.32.550 0.32.520 0.32.512
+org.intellij.scala 1.7.4 1.7.2 1.6.2 1.6.1 1.6.0 1.2.67.3.EAP 1.3.2-14.1 1.3.3-14.1 1.0.93.10.EAP 1.0.93.4.EAP 1.0.93.9.EAP 1.0.58 1.0.16 1.0.14 1.0.13 0.42.28 0.42.23 0.40.20 0.40.18 0.40.16 0.32.593 0.32.562 0.32.558 0.32.550 0.32.520 0.32.512
org.jetbrains.kannotator 0.2.420
org.jetbrains.kotlin 0.10.555 0.10.4 0.8.1615 0.7.1360 0.7.1376 0.8.7
SBT 1.0.0 1.1.0 1.2.0 1.3.0 1.3.1 1.4.0 1.5.0 1.5.1 1.6.0 1.6.1
@@ -28,4 +28,5 @@ AWSCloudFormation 0.3.9
com.jetbrains.typoscript 0.1.1 0.1.0
Dart 133.239 133.432 133.726 130.1494 130.549 131.17 132.1045 132.207 132.835 130.1494 131.17 0.5.1
IdeaVIM 0.15.59 0.16.60 0.20.75 0.20.80 0.21.86 0.22.90 0.23.111 0.23.115 0.23.93 0.24-148 0.25 0.26 0.27 0.28 0.29 0.30 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39
-IDETalk 0.5 0.5.1 0.5.2 0.5.3 0.5.5 0.5.6 0.5.6.1 0.5.6.2 0.5.4 0.1.2 0.1.1 141.177.4
\ No newline at end of file
+IDETalk 0.5 0.5.1 0.5.2 0.5.3 0.5.5 0.5.6 0.5.6.1 0.5.6.2 0.5.4 0.1.2 0.1.1 141.177.4
+com.jetbrains.plugins.meteor 142.2481
\ No newline at end of file
diff --git a/platform/platform-resources/src/idea/LangActions.xml b/platform/platform-resources/src/idea/LangActions.xml
index 1f043ec05433..2589cbd4903e 100644
--- a/platform/platform-resources/src/idea/LangActions.xml
+++ b/platform/platform-resources/src/idea/LangActions.xml
@@ -12,6 +12,9 @@
+
+
+
@@ -849,7 +852,6 @@
-
diff --git a/platform/platform-tests/testSrc/com/intellij/history/integration/ui/FileHistoryDialogTest.java b/platform/platform-tests/testSrc/com/intellij/history/integration/ui/FileHistoryDialogTest.java
index 8a138b961877..19680575a3b6 100644
--- a/platform/platform-tests/testSrc/com/intellij/history/integration/ui/FileHistoryDialogTest.java
+++ b/platform/platform-tests/testSrc/com/intellij/history/integration/ui/FileHistoryDialogTest.java
@@ -110,8 +110,8 @@ public class FileHistoryDialogTest extends LocalHistoryUITestCase {
DiffContent left = getLeftDiffContent(m);
DiffContent right = getRightDiffContent(m);
- assertEquals(leftContent, left);
- assertEquals(rightContent, right);
+ assertContent(leftContent, left);
+ assertContent(rightContent, right);
}
private DiffContent getLeftDiffContent(FileHistoryDialogModel m) {
diff --git a/platform/platform-tests/testSrc/com/intellij/history/integration/ui/LocalHistoryUITestCase.java b/platform/platform-tests/testSrc/com/intellij/history/integration/ui/LocalHistoryUITestCase.java
index 36dcb951c1b0..3de6a99e6dbd 100644
--- a/platform/platform-tests/testSrc/com/intellij/history/integration/ui/LocalHistoryUITestCase.java
+++ b/platform/platform-tests/testSrc/com/intellij/history/integration/ui/LocalHistoryUITestCase.java
@@ -23,7 +23,7 @@ import com.intellij.testFramework.SkipInHeadlessEnvironment;
@SkipInHeadlessEnvironment
public abstract class LocalHistoryUITestCase extends IntegrationTestCase {
- protected void assertEquals(String expected, DiffContent actual) {
+ protected void assertContent(String expected, DiffContent actual) {
actual.onAssigned(true);
try {
assertEquals(expected, ((DocumentContent)actual).getDocument().getText());
diff --git a/platform/platform-tests/testSrc/com/intellij/history/integration/ui/SelectionHistoryDialogTest.java b/platform/platform-tests/testSrc/com/intellij/history/integration/ui/SelectionHistoryDialogTest.java
index 008a9bb166a9..b97af0ef8787 100644
--- a/platform/platform-tests/testSrc/com/intellij/history/integration/ui/SelectionHistoryDialogTest.java
+++ b/platform/platform-tests/testSrc/com/intellij/history/integration/ui/SelectionHistoryDialogTest.java
@@ -90,8 +90,8 @@ public class SelectionHistoryDialogTest extends LocalHistoryUITestCase {
DiffContent left = dm.getLeftDiffContent(new NullRevisionsProgress());
DiffContent right = dm.getRightDiffContent(new NullRevisionsProgress());
- assertEquals("b", left);
- assertEquals("bc", right);
+ assertContent("b", left);
+ assertContent("bc", right);
}
public void testDiffContentsAndTitleForCurrentRevision() throws IOException {
@@ -101,7 +101,7 @@ public class SelectionHistoryDialogTest extends LocalHistoryUITestCase {
DiffContent right = dm.getRightDiffContent(new NullRevisionsProgress());
- assertEquals("bcd", right);
+ assertContent("bcd", right);
assertTrue(right instanceof DocumentFragmentContent);
}
@@ -114,8 +114,8 @@ public class SelectionHistoryDialogTest extends LocalHistoryUITestCase {
initModelOnSecondLineAndSelectRevisions(3, 3);
- assertEquals("b", dm.getLeftDiffContent(new NullRevisionsProgress()));
- assertEquals("bcd", dm.getRightDiffContent(new NullRevisionsProgress()));
+ assertContent("b", dm.getLeftDiffContent(new NullRevisionsProgress()));
+ assertContent("bcd", dm.getRightDiffContent(new NullRevisionsProgress()));
}
public void testRevert() throws IOException {
diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/LaterInvocatorTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/LaterInvocatorTest.java
new file mode 100644
index 000000000000..7b72162a0903
--- /dev/null
+++ b/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/LaterInvocatorTest.java
@@ -0,0 +1,559 @@
+package com.intellij.openapi.application.impl;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.Conditions;
+import com.intellij.testFramework.PlatformTestCase;
+import com.intellij.testFramework.SkipInHeadlessEnvironment;
+import com.intellij.testFramework.UsefulTestCase;
+import com.intellij.util.ui.UIUtil;
+import junit.framework.TestCase;
+
+import javax.swing.*;
+import java.awt.*;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+
+@SuppressWarnings({"SSBasedInspection", "SynchronizeOnThis"})
+@SkipInHeadlessEnvironment
+public class LaterInvocatorTest extends PlatformTestCase {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.application.impl.LaterInvokatorTest");
+
+ private final ArrayList myOrder = new ArrayList();
+
+ @SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized") private Window myWindow1;
+ private Window myWindow2;
+
+ private final Runnable LEAVE_MODAL = new Runnable() {
+ @Override
+ public void run() {
+ LaterInvocator.leaveModal(myWindow1);
+ }
+ public String toString() {
+ return "leave modal later";
+ }
+
+ };
+ private final Runnable ENTER_MODAL = new Runnable() {
+ @Override
+ public void run() {
+ LaterInvocator.enterModal(myWindow1);
+ }
+
+ public String toString() {
+ return "enter modal later";
+ }
+ };
+
+ @Override
+ protected void setUp() throws Exception {
+ myWindow1 = new Frame() {
+ public String toString() {
+ return "Window1";
+ }
+ };
+ myWindow2 = new Frame() {
+ public String toString() {
+ return "Window2";
+ }
+ };
+ final Exception[] exception = {null};
+ UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ LaterInvocatorTest.super.setUp();
+ final Object[] modalEntities = LaterInvocator.getCurrentModalEntities();
+ if (modalEntities.length > 0) {
+ LOG.error(
+ "Expect no modal entries. Probably some of the previous tests didn't left their entries. Top entry is: " + modalEntities[0]);
+ }
+ }
+ catch (Exception e) {
+ exception[0] = e;
+ }
+ }
+ });
+ if (exception[0] != null) throw exception[0];
+ UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+ @Override
+ public void run() {
+ TestCase.assertFalse("Can't run test", LaterInvocator.isInModalContext());
+ }
+ });
+
+ flushSwingQueue();
+ }
+
+ @Override
+ protected void runBareRunnable(Runnable runnable) throws Throwable {
+ runnable.run();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ myOrder.clear();
+ final boolean[] inModalState = {true};
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (inModalState) {
+ inModalState[0] = false;
+ }
+ }
+ }, ModalityState.NON_MODAL);
+ flushSwingQueue();
+ flushSwingQueue();
+ UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+ @Override
+ public void run() {
+ while (true) {
+ synchronized (inModalState) {
+ if (!inModalState[0]) break;
+ }
+ flushSwingQueue();
+ synchronized (inModalState) {
+ if (inModalState[0] && LaterInvocator.isInModalContext()) LaterInvocator.leaveAllModals();
+ }
+ }
+ }
+ });
+
+ final Exception[] exception = {null};
+ UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ LaterInvocatorTest.super.tearDown();
+ }
+ catch (Exception e) {
+ exception[0] = e;
+ }
+ }
+
+ public String toString() {
+ return "super teardown";
+ }
+ });
+ if (exception[0] != null) throw exception[0];
+ }
+
+ public void testReorder() throws InterruptedException, InvocationTargetException {
+ UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+ @Override
+ public void run() {
+ LaterInvocator.enterModal(myWindow1);
+ synchronized (LaterInvocatorTest.this) {
+ blockSwingThread();
+ ApplicationManager.getApplication().invokeLater(new MyRunnable("1"), ModalityState.NON_MODAL);
+ SwingUtilities.invokeLater(ENTER_MODAL);
+ LaterInvocator.leaveModal(myWindow1);
+ SwingUtilities.invokeLater(LEAVE_MODAL);
+ ApplicationManager.getApplication().invokeLater(new MyRunnable("2"), ModalityState.NON_MODAL);
+ }
+ flushSwingQueue();
+ checkOrder(2);
+ }
+ });
+ }
+
+ public void testTrueReorder() throws InvocationTargetException, InterruptedException {
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ LaterInvocator.enterModal(myWindow1);
+ ApplicationManager.getApplication().invokeLater(new MyRunnable("1"), ModalityState.NON_MODAL);
+ SwingUtilities.invokeLater(ENTER_MODAL);
+ LaterInvocator.leaveModal(myWindow1);
+ SwingUtilities.invokeLater(LEAVE_MODAL);
+ ApplicationManager.getApplication().invokeLater(new MyRunnable("2"), ModalityState.NON_MODAL);
+ }
+ });
+ flushSwingQueue();
+ flushSwingQueue();
+ checkOrder(2);
+ }
+
+ public void testEverInvoked() throws InvocationTargetException, InterruptedException {
+ UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+ @Override
+ public void run() {
+ LaterInvocator.enterModal(myWindow1);
+ ApplicationManager.getApplication().invokeLater(ENTER_MODAL, ModalityState.NON_MODAL);
+ ApplicationManager.getApplication().invokeLater(new MyRunnable("1"), ModalityState.NON_MODAL);
+ LaterInvocator.leaveModal(myWindow1);
+ flushSwingQueue();
+ flushSwingQueue();
+ checkOrder(0);
+ SwingUtilities.invokeLater(LEAVE_MODAL);
+ flushSwingQueue();
+ flushSwingQueue();
+ checkOrder(1);
+ }
+ });
+ }
+
+ public void testDoesNotInvokeWhenModal() throws InvocationTargetException, InterruptedException {
+ UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+ @Override
+ public void run() {
+ LaterInvocator.enterModal(myWindow1);
+
+ ApplicationManager.getApplication().invokeLater(ENTER_MODAL, ModalityState.NON_MODAL);
+ ApplicationManager.getApplication().invokeLater(new MyRunnable("1"), ModalityState.NON_MODAL);
+ LaterInvocator.leaveModal(myWindow1);
+ flushSwingQueue();
+ checkOrder(0);
+ LaterInvocator.leaveModal(myWindow1);
+ flushSwingQueue();
+ checkOrder(1);
+ }
+ });
+ }
+
+ public void testRunQueuedRunnablesOnLeavingModality() {
+ UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+ @Override
+ public void run() {
+ LaterInvocator.invokeLater(ENTER_MODAL, ModalityState.NON_MODAL);
+ LaterInvocator.invokeLater(new MyRunnable("3"), ModalityState.NON_MODAL);
+ flushSwingQueue();
+ checkOrder(0);
+
+ final ModalityState modalityState = ModalityState.stateForComponent(myWindow1);
+ LaterInvocator.invokeLater(new MyRunnable("1") {
+ @Override
+ public void run() {
+ super.run();
+ checkOrder(1);
+ LaterInvocator.invokeLater(new MyRunnable("2"), modalityState);
+ checkOrder(1);
+ LaterInvocator.leaveModal(myWindow1);
+ checkOrder(2);
+ }
+ }, modalityState);
+ flushSwingQueue(); // let "1" run
+
+ flushSwingQueue(); // let "3" run
+ checkOrder(3);
+ }
+ });
+ }
+
+ public void testStress() throws Exception {
+ UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+ @Override
+ public void run() {
+ int N = 1000;
+ //long start = System.currentTimeMillis();
+ for (int i = 0; i < N; i++) {
+ /*
+ if (i % 10 == 0) {
+ long elapsed = System.currentTimeMillis() - start;
+ System.out.println("i = " + i+"; elapsed="+elapsed);
+ start = System.currentTimeMillis();
+ }
+ */
+
+ //assertEquals(null, Toolkit.getDefaultToolkit().getSystemEventQueue().peekEvent());
+ //assertEquals(null, LaterInvocator.dumpQueue());
+
+ UsefulTestCase.assertEmpty(LaterInvocator.getCurrentModalEntities());
+ LaterInvocator.enterModal(myWindow2);
+ //some weird things like MyFireIdleRequest may still sneak in
+ //assertEquals(null, LaterInvocator.dumpQueue());
+ TestCase.assertTrue(LaterInvocator.isInModalContext());
+ TestCase.assertEquals(1, LaterInvocator.getCurrentModalEntities().length);
+
+ LaterInvocator.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ TestCase.assertTrue(!LaterInvocator.isInModalContext());
+ }
+
+ public String toString() {
+ return "ass2";
+ }
+ }, ModalityState.NON_MODAL);
+ LaterInvocator.invokeLater(ENTER_MODAL, ModalityState.NON_MODAL);
+
+ LaterInvocator.invokeLater(new MyRunnable("1"), ModalityState.NON_MODAL);
+
+ //some weird things like MyFireIdleRequest may still sneak in
+ //java.util.List