diff --git a/java/compiler/impl/src/com/intellij/compiler/actions/CompileActionBase.java b/java/compiler/impl/src/com/intellij/compiler/actions/CompileActionBase.java index f7f320df8342..1b6ef6c9ad24 100644 --- a/java/compiler/impl/src/com/intellij/compiler/actions/CompileActionBase.java +++ b/java/compiler/impl/src/com/intellij/compiler/actions/CompileActionBase.java @@ -17,6 +17,8 @@ package com.intellij.compiler.actions; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.application.TransactionKind; +import com.intellij.openapi.application.WrapInTransaction; import com.intellij.openapi.compiler.CompilerManager; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; @@ -24,6 +26,7 @@ import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.DumbService; import com.intellij.psi.PsiFile; +@WrapInTransaction(TransactionKind.Common.ANY_CHANGE) public abstract class CompileActionBase extends AnAction implements DumbAware { public void actionPerformed(AnActionEvent e) { final DataContext dataContext = e.getDataContext(); diff --git a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java index e825106a03e2..369ad83d08b7 100644 --- a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java +++ b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java @@ -36,10 +36,7 @@ import com.intellij.ide.PowerSaveMode; import com.intellij.ide.file.BatchFileChangeListener; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; -import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.application.*; import com.intellij.openapi.compiler.CompilationStatusListener; import com.intellij.openapi.compiler.CompileContext; import com.intellij.openapi.compiler.CompilerPaths; @@ -77,7 +74,6 @@ import com.intellij.openapi.vfs.newvfs.events.VFileEvent; import com.intellij.openapi.vfs.newvfs.impl.FileNameCache; import com.intellij.openapi.wm.IdeFrame; import com.intellij.util.*; -import com.intellij.util.concurrency.Semaphore; import com.intellij.util.concurrency.SequentialTaskExecutor; import com.intellij.util.containers.IntArrayList; import com.intellij.util.io.storage.HeavyProcessLatch; @@ -105,7 +101,6 @@ import org.jetbrains.jps.cmdline.ClasspathBootstrap; import org.jetbrains.jps.incremental.Utils; import org.jetbrains.jps.model.serialization.JpsGlobalLoader; -import javax.swing.*; import javax.tools.*; import java.awt.*; import java.io.File; @@ -186,25 +181,11 @@ public class BuildManager implements Disposable { return Registry.intValue("compiler.document.save.trigger.delay"); } - private final Semaphore mySemaphore = new Semaphore(); - private final Runnable mySaveDocsRunnable = new Runnable() { - @Override - public void run() { - try { - ((FileDocumentManagerImpl)FileDocumentManager.getInstance()).saveAllDocuments(false); - } - finally { - mySemaphore.up(); - } - } - }; - @Override public void runTask() { if (shouldSaveDocuments()) { - mySemaphore.down(); - ApplicationManager.getApplication().invokeLater(mySaveDocsRunnable, ModalityState.NON_MODAL); - mySemaphore.waitFor(); + TransactionGuard.getInstance().submitTransactionAndWait(TransactionKind.ANY_CHANGE, () -> + ((FileDocumentManagerImpl)FileDocumentManager.getInstance()).saveAllDocuments(false)); } } @@ -772,12 +753,7 @@ public class BuildManager implements Disposable { // ensure project model is saved on disk, so that automake sees the latest model state. // For ordinary make all project, app settings and unsaved docs are always saved before build starts. try { - SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - project.save(); - } - }); + TransactionGuard.getInstance().submitTransactionAndWait(TransactionKind.ANY_CHANGE, project::save); } catch (Throwable e) { LOG.info(e); diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaPredefinedConfigurations.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaPredefinedConfigurations.java index 345eef5f2531..dba551260f4a 100644 --- a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaPredefinedConfigurations.java +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaPredefinedConfigurations.java @@ -24,7 +24,7 @@ class JavaPredefinedConfigurations { // Expression patterns createSearchTemplateInfo(SSRBundle.message("predefined.configuration.method.calls"), "'_Instance?.'MethodCall('_Parameter*)", EXPRESSION_TYPE), createSearchTemplateInfo(SSRBundle.message("predefined.configuration.new.expressions"), "new 'Constructor('_Argument*)", EXPRESSION_TYPE), - createSearchTemplateInfo(SSRBundle.message("predefined.configuration.lambdas"), "('_Parameter*) -> ", EXPRESSION_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.lambdas"), "('_Parameter*) -> {}", EXPRESSION_TYPE), createSearchTemplateInfo(SSRBundle.message("predefined.configuration.field.selections"),"'_Instance?.'Field",EXPRESSION_TYPE), createSearchTemplateInfo(SSRBundle.message("predefined.configuration.array.access"),"'_Field['_Index]",EXPRESSION_TYPE), createSearchTemplateInfo(SSRBundle.message("predefined.configuration.assignments"),"'_Inst = '_Expr",EXPRESSION_TYPE), @@ -279,8 +279,8 @@ class JavaPredefinedConfigurations { createSearchTemplateInfo(SSRBundle.message("predefined.configuration.fields.variables.read"),"'Symbol:[read]",INTERESTING_TYPE), createSearchTemplateInfo(SSRBundle.message("predefined.configuration.fields_variables.with.given.name.pattern.updated"),"'Symbol:[regex( name ) && write]",INTERESTING_TYPE), createSearchTemplateInfo(SSRBundle.message("predefined.configuration.usage.of.derived.type.in.cast"),"('CastType:*[regex( Base )]) '_Expr",INTERESTING_TYPE), - createSearchTemplateInfo(SSRBundle.message("predefined.configuration.boxing.in.declarations"),"'_Type:Object|Integer|Boolean|Long|Character|Short|Byte 'Var = '_Value:[exprtype( int|boolean|long|char|short|byte )]",INTERESTING_TYPE), - createSearchTemplateInfo(SSRBundle.message("predefined.configuration.unboxing.in.declarations"),"'_Type:int|boolean|long|char|short|byte 'Var = '_Value:[exprtype( Integer|Boolean|Long|Character|Short|Byte )]",INTERESTING_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.boxing.in.declarations"),"'_Type:Object|Integer|Boolean|Long|Character|Short|Byte 'Var = '_Value:[exprtype( int|boolean|long|char|short|byte )];",INTERESTING_TYPE), + createSearchTemplateInfo(SSRBundle.message("predefined.configuration.unboxing.in.declarations"),"'_Type:int|boolean|long|char|short|byte 'Var = '_Value:[exprtype( Integer|Boolean|Long|Character|Short|Byte )];",INTERESTING_TYPE), createSearchTemplateInfo(SSRBundle.message("predefined.configuration.boxing.in.method.calls"),"'_Instance?.'Call('_BeforeParam*,'_Param:[ exprtype( int|boolean|long|char|short|byte ) && formal( Object|Integer|Boolean|Long|Character|Short|Byte )],'_AfterParam*)",INTERESTING_TYPE), createSearchTemplateInfo(SSRBundle.message("predefined.configuration.unboxing.in.method.calls"), "'_Instance?.'Call('_BeforeParam*,'_Param:[ formal( int|boolean|long|char|short|byte ) && exprtype( Integer|Boolean|Long|Character|Short|Byte )],'_AfterParam*)",INTERESTING_TYPE), createSearchTemplateInfo(SSRBundle.message("predefined.configuration.any.boxing"), "'_expression:[ exprtype( int|boolean|long|char|short|byte ) && formal( Object|Integer|Boolean|Long|Character|Short|Byte )]", INTERESTING_TYPE), diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java index fa5025b35a81..7ffb5814678c 100644 --- a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java @@ -407,13 +407,35 @@ public class JavaStructuralSearchProfile extends StructuralSearchProfile { @Override public void visitErrorElement(PsiErrorElement element) { super.visitErrorElement(element); - //final PsiElement parent = element.getParent(); - //if (parent != myCurrent || !"';' expected".equals(element.getErrorDescription())) { - // throw new MalformedPatternException(element.getErrorDescription()); - //} + final PsiElement parent = element.getParent(); + final String errorDescription = element.getErrorDescription(); + if (parent instanceof PsiClass && "Identifier expected".equals(errorDescription)) { + // other class content variable. + return; + } + if (parent instanceof PsiTryStatement && "'catch' or 'finally' expected".equals(errorDescription)) { + // searching for naked try allowed + return; + } + if (parent == myCurrent) { + // search for expression, type, annotation or symbol + if ("';' expected".equals(errorDescription)) { + // expression + return; + } + if ("Identifier or type expected".equals(errorDescription)) { + // annotation + return; + } + if ("Identifier expected".equals(errorDescription)) { + // type + return; + } + } + throw new MalformedPatternException(errorDescription); } - public void setCurrent(PsiElement current) { + void setCurrent(PsiElement current) { myCurrent = current; } } @@ -423,7 +445,8 @@ public class JavaStructuralSearchProfile extends StructuralSearchProfile { final NodeIterator nodes = compiledPattern.getNodes(); while (nodes.hasNext()) { final PsiElement current = nodes.current(); - visitor.setCurrent(nodeCount == 1 && current instanceof PsiExpressionStatement ? current : null); + visitor.setCurrent((nodeCount == 1 && (current instanceof PsiExpressionStatement|| current instanceof PsiDeclarationStatement)) + ? current : null); current.accept(visitor); nodes.advance(); } diff --git a/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsConfigurable.java b/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsConfigurable.java index 9646a175c71c..4e5989a89d5f 100644 --- a/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsConfigurable.java +++ b/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsConfigurable.java @@ -374,6 +374,7 @@ public class JsonSchemaMappingsConfigurable extends MasterDetailsComponent imple myText = FileUtil.loadFile(ioFile); } catch (IOException e1) { + JsonSchemaReader.LOG.info(e1); myError = "Problem during reading JSON schema from '" + myFile.getName() + "': " + e1.getMessage(); return false; } diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaReader.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaReader.java index 43d06da54d44..e31f6a1b24da 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaReader.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaReader.java @@ -5,6 +5,7 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; import com.intellij.notification.NotificationGroup; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.util.Consumer; import com.intellij.util.ThrowablePairConsumer; import org.jetbrains.annotations.NotNull; @@ -18,10 +19,12 @@ import java.util.*; * @author Irina.Chernushina on 8/27/2015. */ public class JsonSchemaReader { + public static final Logger LOG = Logger.getInstance("#com.jetbrains.jsonSchema.impl.JsonSchemaReader"); public static final NotificationGroup ERRORS_NOTIFICATION = NotificationGroup.logOnlyGroup("JSON Schema"); public JsonSchemaObject read(@NotNull final Reader reader) throws IOException { final JsonReader in = new JsonReader(reader); + in.setLenient(true); in.beginObject(); final JsonSchemaObject object = new JsonSchemaObject(); @@ -43,9 +46,11 @@ public class JsonSchemaReader { new JsonSchemaReader().read(new java.io.StringReader(string)); return true; } catch (IOException e) { + LOG.info(e); errorConsumer.consume(e.getMessage()); return false; } catch (Exception e) { + LOG.info(e); errorConsumer.consume(e.getMessage()); return false; } @@ -293,7 +298,12 @@ public class JsonSchemaReader { } private JsonSchemaType parseType(JsonReader in) throws IOException { - return JsonSchemaType.valueOf("_" + in.nextString()); + final String typeString = in.nextString(); + try { + return JsonSchemaType.valueOf("_" + typeString); + } catch (IllegalArgumentException e) { + throw new IOException("Wrong type value: " + typeString + "\""); + } } }; } diff --git a/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java b/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java index 70a54e4886a0..eb4b02eec959 100644 --- a/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java @@ -45,7 +45,8 @@ public class TransactionGuardImpl extends TransactionGuard { return AccessToken.EMPTY_ACCESS_TOKEN; } if (myTransactionStartTrace != null) { - LOG.error("Nested transactions are not allowed, see FAQ in TransactionGuard class javadoc. Transaction start trace is in attachment.", + // please assign exceptions that occur here to Peter + LOG.error("Nested transactions are not allowed, see FAQ in TransactionGuard class javadoc. Transaction start trace is in attachment. Kind is " + kind, new Attachment("trace.txt", myTransactionStartTrace)); //throw new IllegalStateException("Nested transactions are not allowed"); } diff --git a/platform/core-impl/src/com/intellij/openapi/vfs/impl/ZipHandler.java b/platform/core-impl/src/com/intellij/openapi/vfs/impl/ZipHandler.java index 2638143eee46..065ad388f6c2 100644 --- a/platform/core-impl/src/com/intellij/openapi/vfs/impl/ZipHandler.java +++ b/platform/core-impl/src/com/intellij/openapi/vfs/impl/ZipHandler.java @@ -26,7 +26,10 @@ import com.intellij.util.text.ByteArrayCharSequence; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.*; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; import java.util.Enumeration; import java.util.Map; import java.util.zip.ZipEntry; @@ -53,14 +56,9 @@ public class ZipHandler extends ArchiveHandler { } @Override - protected void disposeAccessor(final ZipFile fileAccessor) { + protected void disposeAccessor(final ZipFile fileAccessor) throws IOException { // todo: ZipFile isn't disposable for Java6, replace the code below with 'disposeCloseable(fileAccessor);' - disposeCloseable(new Closeable() { - @Override - public void close() throws IOException { - fileAccessor.close(); - } - }); + fileAccessor.close(); } @Override diff --git a/platform/lang-api/src/com/intellij/codeInsight/completion/OffsetMap.java b/platform/lang-api/src/com/intellij/codeInsight/completion/OffsetMap.java index ef797faa556f..90a9b12292e9 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/completion/OffsetMap.java +++ b/platform/lang-api/src/com/intellij/codeInsight/completion/OffsetMap.java @@ -18,6 +18,7 @@ package com.intellij.codeInsight.completion; import com.intellij.openapi.Disposable; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.RangeMarker; +import com.intellij.openapi.progress.ProgressManager; import gnu.trove.THashMap; import gnu.trove.THashSet; @@ -45,18 +46,20 @@ public class OffsetMap implements Disposable { * -1 if offset wasn't registered or became invalidated due to document changes */ public int getOffset(OffsetKey key) { - final RangeMarker marker = myMap.get(key); - if (marker == null) return -1; - if (!marker.isValid()) { - removeOffset(key); - return -1; - } + synchronized (myMap) { + final RangeMarker marker = myMap.get(key); + if (marker == null) return -1; + if (!marker.isValid()) { + removeOffset(key); + return -1; + } - final int endOffset = marker.getEndOffset(); - if (marker.getStartOffset() != endOffset) { - saveOffset(key, endOffset, false); + final int endOffset = marker.getEndOffset(); + if (marker.getStartOffset() != endOffset) { + saveOffset(key, endOffset, false); + } + return endOffset; } - return endOffset; } /** @@ -66,12 +69,14 @@ public class OffsetMap implements Disposable { * @param offset offset in the document */ public void addOffset(OffsetKey key, int offset) { - if (offset < 0) { - removeOffset(key); - return; - } + synchronized (myMap) { + if (offset < 0) { + removeOffset(key); + return; + } - saveOffset(key, offset, true); + saveOffset(key, offset, true); + } } private void saveOffset(OffsetKey key, int offset, boolean externally) { @@ -88,37 +93,49 @@ public class OffsetMap implements Disposable { } public void removeOffset(OffsetKey key) { - assert !myDisposed; - myModified.add(key); - RangeMarker old = myMap.get(key); - if (old != null) old.dispose(); + synchronized (myMap) { + ProgressManager.checkCanceled(); + assert !myDisposed; + myModified.add(key); + RangeMarker old = myMap.get(key); + if (old != null) old.dispose(); - myMap.remove(key); + myMap.remove(key); + } } public List getAllOffsets() { - assert !myDisposed; - return new ArrayList(myMap.keySet()); + synchronized (myMap) { + ProgressManager.checkCanceled(); + assert !myDisposed; + return new ArrayList(myMap.keySet()); + } } @Override public String toString() { - final StringBuilder builder = new StringBuilder("OffsetMap:"); - for (final OffsetKey key : myMap.keySet()) { - builder.append(key).append("->").append(myMap.get(key)).append(";"); + synchronized (myMap) { + final StringBuilder builder = new StringBuilder("OffsetMap:"); + for (final OffsetKey key : myMap.keySet()) { + builder.append(key).append("->").append(myMap.get(key)).append(";"); + } + return builder.toString(); } - return builder.toString(); } public boolean wasModified(OffsetKey key) { - return myModified.contains(key); + synchronized (myMap) { + return myModified.contains(key); + } } @Override public void dispose() { - myDisposed = true; - for (RangeMarker rangeMarker : myMap.values()) { - rangeMarker.dispose(); + synchronized (myMap) { + myDisposed = true; + for (RangeMarker rangeMarker : myMap.values()) { + rangeMarker.dispose(); + } } } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java index 4967228a9617..994a029e4f13 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java @@ -30,7 +30,10 @@ import com.intellij.injected.editor.DocumentWindow; import com.intellij.lang.Language; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.application.AccessToken; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.TransactionGuard; +import com.intellij.openapi.application.TransactionKind; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.*; @@ -176,7 +179,9 @@ public class CodeCompletionHandlerBase { } }; if (autopopup) { - CommandProcessor.getInstance().runUndoTransparentAction(initCmd); + try (AccessToken ignored = TransactionGuard.getInstance().startSynchronousTransaction(TransactionKind.TEXT_EDITING)) { + CommandProcessor.getInstance().runUndoTransparentAction(initCmd); + } CompletionAssertions.checkEditorValid(editor); if (!restarted && shouldSkipAutoPopup(editor, initializationContext[0].getFile())) { CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion); @@ -626,20 +631,18 @@ public class CodeCompletionHandlerBase { private static void afterItemInsertion(final CompletionProgressIndicator indicator, final Runnable laterRunnable) { if (laterRunnable != null) { - final Runnable runnable1 = new Runnable() { - @Override - public void run() { - if (!indicator.getProject().isDisposed()) { - laterRunnable.run(); - } - indicator.disposeIndicator(); + final Runnable runnable1 = () -> { + if (!indicator.getProject().isDisposed()) { + laterRunnable.run(); } + indicator.disposeIndicator(); }; if (ApplicationManager.getApplication().isUnitTestMode()) { runnable1.run(); } else { - ApplicationManager.getApplication().invokeLater(runnable1); + ApplicationManager.getApplication().invokeLater( + () -> TransactionGuard.getInstance().submitMergeableTransaction(TransactionKind.TEXT_EDITING, runnable1)); } } else { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java index a23cdb996000..bdeea74b5bfb 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java @@ -34,8 +34,6 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.IdeActions; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.Result; -import com.intellij.openapi.application.TransactionGuard; -import com.intellij.openapi.application.TransactionKind; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diagnostic.Logger; @@ -476,9 +474,7 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement } void disposeIndicator() { - // our offset map should be disposed under write action, so that duringCompletion (read action) won't access it after disposing - TransactionGuard.getInstance().submitMergeableTransaction(TransactionKind.TEXT_EDITING, () -> - ApplicationManager.getApplication().runWriteAction(() -> Disposer.dispose(this))); + Disposer.dispose(this); } @TestOnly diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/actions/BaseCodeCompletionAction.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/actions/BaseCodeCompletionAction.java index 5dccecef4bee..2b08a932280f 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/actions/BaseCodeCompletionAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/actions/BaseCodeCompletionAction.java @@ -18,13 +18,14 @@ package com.intellij.codeInsight.completion.actions; import com.intellij.codeInsight.completion.CodeCompletionHandlerBase; import com.intellij.codeInsight.completion.CompletionType; import com.intellij.codeInsight.hint.HintManagerImpl; -import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.TransactionKind; +import com.intellij.openapi.application.WrapInTransaction; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiUtilBase; @@ -34,7 +35,8 @@ import java.awt.event.InputEvent; /** * @author peter */ -public abstract class BaseCodeCompletionAction extends AnAction implements HintManagerImpl.ActionToIgnore, DumbAware { +@WrapInTransaction(TransactionKind.Common.TEXT_EDITING) +public abstract class BaseCodeCompletionAction extends DumbAwareAction implements HintManagerImpl.ActionToIgnore { protected BaseCodeCompletionAction() { setEnabledInModalContext(true); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionViewNavigationPanel.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionViewNavigationPanel.java index 4579854165ee..0b316bb3b9c9 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionViewNavigationPanel.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionViewNavigationPanel.java @@ -55,6 +55,6 @@ public class InspectionViewNavigationPanel extends JPanel { @NotNull public static String getTitleText(boolean addGroupWord, boolean addColon) { - return "Select inspection " + (addGroupWord ? "group" : "") + " to see problems" + (addColon ? ":" : "."); + return "Select inspection" + (addGroupWord ? " group " : " ") + "to see problems" + (addColon ? ":" : "."); } } diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java b/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java index 7e8720d52beb..52448f725767 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java @@ -29,6 +29,7 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.TransactionGuard; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.EditorSettings; @@ -479,7 +480,7 @@ public class FindDialog extends DialogWrapper { if (component instanceof EditorTextField) { final Document document = ((EditorTextField)component).getDocument(); if (document != null) { - PsiDocumentManager.getInstance(myProject).commitDocument(document); + TransactionGuard.submitTransaction(() -> PsiDocumentManager.getInstance(myProject).commitDocument(document)); } } diff --git a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java index ceef97a90a55..115023f1657d 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java @@ -1526,7 +1526,7 @@ public class FileBasedIndexImpl extends FileBasedIndex { else { // we do invoke later since we can have read lock acquired //noinspection SSBasedInspection - ApplicationManager.getApplication().invokeLater(rebuildRunnable, ModalityState.NON_MODAL); + ApplicationManager.getApplication().invokeLater(() -> TransactionGuard.submitTransaction(rebuildRunnable), ModalityState.NON_MODAL); } } } diff --git a/platform/platform-impl/src/com/intellij/ide/SaveAndSyncHandlerImpl.java b/platform/platform-impl/src/com/intellij/ide/SaveAndSyncHandlerImpl.java index 40abb126da78..4d24e0aa97f4 100644 --- a/platform/platform-impl/src/com/intellij/ide/SaveAndSyncHandlerImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/SaveAndSyncHandlerImpl.java @@ -74,12 +74,9 @@ public class SaveAndSyncHandlerImpl extends SaveAndSyncHandler implements Dispos mySettings = generalSettings; myProgressManager = progressManager; - myIdleListener = new Runnable() { - @Override - public void run() { - if (mySettings.isAutoSaveIfInactive() && canSyncOrSave()) { - ((FileDocumentManagerImpl)fileDocumentManager).saveAllDocuments(false); - } + myIdleListener = () -> { + if (mySettings.isAutoSaveIfInactive() && canSyncOrSave()) { + TransactionGuard.submitTransaction(() -> ((FileDocumentManagerImpl)fileDocumentManager).saveAllDocuments(false)); } }; IdeEventQueue.getInstance().addIdleListener(myIdleListener, mySettings.getInactiveTimeout() * 1000); diff --git a/platform/platform-impl/src/com/intellij/ide/actions/SaveAllAction.java b/platform/platform-impl/src/com/intellij/ide/actions/SaveAllAction.java index 1425b458c288..2a7b35ebc404 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/SaveAllAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/SaveAllAction.java @@ -18,9 +18,11 @@ package com.intellij.ide.actions; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.WrapInTransaction; import com.intellij.openapi.project.DumbAware; import org.jetbrains.annotations.NotNull; +@WrapInTransaction public class SaveAllAction extends AnAction implements DumbAware { @Override public void actionPerformed(@NotNull AnActionEvent e) { diff --git a/platform/platform-impl/src/com/intellij/idea/StartupUtil.java b/platform/platform-impl/src/com/intellij/idea/StartupUtil.java index 13cad7632d7f..038952daa54e 100644 --- a/platform/platform-impl/src/com/intellij/idea/StartupUtil.java +++ b/platform/platform-impl/src/com/intellij/idea/StartupUtil.java @@ -34,7 +34,6 @@ import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.ShutDownTracker; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.SystemInfoRt; -import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.io.win32.IdeaWin32; import com.intellij.openapi.util.text.StringUtil; @@ -52,6 +51,7 @@ import com.sun.jna.Native; import org.apache.log4j.ConsoleAppender; import org.apache.log4j.Level; import org.apache.log4j.PatternLayout; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.io.BuiltInServer; @@ -421,24 +421,15 @@ public class StartupUtil { } /** - * @param alternativeHTML Updated version of Privacy Policy text if any. + * @param htmlText Updated version of Privacy Policy text if any. * If it's null the standard text from bundled resources would be used. */ - public static void showPrivacyPolicyAgreement(@Nullable String alternativeHTML) { + public static void showPrivacyPolicyAgreement(@NotNull String htmlText) { DialogWrapper dialog = new DialogWrapper(true) { @Nullable @Override protected JComponent createCenterPanel() { - JPanel centerPanel = new JPanel(new BorderLayout(5, 5)); - String html = alternativeHTML; - if (html == null) { - try { - html = FileUtil.loadTextAndClose(StartupUtil.class.getResource("/PrivacyPolicy.html").openStream()); - } - catch (IOException e) { - //ignore - } - } + JPanel centerPanel = new JPanel(new BorderLayout(JBUI.scale(5), JBUI.scale(5))); JEditorPane viewer = SwingHelper.createHtmlViewer(true, null, JBColor.WHITE, JBColor.BLACK); viewer.setFocusable(true); viewer.addHyperlinkListener(new HyperlinkAdapter() { @@ -447,12 +438,13 @@ public class StartupUtil { URL url = e.getURL(); if (url != null) { BrowserUtil.browse(url); - } else { + } + else { SwingHelper.scrollToReference(viewer, e.getDescription()); } } }); - viewer.setText(html); + viewer.setText(htmlText); viewer.setCaretPosition(0); viewer.setBorder(JBUI.Borders.empty(5)); centerPanel.add(new JLabel("Please read and accept these terms and conditions:"), BorderLayout.NORTH); @@ -485,8 +477,7 @@ public class StartupUtil { }; dialog.setModal(true); dialog.setTitle(ApplicationNamesInfo.getInstance().getFullProductName() + " Privacy Policy Agreement"); - dialog.setResizable(false); - dialog.setSize(509, 395); + dialog.setSize(JBUI.scale(509), JBUI.scale(395)); dialog.show(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/SingleConfigurableEditor.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/SingleConfigurableEditor.java index 4987e9164122..d1755b1948a9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/SingleConfigurableEditor.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/SingleConfigurableEditor.java @@ -19,6 +19,7 @@ import com.intellij.CommonBundle; import com.intellij.ide.actions.ShowSettingsUtilImpl; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.TransactionGuard; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.help.HelpManager; import com.intellij.openapi.options.BaseConfigurable; @@ -288,7 +289,7 @@ public class SingleConfigurableEditor extends DialogWrapper { myConfigurable = null; if (mySaveAllOnClose) { - ApplicationManager.getApplication().saveAll(); + TransactionGuard.submitTransaction(ApplicationManager.getApplication()::saveAll); } } } 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 2d41df1698e5..b01e269a473e 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 @@ -18,6 +18,7 @@ package com.intellij.openapi.options.newEditor; import com.intellij.CommonBundle; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.TransactionGuard; import com.intellij.openapi.help.HelpManager; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurableGroup; @@ -156,7 +157,7 @@ public class SettingsDialog extends DialogWrapper implements DataProvider { @Override public void doOKAction() { if (myEditor.apply()) { - ApplicationManager.getApplication().saveAll(); + TransactionGuard.submitTransaction(ApplicationManager.getApplication()::saveAll); SettingsDialog.super.doOKAction(); } } diff --git a/platform/platform-impl/src/com/intellij/remote/VagrantBasedCredentialsHolder.java b/platform/platform-impl/src/com/intellij/remote/VagrantBasedCredentialsHolder.java index fd05645a94d0..1eabdc20b34c 100644 --- a/platform/platform-impl/src/com/intellij/remote/VagrantBasedCredentialsHolder.java +++ b/platform/platform-impl/src/com/intellij/remote/VagrantBasedCredentialsHolder.java @@ -27,10 +27,12 @@ public class VagrantBasedCredentialsHolder { private static final String VAGRANT_FOLDER = "VAGRANT_FOLDER"; private static final String MACHINE_NAME = "MACHINE_NAME"; + @NotNull private String myVagrantFolder; private String myMachineName; public VagrantBasedCredentialsHolder() { + myVagrantFolder = ""; } @Deprecated diff --git a/platform/platform-resources/src/PrivacyPolicy.html b/platform/platform-resources/src/PrivacyPolicy-1.0.html similarity index 100% rename from platform/platform-resources/src/PrivacyPolicy.html rename to platform/platform-resources/src/PrivacyPolicy-1.0.html diff --git a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralReplaceTest.java b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralReplaceTest.java index 8f71f339bad6..002045385084 100644 --- a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralReplaceTest.java +++ b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralReplaceTest.java @@ -584,8 +584,8 @@ public class StructuralReplaceTest extends StructuralReplaceTestCase { public void testReplaceParameter() { String s1 = "class A { void b(int c, int d, int e) {} }"; - String s2 = "int d"; - String s3 = "int d2"; + String s2 = "int d;"; + String s3 = "int d2;"; String expectedResult = "class A { void b(int c, int d2, int e) {} }"; actualResult = replacer.testReplace(s1,s2,s3,options); @@ -2194,12 +2194,12 @@ public class StructuralReplaceTest extends StructuralReplaceTestCase { " final int x = 5;\n" + " }\n" + "}"; - String s2 = "final '_type 'var = '_init?"; - String s3 = "$type$ $var$ = $init$"; + String s2 = "final '_type 'var = '_init?;"; + String s3 = "$type$ $var$ = $init$;"; String expected = "class Foo {\n" + " void foo(int i, int i2, int i3) {\n" + - " int x = 5\n" + + " int x = 5;\n" + " }\n" + "}"; diff --git a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java index f331b8555f52..74c99ef65de0 100644 --- a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java +++ b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java @@ -680,9 +680,9 @@ public class StructuralSearchTest extends StructuralSearchTestCase { " void main(String argv[]);" + " void main(String argv);" + "}"; - String s12 = "'_t:[regex( *Object\\[\\] ) ] '_t2"; - String s12_2 = "'_t:[regex( *Object ) ] '_t2 []"; - String s12_3 = "'_t:[regex( *Object ) ] '_t2"; + String s12 = "'_t:[regex( *Object\\[\\] ) ] '_t2;"; + String s12_2 = "'_t:[regex( *Object ) ] '_t2 [];"; + String s12_3 = "'_t:[regex( *Object ) ] '_t2;"; assertEquals( "Find array covariant types", @@ -725,10 +725,10 @@ public class StructuralSearchTest extends StructuralSearchTestCase { " void y(String... ss) {}" + " void y(boolean b) {}" + "}"; - assertEquals("find ellipsis type 1", 1, findMatchesCount(source2, "String[] '_a")); - assertEquals("find ellipsis type 2", 1, findMatchesCount(source2, "int[] '_a")); + assertEquals("find ellipsis type 1", 1, findMatchesCount(source2, "String[] '_a;")); + assertEquals("find ellipsis type 2", 1, findMatchesCount(source2, "int[] '_a;")); assertEquals("find ellipsis type 3", 1, findMatchesCount(source2, "class '_X { void '_m(int... '_a); }")); - assertEquals("find ellipsis type 4", 2, findMatchesCount(source2, "'_T[] '_a")); + assertEquals("find ellipsis type 4", 2, findMatchesCount(source2, "'_T[] '_a;")); String source3 = "class A {" + " private int[] is;" + @@ -1200,7 +1200,7 @@ public class StructuralSearchTest extends StructuralSearchTestCase { "class '_a {" + " '_b '_c = '_d;" + " '_e '_f() {" + - " '_g '_h = '_i" + + " '_g '_h = '_i;" + " return '_j;" + " }" + "}")); @@ -1621,7 +1621,13 @@ public class StructuralSearchTest extends StructuralSearchTestCase { // typed var with instanceof assertEquals("typed instanceof",findMatchesCount(s65,s66),1); - assertEquals("don't throw exception on incomplete instanceof expression", findMatchesCount(s65, "'_T instanceof"), 2); + try { + // warn on incomplete instanceof + findMatchesCount(s65, "'_T instanceof"); + fail(); + } catch (MalformedPatternException e) { + assertEquals("Type expected", e.getMessage()); + } // typed vars with arrays assertEquals("typed pattern with array",findMatchesCount(s23,s24_1),2); @@ -3054,22 +3060,22 @@ public class StructuralSearchTest extends StructuralSearchTestCase { "}"; String pattern1 = "class '_A {" + - " '_type+ 'method+ () throws '_E{0,0}" + + " '_type+ 'method+ () throws '_E{0,0};" + "}"; assertEquals(1, findMatchesCount(source, pattern1)); String pattern2 = "class '_A {" + - " '_type+ 'method+ () throws '_E{1,2}" + + " '_type+ 'method+ () throws '_E{1,2};" + "}"; assertEquals(2, findMatchesCount(source, pattern2)); String pattern3 = "class '_A {" + - " '_type+ 'method+ () throws '_E{2,2}" + + " '_type+ 'method+ () throws '_E{2,2};" + "}"; assertEquals(1, findMatchesCount(source, pattern3)); String pattern4 = "class '_A {" + - " '_type+ 'method+ () throws '_E{0,0}:[ regex( E2 )]" + + " '_type+ 'method+ () throws '_E{0,0}:[ regex( E2 )];" + "}"; assertEquals(2, findMatchesCount(source, pattern4)); } @@ -3146,8 +3152,8 @@ public class StructuralSearchTest extends StructuralSearchTestCase { " }" + "}"; - String pattern1 = "() ->"; - assertEquals("should find lamdas", 4, findMatchesCount(source, pattern1)); + String pattern1 = "() -> {}"; + assertEquals("should find lambdas", 4, findMatchesCount(source, pattern1)); String pattern2 = "(int '_a) -> {}"; assertEquals("should find lambdas with specific parameter type", 1, findMatchesCount(source, pattern2)); @@ -3330,7 +3336,7 @@ public class StructuralSearchTest extends StructuralSearchTestCase { " } finally {}\n" + "}}"; - String pattern1 = "try ('_ResourceType '_Var) { '_Statement*; }"; + String pattern1 = "try ('_ResourceType '_Var = '_exp) { '_Statement*; }"; assertEquals("Find try-with-resources", 1, findMatchesCount(source, pattern1)); String pattern2 = "try { '_St1*; } catch ('_ExceptionType1 '_e1) { '_St2*; } catch ('_ExceptionType2 '_e2) { '_St3*; }"; diff --git a/platform/util/src/com/intellij/openapi/util/Version.java b/platform/util/src/com/intellij/openapi/util/Version.java index 674ab41e32d8..f09ca80edae1 100644 --- a/platform/util/src/com/intellij/openapi/util/Version.java +++ b/platform/util/src/com/intellij/openapi/util/Version.java @@ -4,9 +4,6 @@ import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - public class Version { public final int major; public final int minor; @@ -22,23 +19,27 @@ public class Version { public static Version parseVersion(@NotNull String versionString) { String[] versions = versionString.split("\\."); String version = versions[0]; - int major = StringUtil.parseInt(version, -1); + int major = parseNumber(version, -1); if (major < 0) { return null; } - int minor = (versions.length > 1) ? StringUtil.parseInt(versions[1], -1) : 0; + int minor = (versions.length > 1) ? parseNumber(versions[1], -1) : 0; if (minor < 0) { - return null; + return new Version(major, 0, 0); } - int patch = (versions.length > 2) ? StringUtil.parseInt(versions[2], -1) : 0; + int patch = (versions.length > 2) ? parseNumber(versions[2], -1) : 0; if (patch < 0) { - return null; + return new Version(major, minor, 0); } return new Version(major, minor, patch); } + + private static int parseNumber(String num, int def) { + return StringUtil.parseInt(num.replaceFirst("(\\d+).*", "$1"), def); + } public boolean is(@Nullable Integer major) { return is(major, null); diff --git a/platform/util/src/com/intellij/util/io/FileAccessorCache.java b/platform/util/src/com/intellij/util/io/FileAccessorCache.java index 180b8cf5649d..cfb040a9bc96 100644 --- a/platform/util/src/com/intellij/util/io/FileAccessorCache.java +++ b/platform/util/src/com/intellij/util/io/FileAccessorCache.java @@ -18,7 +18,6 @@ package com.intellij.util.io; import com.intellij.util.containers.SLRUCache; import org.jetbrains.annotations.NotNull; -import java.io.Closeable; import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; @@ -46,15 +45,7 @@ public abstract class FileAccessorCache implements com.intellij.util.conta } protected abstract T createAccessor(K key) throws IOException; - protected abstract void disposeAccessor(T fileAccessor); - - protected void disposeCloseable(Closeable fileAccessor) { - try { - fileAccessor.close(); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - } + protected abstract void disposeAccessor(T fileAccessor) throws IOException; @NotNull public final Handle get(K key) { @@ -113,7 +104,11 @@ public abstract class FileAccessorCache implements com.intellij.util.conta public final void release() { if (myRefCount.decrementAndGet() == 0) { - myOwner.disposeAccessor(myFileAccessor); + try { + myOwner.disposeAccessor(myFileAccessor); + } catch (IOException ex) { + throw new RuntimeException(ex); + } } } diff --git a/platform/util/src/com/intellij/util/io/PersistentHashMapValueStorage.java b/platform/util/src/com/intellij/util/io/PersistentHashMapValueStorage.java index ac10e424db01..dbe734157d99 100644 --- a/platform/util/src/com/intellij/util/io/PersistentHashMapValueStorage.java +++ b/platform/util/src/com/intellij/util/io/PersistentHashMapValueStorage.java @@ -60,8 +60,8 @@ public class PersistentHashMapValueStorage { } @Override - protected void disposeAccessor(RandomAccessFileWithLengthAndSizeTracking fileAccessor) { - disposeCloseable(fileAccessor); + protected void disposeAccessor(RandomAccessFileWithLengthAndSizeTracking fileAccessor) throws IOException { + fileAccessor.close(); } }; @@ -75,8 +75,8 @@ public class PersistentHashMapValueStorage { } @Override - protected void disposeAccessor(DataOutputStream fileAccessor) { - disposeCloseable(fileAccessor); + protected void disposeAccessor(DataOutputStream fileAccessor) throws IOException { + fileAccessor.close(); } }; diff --git a/platform/util/testSrc/com/intellij/util/VersionTest.java b/platform/util/testSrc/com/intellij/util/VersionTest.java new file mode 100644 index 000000000000..bfd8409d6a3c --- /dev/null +++ b/platform/util/testSrc/com/intellij/util/VersionTest.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2016 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.util; + +import com.intellij.openapi.util.Version; +import junit.framework.TestCase; + +public class VersionTest extends TestCase { + public void testParseVersion() throws Exception { + assertEquals(new Version(1, 0, 0), Version.parseVersion("1")); + assertEquals(new Version(1, 2, 0), Version.parseVersion("1.2")); + assertEquals(new Version(1, 2, 3), Version.parseVersion("1.2.3")); + assertEquals(new Version(1, 2, 3), Version.parseVersion("1.2.3.4")); + + assertEquals(new Version(1, 0, 0), Version.parseVersion("1beta")); + assertEquals(new Version(1, 2, 0), Version.parseVersion("1.2beta")); + assertEquals(new Version(1, 2, 3), Version.parseVersion("1.2.3beta")); + assertEquals(new Version(1, 2, 3), Version.parseVersion("1.2.3.4beta")); + + assertEquals(new Version(1, 0, 0), Version.parseVersion("1-beta")); + assertEquals(new Version(1, 2, 0), Version.parseVersion("1.2-beta")); + assertEquals(new Version(1, 2, 3), Version.parseVersion("1.2.3-beta")); + assertEquals(new Version(1, 2, 3), Version.parseVersion("1.2.3.4-beta")); + + assertEquals(new Version(1, 0, 0), Version.parseVersion("1.beta")); + assertEquals(new Version(1, 2, 0), Version.parseVersion("1.2.beta")); + assertEquals(new Version(1, 2, 3), Version.parseVersion("1.2.3.beta")); + + assertEquals(null, Version.parseVersion("")); + assertEquals(null, Version.parseVersion("beta1")); + assertEquals(null, Version.parseVersion("beta.beta.beta")); + } +} \ No newline at end of file diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AbstractCommonCheckinAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AbstractCommonCheckinAction.java index a99268db1e66..bd59b40c2b80 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AbstractCommonCheckinAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AbstractCommonCheckinAction.java @@ -17,8 +17,7 @@ package com.intellij.openapi.vcs.actions; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.Presentation; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.*; @@ -80,7 +79,7 @@ public abstract class AbstractCommonCheckinAction extends AbstractVcsAction { @NotNull protected FilePath[] prepareRootsForCommit(@NotNull FilePath[] roots, @NotNull Project project) { if (ApplicationManager.getApplication().isDispatchThread()) { - ApplicationManager.getApplication().saveAll(); + TransactionGuard.submitTransaction(ApplicationManager.getApplication()::saveAll); } return filterDescindingFiles(roots, project); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/MainFrame.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/MainFrame.java index 08a80e76af9f..bcbf96180059 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/MainFrame.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/MainFrame.java @@ -293,7 +293,9 @@ public class MainFrame extends JPanel implements DataProvider, Disposable { }), VcsRevisionNumber.class); } else if (VcsDataKeys.VCS.is(dataId)) { - Set roots = ContainerUtil.map2Set(Ints.asList(myGraphTable.getSelectedRows()), new Function() { + int[] selectedRows = myGraphTable.getSelectedRows(); + if (selectedRows.length == 0 || selectedRows.length > VcsLogUtil.MAX_SELECTED_COMMITS) return null; + Set roots = ContainerUtil.map2Set(Ints.asList(selectedRows), new Function() { @Override public VirtualFile fun(@NotNull Integer row) { return myGraphTable.getModel().getRoot(row); diff --git a/plugins/devkit/resources/META-INF/plugin.xml b/plugins/devkit/resources/META-INF/plugin.xml index cce061ba9670..ad836dc17357 100644 --- a/plugins/devkit/resources/META-INF/plugin.xml +++ b/plugins/devkit/resources/META-INF/plugin.xml @@ -147,7 +147,7 @@ groupKey="inspections.group.name" enabledByDefault="false" isInternal="true" level="ERROR" implementationClass="org.jetbrains.idea.devkit.inspections.internal.HighlightVisitorInternalInspection"/> - remoteBranches = readState().getRemoteBranches().keySet(); - assertTrue("Remote branch not found", ContainerUtil.exists(remoteBranches, new Condition() { - @Override - public boolean value(GitRemoteBranch branch) { - return branch.getNameForLocalOperations().equals(INVALID_REMOTE + "/" + INVALID_REMOTE_BRANCH); - } - })); - } - - // inspired by IDEA-134286 - public void test_detached_HEAD() throws IOException { - String head = getToDetachedHead(); - GitBranchState state = readState(); - assertEquals("Detached HEAD is not detected", GitRepository.State.DETACHED, state.getState()); - assertEquals("Detached HEAD hash is incorrect", head, state.getCurrentRevision()); - } - - // inspired by IDEA-135966 - public void test_no_local_branches() throws IOException { - String head = getToDetachedHead(); - git("branch -D master"); - GitBranchState state = readState(); - assertEquals("Detached HEAD is not detected", GitRepository.State.DETACHED, state.getState()); - assertEquals("Detached HEAD hash is incorrect", head, state.getCurrentRevision()); - assertTrue("There should be no local branches", state.getLocalBranches().isEmpty()); - } - - public void test_tracking_remote_with_complex_name() throws IOException { - makeCommit("file.txt"); - git("remote add my/remote http://my.remote.git"); - git("update-ref refs/remotes/my/remote/master HEAD"); - git("config branch.master.remote my/remote"); - git("config branch.master.merge refs/heads/master"); - myRepo.update(); - - - GitBranchTrackInfo trackInfo = GitBranchUtil.getTrackInfoForBranch(myRepo, ObjectUtils.assertNotNull(myRepo.getCurrentBranch())); - assertNotNull(trackInfo); - GitRemote remote = trackInfo.getRemote(); - assertEquals("my/remote", remote.getName()); - assertEquals("http://my.remote.git", remote.getFirstUrl()); - } - - @NotNull - private static String getToDetachedHead() throws IOException { - makeCommit("file.txt"); - makeCommit("file.txt"); - git("checkout HEAD^"); - return last(); - } - - @NotNull - private GitBranchState readState() { - GitRepositoryFiles gitFiles = myRepo.getRepositoryFiles(); - GitConfig config = GitConfig.read(myPlatformFacade, gitFiles.getConfigFile()); - GitRepositoryReader reader = new GitRepositoryReader(gitFiles); - Collection remotes = config.parseRemotes(); - return reader.readState(remotes); - } - - // inspired by IDEA-134412 - public void test_fresh_repository_is_on_branch() { - GitLocalBranch currentBranch = readState().getCurrentBranch(); - assertNotNull("Current branch shouldn't be null in a fresh repository", currentBranch); - assertEquals("Fresh repository should be on master", "master", currentBranch.getName()); - } - - @Override - protected boolean makeInitialCommit() { - return false; - } -} diff --git a/plugins/git4idea/tests/git4idea/repo/GitRepositoryReaderNewTest.kt b/plugins/git4idea/tests/git4idea/repo/GitRepositoryReaderNewTest.kt new file mode 100644 index 000000000000..76116e513335 --- /dev/null +++ b/plugins/git4idea/tests/git4idea/repo/GitRepositoryReaderNewTest.kt @@ -0,0 +1,128 @@ +/* + * Copyright 2000-2012 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 git4idea.repo + +import com.intellij.dvcs.repo.Repository.State +import git4idea.branch.GitBranchUtil +import git4idea.test.GitExecutor.git +import git4idea.test.GitExecutor.last +import git4idea.test.GitScenarios.commit +import git4idea.test.GitScenarios.conflict +import git4idea.test.GitSingleRepoTest +import git4idea.test.GitTestUtil.makeCommit + +/** + * [GitRepositoryReaderTest] reads information from the pre-created .git directory from a real project. + * This one, on the other hand, operates on a live Git repository, putting it to various situations and checking the results. + */ +class GitRepositoryReaderNewTest : GitSingleRepoTest() { + + override fun makeInitialCommit(): Boolean { + return false + } + + // IDEA-152632 + fun `test current branch is known during rebase`() { + makeCommit("file.txt") + conflict(myRepo, "feature") + git("checkout feature") + git("rebase master", true) + + val state = readState() + assertEquals("State value is incorrect", State.REBASING, state.state) + val currentBranch = state.currentBranch + assertNotNull("Current branch should be known during rebase", currentBranch) + assertEquals("Current branch is incorrect", "feature", currentBranch!!.name) + } + + fun `test rebase with conflicts while being on detached HEAD`() { + makeCommit("file.txt") + conflict(myRepo, "feature") + commit(myRepo) + commit(myRepo) + git("checkout HEAD^") + git("rebase feature", true) + + val state = readState() + assertNull("Current branch can't be identified for this case", state.currentBranch) + assertEquals("State value is incorrect", State.REBASING, state.state) + } + + // IDEA-124052 + fun `test remote reference without remote`() { + makeCommit("file.txt") + val INVALID_REMOTE = "invalid-remote" + val INVALID_REMOTE_BRANCH = "master" + git("update-ref refs/remotes/$INVALID_REMOTE/$INVALID_REMOTE_BRANCH HEAD") + + val remoteBranches = readState().remoteBranches.keys + assertTrue("Remote branch not found", remoteBranches.any { it.nameForLocalOperations == "$INVALID_REMOTE/$INVALID_REMOTE_BRANCH" }) + } + + // IDEA-134286 + fun `test detached HEAD`() { + val head = moveToDetachedHead() + val state = readState() + assertEquals("Detached HEAD is not detected", State.DETACHED, state.state) + assertEquals("Detached HEAD hash is incorrect", head, state.currentRevision) + } + + // IDEA-135966 + fun `test no local branches`() { + val head = moveToDetachedHead() + git("branch -D master") + val state = readState() + assertEquals("Detached HEAD is not detected", State.DETACHED, state.state) + assertEquals("Detached HEAD hash is incorrect", head, state.currentRevision) + assertTrue("There should be no local branches", state.localBranches.isEmpty()) + } + + fun `test tracking remote with complex name`() { + makeCommit("file.txt") + git("remote add my/remote http://my.remote.git") + git("update-ref refs/remotes/my/remote/master HEAD") + git("config branch.master.remote my/remote") + git("config branch.master.merge refs/heads/master") + myRepo.update() + + val trackInfo = GitBranchUtil.getTrackInfoForBranch(myRepo, myRepo.currentBranch!!)!! + val remote = trackInfo.remote + assertEquals("my/remote", remote.name) + assertEquals("http://my.remote.git", remote.firstUrl) + } + + // IDEA-134412 + fun `test fresh repository is on branch`() { + val currentBranch = readState().currentBranch + assertNotNull("Current branch shouldn't be null in a fresh repository", currentBranch) + assertEquals("Fresh repository should be on master", "master", currentBranch!!.name) + } + + private fun moveToDetachedHead(): String { + makeCommit("file.txt") + makeCommit("file.txt") + git("checkout HEAD^") + return last() + } + + private fun readState(): GitBranchState { + val gitFiles = myRepo.repositoryFiles + val config = GitConfig.read(myPlatformFacade, gitFiles.configFile) + val reader = GitRepositoryReader(gitFiles) + val remotes = config.parseRemotes() + return reader.readState(remotes) + } +}