From 1070fd9c9bb84e12bc08ed1235071741ca68fedc Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Mon, 14 Nov 2016 12:21:32 +0100 Subject: [PATCH 01/33] presort descriptions per file (IDEA-163879) --- .../actions/CleanupInspectionIntention.java | 40 ++++++++++++------- .../ex/GlobalInspectionContextImpl.java | 3 +- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInspection/actions/CleanupInspectionIntention.java b/platform/lang-impl/src/com/intellij/codeInspection/actions/CleanupInspectionIntention.java index f3d465ab3032..2143d6c3f5f1 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/actions/CleanupInspectionIntention.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/actions/CleanupInspectionIntention.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * 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. @@ -95,6 +95,30 @@ public class CleanupInspectionIntention implements IntentionAction, HighPriority @NotNull String presentationText, @NotNull List descriptions, @Nullable Class quickfixClass) { + sortDescriptions(descriptions); + return applyFixesNoSort(project, presentationText, descriptions, quickfixClass); + } + + public static AbstractPerformFixesTask applyFixesNoSort(@NotNull Project project, + @NotNull String presentationText, + @NotNull List descriptions, + @Nullable Class quickfixClass) { + final SequentialModalProgressTask progressTask = + new SequentialModalProgressTask(project, presentationText, true); + final boolean isBatch = quickfixClass != null && BatchQuickFix.class.isAssignableFrom(quickfixClass); + final AbstractPerformFixesTask fixesTask = isBatch ? + new PerformBatchFixesTask(project, descriptions.toArray(ProblemDescriptor.EMPTY_ARRAY), progressTask, quickfixClass) : + new PerformFixesTask(project, descriptions.toArray(ProblemDescriptor.EMPTY_ARRAY), progressTask, quickfixClass); + CommandProcessor.getInstance().executeCommand(project, () -> { + CommandProcessor.getInstance().markCurrentCommandAsGlobal(project); + progressTask.setMinIterationTime(200); + progressTask.setTask(fixesTask); + ProgressManager.getInstance().run(progressTask); + }, presentationText, null); + return fixesTask; + } + + public static void sortDescriptions(@NotNull List descriptions) { Collections.sort(descriptions, (o1, o2) -> { final ProblemDescriptorBase d1 = (ProblemDescriptorBase)o1; final ProblemDescriptorBase d2 = (ProblemDescriptorBase)o2; @@ -104,20 +128,6 @@ public class CleanupInspectionIntention implements IntentionAction, HighPriority } return -elementsDiff; }); - - final SequentialModalProgressTask progressTask = - new SequentialModalProgressTask(project, presentationText, true); - final boolean isBatch = quickfixClass != null && BatchQuickFix.class.isAssignableFrom(quickfixClass); - final AbstractPerformFixesTask fixesTask = isBatch ? - new PerformBatchFixesTask(project, descriptions.toArray(ProblemDescriptor.EMPTY_ARRAY), progressTask, quickfixClass) : - new PerformFixesTask(project, descriptions.toArray(ProblemDescriptor.EMPTY_ARRAY), progressTask, quickfixClass); - CommandProcessor.getInstance().executeCommand(project, () -> { - CommandProcessor.getInstance().markCurrentCommandAsGlobal(project); - progressTask.setMinIterationTime(200); - progressTask.setTask(fixesTask); - ProgressManager.getInstance().run(progressTask); - }, presentationText, null); - return fixesTask; } @Override diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java index ba7942d4d695..d5c8f9ee95fb 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java @@ -938,6 +938,7 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp } } if (!localDescriptors.isEmpty()) { + CleanupInspectionIntention.sortDescriptions(localDescriptors); descriptors.addAll(localDescriptors); files.add(file); } @@ -962,7 +963,7 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp } Runnable runnable = () -> { if (!FileModificationService.getInstance().preparePsiElementsForWrite(files)) return; - CleanupInspectionIntention.applyFixes(getProject(), "Code Cleanup", descriptors, null); + CleanupInspectionIntention.applyFixesNoSort(getProject(), "Code Cleanup", descriptors, null); }; TransactionGuard.submitTransaction(getProject(), runnable); } From a4408fd41b8953e22ca985581198d0bd1ea9e4ae Mon Sep 17 00:00:00 2001 From: irengrig Date: Mon, 14 Nov 2016 11:11:12 +0100 Subject: [PATCH 02/33] catch process canceled exception of pattern match calculation - fix review comment for WEB-22649 JSON Schema: patternProperties does not work. --- .../jetbrains/jsonSchema/impl/JsonSchemaObject.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java index b5ef3b1e50b2..5b6b128783af 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java @@ -1,5 +1,6 @@ package com.jetbrains.jsonSchema.impl; +import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.containers.SLRUMap; import org.jetbrains.annotations.NotNull; @@ -509,10 +510,14 @@ public class JsonSchemaObject { final List strings = new ArrayList<>(myPatternProperties.keySet()); Collections.sort(strings); for (final String pattern : strings) { - final boolean matches = Pattern.compile(adaptSchemaPattern(pattern)).matcher(StringUtil.newBombedCharSequence(name, 1000)).matches(); - if (matches) { - myCachedPatternProperties.put(name, pattern); - return myPatternProperties.get(pattern); + try { + final boolean matches = Pattern.compile(adaptSchemaPattern(pattern)).matcher(StringUtil.newBombedCharSequence(name, 1000)).matches(); + if (matches) { + myCachedPatternProperties.put(name, pattern); + return myPatternProperties.get(pattern); + } + } catch (ProcessCanceledException e) { + //ignored } } myCachedPatternProperties.put(name, ""); From 4e3281f641893d20bc2901ae23bd01bcae5f550d Mon Sep 17 00:00:00 2001 From: irengrig Date: Mon, 14 Nov 2016 11:45:37 +0100 Subject: [PATCH 03/33] json schema: improve json files refresh after json schema editing - review comment for WEB-21418 JSON Schema: if file under the schema is placed in the split Editor tab update comes later than should be --- .../jsonSchema/JsonSchemaVfsListener.java | 44 ++++++++++--------- .../util/resources/misc/registry.properties | 2 - 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/json/src/com/jetbrains/jsonSchema/JsonSchemaVfsListener.java b/json/src/com/jetbrains/jsonSchema/JsonSchemaVfsListener.java index 66d51f6217d5..dab691f98614 100644 --- a/json/src/com/jetbrains/jsonSchema/JsonSchemaVfsListener.java +++ b/json/src/com/jetbrains/jsonSchema/JsonSchemaVfsListener.java @@ -18,16 +18,16 @@ package com.jetbrains.jsonSchema; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.json.JsonFileType; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.ZipperUpdater; -import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.impl.BulkVirtualFileListenerAdapter; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; -import com.intellij.psi.search.FileTypeIndex; -import com.intellij.psi.search.GlobalSearchScope; import com.intellij.util.Alarm; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.jsonSchema.impl.JsonSchemaServiceImpl; @@ -53,23 +53,29 @@ public class JsonSchemaVfsListener extends BulkVirtualFileListenerAdapter { if (scope.isEmpty()) return; final DaemonCodeAnalyzer analyzer = DaemonCodeAnalyzer.getInstance(project); - ApplicationManager.getApplication().runReadAction(() -> { - FileTypeIndex.processFiles(JsonFileType.INSTANCE, jsonFile -> { - final Collection> collection = myService.getSchemaFilesByFile(jsonFile); - if (collection != null && !collection.isEmpty()) { - for (Pair pair : collection) { - if (scope.contains(pair.getFirst())) { - final PsiFile psiFile = PsiManager.getInstance(project).findFile(jsonFile); - if (psiFile != null) { - analyzer.restart(psiFile); + final PsiManager psiManager = PsiManager.getInstance(project); + final Editor[] editors = EditorFactory.getInstance().getAllEditors(); + for (Editor editor : editors) { + if (editor instanceof EditorEx) { + final VirtualFile file = ((EditorEx)editor).getVirtualFile(); + if (JsonFileType.INSTANCE.equals(file.getFileType())) { + final Collection> collection = myService.getSchemaFilesByFile(file); + if (collection != null && !collection.isEmpty()) { + for (Pair pair : collection) { + if (scope.contains(pair.getFirst())) { + ApplicationManager.getApplication().runReadAction(() -> { + final PsiFile psiFile = psiManager.findFile(file); + if (psiFile != null) { + analyzer.restart(psiFile); + } + }); + break; } - return true; } } } - return true; - }, GlobalSearchScope.projectScope(project)); - }); + } + } }; @Override @@ -105,10 +111,8 @@ public class JsonSchemaVfsListener extends BulkVirtualFileListenerAdapter { private void onFileChange(@NotNull final VirtualFile schemaFile) { if (myMappingsProjectConfiguration.isRegisteredSchemaFile(schemaFile)) { myService.dropProviderFromCache(schemaFile); - if (Registry.is("json.schema.fast.annotation.update")) { - myDirtySchemas.add(schemaFile); - myUpdater.queue(myRunnable); - } + myDirtySchemas.add(schemaFile); + myUpdater.queue(myRunnable); } } }); diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 226cec12b9e1..e18b56e2c4a9 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -913,5 +913,3 @@ build.manager.async.fs.events.description=Experimental: TranslatingCompilerFiles batch.inspections.use.psi.as.ref.table.key=false batch.inspections.use.psi.as.ref.table.key.description=Use PsiElement as key for RefManager's table -json.schema.fast.annotation.update=false -json.schema.fast.annotation.update.description=Restart highlighting for all json files matching json schema on json schema fiel change \ No newline at end of file From 82284d476199f0998b4c6b47486da2bdcd5e316f Mon Sep 17 00:00:00 2001 From: irengrig Date: Mon, 14 Nov 2016 12:33:10 +0100 Subject: [PATCH 04/33] json schema: pattern properties: better caching - fix review comment for WEB-22649 JSON Schema: patternProperties does not work. --- .../jsonSchema/impl/JsonSchemaObject.java | 109 ++++++++++++++---- 1 file changed, 87 insertions(+), 22 deletions(-) diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java index 5b6b128783af..51424d00baff 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java @@ -1,12 +1,14 @@ package com.jetbrains.jsonSchema.impl; import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.containers.SLRUMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.regex.Pattern; /** @@ -17,7 +19,7 @@ public class JsonSchemaObject { private Map myDefinitions; private Map myProperties; private Map myPatternProperties; - private final SLRUMap myCachedPatternProperties = new SLRUMap<>(100, 100); + private final PatternCalculator myPatternCalculator = new PatternCalculator(); private String myId; private String mySchema; @@ -81,7 +83,7 @@ public class JsonSchemaObject { myProperties = other.myProperties; myDefinitions = other.myDefinitions; myPatternProperties = other.myPatternProperties; - myCachedPatternProperties.clear(); + myPatternCalculator.clear(); myType = other.myType; myDefault = other.myDefault; @@ -124,7 +126,7 @@ public class JsonSchemaObject { myProperties.putAll(other.myProperties); myDefinitions = copyMap(myDefinitions, other.myDefinitions); myPatternProperties = copyMap(myPatternProperties, other.myPatternProperties); - myCachedPatternProperties.clear(); + myPatternCalculator.clear(); if (!StringUtil.isEmptyOrSpaces(other.myDescription)) { myDescription = other.myDescription; } @@ -199,7 +201,7 @@ public class JsonSchemaObject { public void setPatternProperties(Map patternProperties) { myPatternProperties = patternProperties; - myCachedPatternProperties.clear(); + myPatternCalculator.clear(); } public JsonSchemaType getType() { @@ -504,24 +506,7 @@ public class JsonSchemaObject { @Nullable public JsonSchemaObject getMatchingPatternPropertySchema(@NotNull String name) { - if (myPatternProperties == null || myPatternProperties.isEmpty()) return null; - final String s = myCachedPatternProperties.get(name); - if (s != null) return myPatternProperties.get(s); - final List strings = new ArrayList<>(myPatternProperties.keySet()); - Collections.sort(strings); - for (final String pattern : strings) { - try { - final boolean matches = Pattern.compile(adaptSchemaPattern(pattern)).matcher(StringUtil.newBombedCharSequence(name, 1000)).matches(); - if (matches) { - myCachedPatternProperties.put(name, pattern); - return myPatternProperties.get(pattern); - } - } catch (ProcessCanceledException e) { - //ignored - } - } - myCachedPatternProperties.put(name, ""); - return null; + return myPatternCalculator.getMatchingPatternPropertySchema(myPatternProperties, name); } @NotNull @@ -530,4 +515,84 @@ public class JsonSchemaObject { pattern = pattern.endsWith("+") || pattern.endsWith("*") ? pattern : (pattern + ".*"); return pattern; } + + private static class PatternCalculator { + private final ReentrantReadWriteLock myLock = new ReentrantReadWriteLock(); + private Map myCachedPatterns; + private SLRUMap myCachedPatternProperties; + + @Nullable + public JsonSchemaObject getMatchingPatternPropertySchema(@Nullable final Map patternProperties, + @NotNull final String name) { + if (patternProperties == null || patternProperties.isEmpty()) return null; + myLock.readLock().lock(); + try { + if (myCachedPatterns == null) { + initPatternCache(patternProperties); + } + assert myCachedPatternProperties != null; + final String s = myCachedPatternProperties.get(name); + if (s != null) return patternProperties.get(s); + return matchPatternsToString(name, patternProperties); + } finally { + myLock.readLock().unlock(); + } + } + + public void clear() { + myLock.writeLock().lock(); + try { + myCachedPatterns = null; + myCachedPatternProperties = null; + } finally { + myLock.writeLock().unlock(); + } + } + + private JsonSchemaObject matchPatternsToString(@NotNull final String name, @NotNull final Map patternProperties) { + final List strings = new ArrayList<>(patternProperties.keySet()); + Collections.sort(strings); + + return underWrite(() -> { + for (final String pattern : strings) { + try { + final Pattern compiledPattern = myCachedPatterns.get(pattern); + assert compiledPattern != null; + final boolean matches = compiledPattern.matcher(StringUtil.newBombedCharSequence(name, 300)).matches(); + if (matches) { + myCachedPatternProperties.put(name, pattern); + return patternProperties.get(pattern); + } + } catch (ProcessCanceledException e) { + //ignored + } + } + myCachedPatternProperties.put(name, ""); + return null; + }); + } + + private T underWrite(@NotNull final Computable computable) { + myLock.readLock().unlock(); + myLock.writeLock().lock(); + try { + final T t = computable.compute(); + myLock.readLock().lock(); + return t; + } finally { + myLock.writeLock().unlock(); + } + } + + private void initPatternCache(@NotNull final Map patternProperties) { + underWrite(() -> { + myCachedPatterns = new HashMap<>(patternProperties.size(), 1.0f); + myCachedPatternProperties = new SLRUMap<>(100, 100); + for (String pattern : patternProperties.keySet()) { + myCachedPatterns.put(pattern, Pattern.compile(adaptSchemaPattern(pattern))); + } + return true; + }); + } + } } From 4e218579d7e7982905f1fae2e8bee94e5bc631cd Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Mon, 14 Nov 2016 12:38:31 +0100 Subject: [PATCH 05/33] review feedback --- .../com/intellij/index/IndexTest.groovy | 49 ++++++++++--------- .../util/indexing/FileBasedIndexImpl.java | 7 +-- .../util/indexing/VfsEventsMerger.java | 23 +++------ 3 files changed, 35 insertions(+), 44 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/index/IndexTest.groovy b/java/java-tests/testSrc/com/intellij/index/IndexTest.groovy index de3aac173074..5c01c01c3611 100644 --- a/java/java-tests/testSrc/com/intellij/index/IndexTest.groovy +++ b/java/java-tests/testSrc/com/intellij/index/IndexTest.groovy @@ -32,6 +32,8 @@ import com.intellij.openapi.util.Ref import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.vfs.* +import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent +import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent import com.intellij.openapi.vfs.newvfs.events.VFileEvent import com.intellij.openapi.vfs.newvfs.events.VFilePropertyChangeEvent import com.intellij.openapi.vfs.newvfs.impl.VirtualFileSystemEntry @@ -64,6 +66,7 @@ import com.intellij.util.FileContentUtil import com.intellij.util.Processor import com.intellij.util.indexing.* import com.intellij.util.io.* +import groovy.transform.CompileStatic import org.jetbrains.annotations.NotNull /** * @author Eugene Zhuravlev @@ -566,14 +569,14 @@ class IndexTest extends JavaCodeInsightFixtureTestCase { VfsUtilCore.visitChildrenRecursively(file, new VirtualFileVisitor() { @Override boolean visitFile(@NotNull VirtualFile visitedFile) { - iterator.processFile(visitedFile); - return true; + iterator.processFile(visitedFile) + return true } - }); + }) } protected void doInvalidateIndicesForFile(VirtualFile file, boolean contentChange) { - vfsEventMerger.recordBeforeFileEvent(((VirtualFileWithId)file).id, file, contentChange); + vfsEventMerger.recordBeforeFileEvent(((VirtualFileWithId)file).id, file, contentChange) } @Override @@ -586,12 +589,12 @@ class IndexTest extends JavaCodeInsightFixtureTestCase { vfsEventMerger.processChanges(new VfsEventsMerger.VfsEventProcessor() { @Override boolean process(VfsEventsMerger.ChangeInfo info) { - operation.set(info.toString()); + operation.set(info.toString()) return true } }) - StringUtil.replace(operation.get(), file.getPath(), file.getName()); + StringUtil.replace(operation.get(), file.getPath(), file.getName()) } } @@ -601,7 +604,7 @@ class IndexTest extends JavaCodeInsightFixtureTestCase { ApplicationManager.getApplication().getMessageBus().connect(getTestRootDisposable()).subscribe( VirtualFileManager.VFS_CHANGES, listener - ); + ) def fileName = "test.txt" final VirtualFile testFile = myFixture.addFileToProject(fileName, "test").getVirtualFile() @@ -614,17 +617,17 @@ class IndexTest extends JavaCodeInsightFixtureTestCase { assertEquals("file: $fileName\n" + "operation: REMOVE ADD", listener.indexingOperation(testFile)) - VfsUtil.saveText(testFile, "foo"); - VfsUtil.saveText(testFile, "bar"); + VfsUtil.saveText(testFile, "foo") + VfsUtil.saveText(testFile, "bar") assertEquals("file: $fileName\n" + - "operation: UPDATE-REMOVE UPDATE", listener.indexingOperation(testFile)); + "operation: UPDATE-REMOVE UPDATE", listener.indexingOperation(testFile)) VfsUtil.saveText(testFile, "baz") testFile.delete(null) assertEquals("file: $fileName\n" + - "operation: REMOVE", listener.indexingOperation(testFile)); + "operation: REMOVE", listener.indexingOperation(testFile)) } void "test files inside copied directory are indexed"() { @@ -643,35 +646,35 @@ class IndexTest extends JavaCodeInsightFixtureTestCase { assert JavaFileElementType.isInSourceContent(myFixture.tempDirFixture.getFile('another/doo/A.java')) } - + @CompileStatic void "test Vfs Events Processing Performance"() { def filename = 'A.java' myFixture.addFileToProject('foo/bar/' + filename, 'class A {}') PlatformTestUtil.startPerformanceTest("Vfs Event Processing By Index", 1000, { def files = FilenameIndex.getFilesByName(project, filename, GlobalSearchScope.moduleScope(myModule)) - assert files != null - assert files.length == 1 + assert files?.length == 1 VirtualFile file = files[0].virtualFile def filename2 = 'B.java' def max = 100000 - List eventList = new ArrayList<>(max); - def len = max / 2; + List eventList = new ArrayList<>(max) + def len = max / 2 for(int i = 0; i < len; ++i) { - eventList.add(new VFilePropertyChangeEvent(null, file, VirtualFile.PROP_NAME, filename, filename2, true)) ; - eventList.add(new VFilePropertyChangeEvent(null, file, VirtualFile.PROP_NAME, filename2, filename, true)) ; + eventList.add(new VFilePropertyChangeEvent(null, file, VirtualFile.PROP_NAME, filename, filename2, true)) + eventList.add(new VFilePropertyChangeEvent(null, file, VirtualFile.PROP_NAME, filename2, filename, true)) + eventList.add(new VFileDeleteEvent(null, file, true)) + eventList.add(new VFileCreateEvent(null, file.parent, filename, false, true)) } IndexedFilesListener indexedFilesListener = ((FileBasedIndexImpl)FileBasedIndex.instance).changedFilesCollector - indexedFilesListener.before(eventList); - indexedFilesListener.after(eventList); + indexedFilesListener.before(eventList) + indexedFilesListener.after(eventList) files = FilenameIndex.getFilesByName(project, filename, GlobalSearchScope.moduleScope(myModule)) - assert files != null - assert files.length == 1 - }).cpuBound().ioBound().assertTiming(); + assert files?.length == 1 + }).cpuBound().ioBound().assertTiming() } } 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 6c315d8cdf59..73dba87e302f 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java @@ -39,7 +39,6 @@ import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator; -import com.intellij.openapi.progress.util.ProgressIndicatorUtils; import com.intellij.openapi.project.*; import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.impl.PushedFilePropertiesUpdaterImpl; @@ -2003,11 +2002,7 @@ public class FileBasedIndexImpl extends FileBasedIndex { private void processFilesInReadActionWithYieldingToWriteAction() { try { while (myVfsEventsMerger.hasChanges()) { - boolean result = ProgressIndicatorUtils - .runInReadActionWithWriteActionPriority(this::processFilesInReadAction, null); - if (!result) { - ProgressIndicatorUtils.yieldToPendingWriteActions(); - } + ProgressManager.getInstance().runInReadActionWithWriteActionPriority(this::processFilesInReadAction); } } finally { diff --git a/platform/lang-impl/src/com/intellij/util/indexing/VfsEventsMerger.java b/platform/lang-impl/src/com/intellij/util/indexing/VfsEventsMerger.java index a41a5a529f0d..7bc171f5395b 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/VfsEventsMerger.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/VfsEventsMerger.java @@ -23,7 +23,7 @@ import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.Nullable; /** - * Created by Maxim.Mossienko on 11/10/2016. + * @author Maxim.Mossienko on 11/10/2016. */ public class VfsEventsMerger { public void recordFileEvent(int fileId, VirtualFile file, boolean contentChange) { @@ -35,19 +35,12 @@ public class VfsEventsMerger { } private void updateChange(int fileId, VirtualFile file, short mask) { - ChangeInfo newChangeInfo = new ChangeInfo(file, mask, null); - ChangeInfo existingChangeInfo = myChangeInfos.cacheOrGet(fileId, newChangeInfo); - - if (existingChangeInfo != newChangeInfo) { - while (true) { - newChangeInfo = new ChangeInfo(file, mask, existingChangeInfo); - if (existingChangeInfo == null) { - existingChangeInfo = myChangeInfos.cacheOrGet(fileId, newChangeInfo); - } else { - if (myChangeInfos.replace(fileId, existingChangeInfo, newChangeInfo)) break; - existingChangeInfo = myChangeInfos.get(fileId); - } - } + while (true) { + ChangeInfo existingChangeInfo = myChangeInfos.get(fileId); + ChangeInfo newChangeInfo = new ChangeInfo(file, mask, existingChangeInfo); + boolean replaced = existingChangeInfo == null ? myChangeInfos.putIfAbsent(fileId, newChangeInfo) == null + : myChangeInfos.replace(fileId, existingChangeInfo, newChangeInfo); + if (replaced) break; } } @@ -59,6 +52,7 @@ public class VfsEventsMerger { // 2. Method processes snapshot of available events at the time of the invokation, it does mean that if events are produced concurrently // with the processing then set of events will be not empty // 3. Method regularly checks for cancellations (thus can finish with PCEs) but event processor should process the change info atomically + // (without PCE) public boolean processChanges(VfsEventProcessor eventProcessor) { if (!myChangeInfos.isEmpty()) { int[] fileIds = myChangeInfos.keys(); // snapshot of the keys @@ -148,5 +142,4 @@ public class VfsEventsMerger { return fileId; } } - } From 4f2353912463ccfa34cbd3bc00610d59443aefb8 Mon Sep 17 00:00:00 2001 From: Eldar Abusalimov Date: Mon, 14 Nov 2016 12:50:16 +0300 Subject: [PATCH 06/33] io: Add BaseDataReader.waitFor(long, TimeUnit) --- .../src/com/intellij/util/io/BaseDataReader.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/platform/util/src/com/intellij/util/io/BaseDataReader.java b/platform/util/src/com/intellij/util/io/BaseDataReader.java index 62061de38fed..f6c523835f52 100644 --- a/platform/util/src/com/intellij/util/io/BaseDataReader.java +++ b/platform/util/src/com/intellij/util/io/BaseDataReader.java @@ -22,6 +22,8 @@ import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** * @author Konstantin Kolosovsky. @@ -212,4 +214,13 @@ public abstract class BaseDataReader { LOG.error(e); } } -} \ No newline at end of file + + public void waitFor(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { + try { + myFinishedFuture.get(timeout, unit); + } + catch (ExecutionException e) { + LOG.error(e); + } + } +} From 5acf6406e82d62d625b7a848aaa430e8c9d9c2bf Mon Sep 17 00:00:00 2001 From: Valentina Kiryushkina Date: Fri, 11 Nov 2016 21:48:42 +0300 Subject: [PATCH 07/33] Fix PY-21429 Duplicate output in Jupyter Notebook --- .../plugins/ipnb/editor/panels/code/IpnbCodePanel.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbCodePanel.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbCodePanel.java index d8a149c94689..6c0296bdb664 100644 --- a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbCodePanel.java +++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/code/IpnbCodePanel.java @@ -308,7 +308,10 @@ public class IpnbCodePanel extends IpnbEditablePanel { } if (outputContent != null) { myCell.addCellOutput(outputContent); - addOutputPanel(myViewPanel, outputContent, outputContent instanceof IpnbOutOutputCell); + final JComponent component = myHideableOutputPanel.getSecondComponent(); + if (component != null) { + addOutputPanel(component, outputContent, outputContent instanceof IpnbOutOutputCell); + } } final IpnbFilePanel filePanel = myParent.getIpnbFilePanel(); filePanel.revalidateAndRepaint(); From f5298e391e04c0cad02974f959e88d0b608c9e4f Mon Sep 17 00:00:00 2001 From: Valentina Kiryushkina Date: Mon, 14 Nov 2016 11:20:30 +0300 Subject: [PATCH 08/33] Fix PY-21427 Double click on the output field inside jupyter notebook raises UnsupportedOperationException: use mutable collections instead of EmptyMap, EmptyList --- .../plugins/ipnb/format/IpnbParser.java | 17 ++++++++++------- .../plugins/ipnb/format/cells/IpnbCodeCell.java | 6 +++--- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/format/IpnbParser.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/format/IpnbParser.java index f2acf5f5450a..dc2ebcf61ae2 100644 --- a/python/ipnb/src/org/jetbrains/plugins/ipnb/format/IpnbParser.java +++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/format/IpnbParser.java @@ -28,7 +28,10 @@ import org.jetbrains.plugins.ipnb.format.cells.output.*; import java.io.*; import java.lang.reflect.Type; import java.nio.charset.Charset; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public class IpnbParser { private static final Logger LOG = Logger.getInstance(IpnbParser.class); @@ -57,7 +60,7 @@ public class IpnbParser { IpnbFileRaw rawFile = gson.fromJson(fileText.toString(), IpnbFileRaw.class); if (rawFile == null) { int nbformat = isIpythonNewFormat(virtualFile) ? 4 : 3; - return new IpnbFile(Collections.emptyMap(), nbformat, Lists.newArrayList(), path); + return new IpnbFile(new HashMap<>(), nbformat, Lists.newArrayList(), path); } List cells = new ArrayList<>(); final IpnbWorksheet[] worksheets = rawFile.worksheets; @@ -256,7 +259,7 @@ public class IpnbParser { public IpnbCell createCell(boolean isValidSource) { final IpnbCell cell; if (cell_type.equals("markdown")) { - cell = new IpnbMarkdownCell(isValidSource ? source : Collections.emptyList(), metadata); + cell = new IpnbMarkdownCell(isValidSource ? source : new ArrayList<>(), metadata); } else if (cell_type.equals("code")) { final List outputCells = new ArrayList<>(); @@ -265,14 +268,14 @@ public class IpnbParser { } final Integer prompt = prompt_number != null ? prompt_number : execution_count; cell = new IpnbCodeCell(language == null ? "python" : language, - input == null ? (isValidSource ? source : Collections.emptyList()) : input, + input == null ? (isValidSource ? source : new ArrayList<>()) : input, prompt, outputCells, metadata); } else if (cell_type.equals("raw")) { - cell = new IpnbRawCell(isValidSource ? source : Collections.emptyList()); + cell = new IpnbRawCell(isValidSource ? source : new ArrayList<>()); } else if (cell_type.equals("heading")) { - cell = new IpnbHeadingCell(isValidSource ? source : Collections.emptyList(), level, metadata); + cell = new IpnbHeadingCell(isValidSource ? source : new ArrayList<>(), level, metadata); } else { cell = null; @@ -303,7 +306,7 @@ public class IpnbParser { final CellOutputRaw raw = new CellOutputRaw(); raw.metadata = outputCell.getMetadata(); if (raw.metadata == null && !(outputCell instanceof IpnbStreamOutputCell) && !(outputCell instanceof IpnbErrorOutputCell)) { - raw.metadata = Collections.emptyMap(); + raw.metadata = new HashMap<>(); } if (outputCell instanceof IpnbPngOutputCell) { diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/format/cells/IpnbCodeCell.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/format/cells/IpnbCodeCell.java index 56416e2f40fc..f9fa06a968e4 100644 --- a/python/ipnb/src/org/jetbrains/plugins/ipnb/format/cells/IpnbCodeCell.java +++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/format/cells/IpnbCodeCell.java @@ -5,7 +5,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.ipnb.format.cells.output.IpnbOutputCell; import java.util.ArrayList; -import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -61,7 +61,7 @@ public class IpnbCodeCell extends IpnbEditableCell { @NotNull public static IpnbCodeCell createEmptyCodeCell() { - return new IpnbCodeCell("python", Collections.emptyList(), null, new ArrayList<>(), - Collections.emptyMap()); + return new IpnbCodeCell("python", new ArrayList<>(), null, new ArrayList<>(), + new HashMap<>()); } } From 9d897547a68f7cbd6a8d747d4a2bd76d7d8c3051 Mon Sep 17 00:00:00 2001 From: Valentina Kiryushkina Date: Mon, 14 Nov 2016 11:26:08 +0300 Subject: [PATCH 09/33] Fix PY-21428 Cutting only cell raises ArrayIndexOutOfBoundsException --- .../plugins/ipnb/editor/panels/IpnbFilePanel.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbFilePanel.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbFilePanel.java index 383fe056715c..2bf7c25988d4 100644 --- a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbFilePanel.java +++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbFilePanel.java @@ -391,9 +391,11 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider, D myIpnbFile.removeCell(index); remove(index); - int indexToSelect = index < myIpnbPanels.size() ? index : index-1; - final IpnbEditablePanel panel = myIpnbPanels.get(indexToSelect); - setSelectedCell(panel, false); + if (!myIpnbPanels.isEmpty()) { + int indexToSelect = index < myIpnbPanels.size() ? index : index - 1; + final IpnbEditablePanel panel = myIpnbPanels.get(indexToSelect); + setSelectedCell(panel, false); + } } public void saveToFile(boolean synchronize) { From d193f06c486b5f5228a2ddf141c843dd60f0ba65 Mon Sep 17 00:00:00 2001 From: Valentina Kiryushkina Date: Mon, 14 Nov 2016 12:31:27 +0300 Subject: [PATCH 10/33] Fix PY-21430 "File of this document has been deleted" exception after removing notebook and switching interpreter: Dispose message bus connection for IpnbFilePanel --- .../ipnb/editor/panels/IpnbFilePanel.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbFilePanel.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbFilePanel.java index 2bf7c25988d4..e1af974fa949 100644 --- a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbFilePanel.java +++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbFilePanel.java @@ -22,6 +22,7 @@ import com.intellij.psi.PsiDocumentManager; import com.intellij.ui.JBColor; import com.intellij.util.Alarm; import com.intellij.util.PlatformUtils; +import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -51,6 +52,7 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider, D private static final Logger LOG = Logger.getInstance(IpnbFilePanel.class); private final DocumentAdapter myDocumentListener; private final Document myDocument; + private final MessageBusConnection myBusConnection; private IpnbFile myIpnbFile; private final Project myProject; @NotNull private final IpnbFileEditor myParent; @@ -98,19 +100,22 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider, D }, 10, ModalityState.stateForComponent(this)); UIUtil.requestFocus(this); - ApplicationManager.getApplication().getMessageBus().connect().subscribe(ProjectEx.ProjectSaved.TOPIC, - new ProjectEx.ProjectSaved() { - @Override - public void saved(@NotNull Project project) { - CommandProcessor.getInstance().runUndoTransparentAction( - () -> ApplicationManager.getApplication() - .runWriteAction(() -> saveToFile(false))); - } - }); + myBusConnection = ApplicationManager.getApplication().getMessageBus().connect(); + myBusConnection.subscribe(ProjectEx.ProjectSaved.TOPIC, + new ProjectEx.ProjectSaved() { + @Override + public void saved(@NotNull Project project) { + CommandProcessor.getInstance().runUndoTransparentAction( + () -> ApplicationManager.getApplication() + .runWriteAction(() -> saveToFile(false))); + } + }); } + @Override public void dispose() { myDocument.removeDocumentListener(myDocumentListener); + Disposer.dispose(myBusConnection); } private void readFromFile(boolean showError) { @@ -622,7 +627,7 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider, D createAndAddCell(true, IpnbCodeCell.createEmptyCodeCell()); CommandProcessor.getInstance().executeCommand(getProject(), () -> ApplicationManager.getApplication().runWriteAction( - () -> saveToFile(false)), "Ipnb.runCell", new Object()); + () -> saveToFile(false)), "Ipnb.runCell", new Object()); } } From fa488d75f00520385988aa38f5ef1ffe3c7c2590 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 11 Nov 2016 16:49:49 +0300 Subject: [PATCH 11/33] cleanup --- .../UnusedDeclarationInspectionBase.java | 50 ++++++----------- .../deadCode/UnusedDeclarationInspection.java | 55 ++++--------------- .../codeInspection/GlobalInspectionTool.java | 3 +- .../InspectionProfileEntry.java | 24 +++----- 4 files changed, 39 insertions(+), 93 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspectionBase.java index 8d2b4ede119e..389d4ee0588f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspectionBase.java @@ -65,7 +65,7 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { public boolean ADD_APPLET_TO_ENTRIES = true; public boolean ADD_SERVLET_TO_ENTRIES = true; public boolean ADD_NONJAVA_TO_ENTRIES = true; - protected boolean TEST_ENTRY_POINTS = true; + private boolean TEST_ENTRY_POINTS = true; public static final String DISPLAY_NAME = InspectionsBundle.message("inspection.dead.code.display.name"); public static final String SHORT_NAME = HighlightInfoType.UNUSED_SYMBOL_SHORT_NAME; @@ -290,7 +290,6 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { if (isSuppressed || !scope.contains(file)) { getEntryPointsManager(globalContext).addEntryPoint(refElement, false); } - return; } } } @@ -323,12 +322,9 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { String qualifiedName = psiClass != null ? psiClass.getQualifiedName() : null; if (qualifiedName != null) { final GlobalSearchScope projectScope = GlobalSearchScope.projectScope(globalContext.getProject()); - final PsiNonJavaFileReferenceProcessor processor = new PsiNonJavaFileReferenceProcessor() { - @Override - public boolean process(PsiFile file, int startOffset, int endOffset) { - getEntryPointsManager(globalContext).addEntryPoint(refElement, false); - return false; - } + final PsiNonJavaFileReferenceProcessor processor = (file, startOffset, endOffset) -> { + getEntryPointsManager(globalContext).addEntryPoint(refElement, false); + return false; }; final DelegatingGlobalSearchScope globalSearchScope = new DelegatingGlobalSearchScope(projectScope) { @Override @@ -450,9 +446,9 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { checkForReachableRefs(globalContext); final RefFilter filter = myPhase == 1 ? new StrictUnreferencedFilter(this, globalContext) : new RefUnreachableFilter(this, globalContext); - final boolean[] requestAdded = {false}; LOG.assertTrue(myProcessedSuspicious != null, "phase: " + myPhase); + final boolean[] requestAdded = {false}; globalContext.getRefManager().iterate(new RefJavaVisitor() { @Override public void visitElement(@NotNull RefEntity refEntity) { @@ -469,12 +465,9 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { getEntryPointsManager(globalContext).addEntryPoint(refField, false); } else { - globalContext.getExtension(GlobalJavaInspectionContext.CONTEXT).enqueueFieldUsagesProcessor(refField, new GlobalJavaInspectionContext.UsagesProcessor() { - @Override - public boolean process(PsiReference psiReference) { - getEntryPointsManager(globalContext).addEntryPoint(refField, false); - return false; - } + globalContext.getExtension(GlobalJavaInspectionContext.CONTEXT).enqueueFieldUsagesProcessor(refField, psiReference -> { + getEntryPointsManager(globalContext).addEntryPoint(refField, false); + return false; }); requestAdded[0] = true; } @@ -506,20 +499,14 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { public void visitClass(@NotNull final RefClass refClass) { myProcessedSuspicious.add(refClass); if (!refClass.isAnonymous()) { - globalContext.getExtension(GlobalJavaInspectionContext.CONTEXT).enqueueDerivedClassesProcessor(refClass, new GlobalJavaInspectionContext.DerivedClassesProcessor() { - @Override - public boolean process(PsiClass inheritor) { - getEntryPointsManager(globalContext).addEntryPoint(refClass, false); - return false; - } + globalContext.getExtension(GlobalJavaInspectionContext.CONTEXT).enqueueDerivedClassesProcessor(refClass, inheritor -> { + getEntryPointsManager(globalContext).addEntryPoint(refClass, false); + return false; }); - globalContext.getExtension(GlobalJavaInspectionContext.CONTEXT).enqueueClassUsagesProcessor(refClass, new GlobalJavaInspectionContext.UsagesProcessor() { - @Override - public boolean process(PsiReference psiReference) { - getEntryPointsManager(globalContext).addEntryPoint(refClass, false); - return false; - } + globalContext.getExtension(GlobalJavaInspectionContext.CONTEXT).enqueueClassUsagesProcessor(refClass, psiReference -> { + getEntryPointsManager(globalContext).addEntryPoint(refClass, false); + return false; }); requestAdded[0] = true; } @@ -549,12 +536,9 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { private static void enqueueMethodUsages(GlobalInspectionContext globalContext, final RefMethod refMethod) { if (refMethod.getSuperMethods().isEmpty()) { - globalContext.getExtension(GlobalJavaInspectionContext.CONTEXT).enqueueMethodUsagesProcessor(refMethod, new GlobalJavaInspectionContext.UsagesProcessor() { - @Override - public boolean process(PsiReference psiReference) { - getEntryPointsManager(globalContext).addEntryPoint(refMethod, false); - return false; - } + globalContext.getExtension(GlobalJavaInspectionContext.CONTEXT).enqueueMethodUsagesProcessor(refMethod, psiReference -> { + getEntryPointsManager(globalContext).addEntryPoint(refMethod, false); + return false; }); } else { diff --git a/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspection.java b/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspection.java index 98b0b6423a9d..f6fcde195ba3 100644 --- a/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspection.java @@ -25,7 +25,6 @@ import com.intellij.codeInspection.reference.*; import com.intellij.codeInspection.ui.InspectionToolPresentation; import com.intellij.codeInspection.unusedSymbol.UnusedSymbolLocalInspection; import com.intellij.codeInspection.unusedSymbol.UnusedSymbolLocalInspectionBase; -import com.intellij.openapi.ui.VerticalFlowLayout; import com.intellij.psi.*; import com.intellij.psi.controlFlow.DefUseUtil; import com.intellij.ui.ScrollPaneFactory; @@ -34,7 +33,6 @@ import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBRadioButton; import com.intellij.ui.components.JBTabbedPane; import com.intellij.util.ObjectUtils; -import com.intellij.util.ui.JBInsets; import com.intellij.util.ui.JBUI; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; @@ -43,10 +41,11 @@ import org.jetbrains.annotations.TestOnly; import javax.swing.*; import java.awt.*; -import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.*; +import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.Set; public class UnusedDeclarationInspection extends UnusedDeclarationInspectionBase { private final UnusedParametersInspection myUnusedParameters = new UnusedParametersInspection(); @@ -155,12 +154,7 @@ public class UnusedDeclarationInspection extends UnusedDeclarationInspectionBase final ButtonGroup group = new ButtonGroup(); group.add(asEntryPoint); group.add(asUnused); - final ActionListener listener = new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - setTestEntryPoints(asEntryPoint.isSelected()); - } - }; + final ActionListener listener = e -> setTestEntryPoints(asEntryPoint.isSelected()); asEntryPoint.addActionListener(listener); asUnused.addActionListener(listener); @@ -187,12 +181,7 @@ public class UnusedDeclarationInspection extends UnusedDeclarationInspectionBase myMainsCheckbox = new JCheckBox(InspectionsBundle.message("inspection.dead.code.option.main")); myMainsCheckbox.setSelected(ADD_MAINS_TO_ENTRIES); - myMainsCheckbox.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ADD_MAINS_TO_ENTRIES = myMainsCheckbox.isSelected(); - } - }); + myMainsCheckbox.addActionListener(e -> ADD_MAINS_TO_ENTRIES = myMainsCheckbox.isSelected()); add(myMainsCheckbox, gc); @@ -200,23 +189,13 @@ public class UnusedDeclarationInspection extends UnusedDeclarationInspectionBase myAppletToEntries = new JCheckBox(InspectionsBundle.message("inspection.dead.code.option.applet")); myAppletToEntries.setSelected(ADD_APPLET_TO_ENTRIES); - myAppletToEntries.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ADD_APPLET_TO_ENTRIES = myAppletToEntries.isSelected(); - } - }); + myAppletToEntries.addActionListener(e -> ADD_APPLET_TO_ENTRIES = myAppletToEntries.isSelected()); add(myAppletToEntries, gc); gc.gridy++; myServletToEntries = new JCheckBox(InspectionsBundle.message("inspection.dead.code.option.servlet")); myServletToEntries.setSelected(ADD_SERVLET_TO_ENTRIES); - myServletToEntries.addActionListener(new ActionListener(){ - @Override - public void actionPerformed(ActionEvent e) { - ADD_SERVLET_TO_ENTRIES = myServletToEntries.isSelected(); - } - }); + myServletToEntries.addActionListener(e -> ADD_SERVLET_TO_ENTRIES = myServletToEntries.isSelected()); add(myServletToEntries, gc); gc.gridy++; @@ -224,12 +203,7 @@ public class UnusedDeclarationInspection extends UnusedDeclarationInspectionBase if (extension.showUI()) { final JCheckBox extCheckbox = new JCheckBox(extension.getDisplayName()); extCheckbox.setSelected(extension.isSelected()); - extCheckbox.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - extension.setSelected(extCheckbox.isSelected()); - } - }); + extCheckbox.addActionListener(e -> extension.setSelected(extCheckbox.isSelected())); add(extCheckbox, gc); gc.gridy++; } @@ -238,12 +212,7 @@ public class UnusedDeclarationInspection extends UnusedDeclarationInspectionBase myNonJavaCheckbox = new JCheckBox(InspectionsBundle.message("inspection.dead.code.option.external")); myNonJavaCheckbox.setSelected(ADD_NONJAVA_TO_ENTRIES); - myNonJavaCheckbox.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ADD_NONJAVA_TO_ENTRIES = myNonJavaCheckbox.isSelected(); - } - }); + myNonJavaCheckbox.addActionListener(e -> ADD_NONJAVA_TO_ENTRIES = myNonJavaCheckbox.isSelected()); gc.weighty = 1; add(myNonJavaCheckbox, gc); @@ -267,10 +236,10 @@ public class UnusedDeclarationInspection extends UnusedDeclarationInspectionBase private class UnusedVariablesGraphAnnotator extends RefGraphAnnotator { private final InspectionManager myInspectionManager; - private GlobalInspectionContextImpl myContext; - private Map myTools; + private final GlobalInspectionContextImpl myContext; + private final Map myTools; - public UnusedVariablesGraphAnnotator(InspectionManager inspectionManager, RefManager refManager) { + UnusedVariablesGraphAnnotator(InspectionManager inspectionManager, RefManager refManager) { myInspectionManager = inspectionManager; myContext = (GlobalInspectionContextImpl)((RefManagerImpl)refManager).getContext(); myTools = myContext.getTools(); diff --git a/platform/analysis-api/src/com/intellij/codeInspection/GlobalInspectionTool.java b/platform/analysis-api/src/com/intellij/codeInspection/GlobalInspectionTool.java index a40f7a06e91a..259b41260452 100644 --- a/platform/analysis-api/src/com/intellij/codeInspection/GlobalInspectionTool.java +++ b/platform/analysis-api/src/com/intellij/codeInspection/GlobalInspectionTool.java @@ -148,7 +148,7 @@ public abstract class GlobalInspectionTool extends InspectionProfileEntry { * usages of the same classes and methods, usage searches are not performed directly, but * instead are queued for batch processing through * {@link GlobalJavaInspectionContext#enqueueClassUsagesProcessor} and similar methods. The method - * can add new problems to problemDescriptionsProcessor or remove some of the problems + * can add new problems to {@code problemDescriptionsProcessor} or remove some of the problems * collected by {@link #runInspection(AnalysisScope, InspectionManager, GlobalInspectionContext, ProblemDescriptionsProcessor)} * by calling {@link ProblemDescriptionsProcessor#ignoreElement(RefEntity)}. * @@ -207,7 +207,6 @@ public abstract class GlobalInspectionTool extends InspectionProfileEntry { * @return JobDescriptors array to show inspection progress correctly. TotalAmount should be set (e.g. in * {@link #runInspection(AnalysisScope, InspectionManager, GlobalInspectionContext, ProblemDescriptionsProcessor)}) * ProgressIndicator should progress with {@link GlobalInspectionContext#incrementJobDoneAmount(JobDescriptor, String)} - * @param context */ @Nullable public JobDescriptor[] getAdditionalJobs(GlobalInspectionContext context) { diff --git a/platform/analysis-api/src/com/intellij/codeInspection/InspectionProfileEntry.java b/platform/analysis-api/src/com/intellij/codeInspection/InspectionProfileEntry.java index a1686cf42941..d2d041879c76 100644 --- a/platform/analysis-api/src/com/intellij/codeInspection/InspectionProfileEntry.java +++ b/platform/analysis-api/src/com/intellij/codeInspection/InspectionProfileEntry.java @@ -311,8 +311,8 @@ public abstract class InspectionProfileEntry implements BatchSuppressableTool { /** * Read in settings from XML config. - * Default implementation uses XmlSerializer so you may use public fields (like int TOOL_OPTION) - * and bean-style getters/setters (like int getToolOption(), void setToolOption(int)) to store your options. + * Default implementation uses XmlSerializer so you may use public fields (like {@code int TOOL_OPTION}) + * and bean-style getters/setters (like {@code int getToolOption(), void setToolOption(int)}) to store your options. * * @param node to read settings from. * @throws InvalidDataException if the loaded data was not valid. @@ -334,8 +334,8 @@ public abstract class InspectionProfileEntry implements BatchSuppressableTool { /** * Store current settings in XML config. - * Default implementation uses XmlSerializer so you may use public fields (like int TOOL_OPTION) - * and bean-style getters/setters (like int getToolOption(), void setToolOption(int)) to store your options. + * Default implementation uses XmlSerializer so you may use public fields (like {@code int TOOL_OPTION}) + * and bean-style getters/setters (like {@code int getToolOption(), void setToolOption(int)}) to store your options. * * @param node to store settings to. * @throws WriteExternalException if no data should be saved for this component. @@ -366,17 +366,11 @@ public abstract class InspectionProfileEntry implements BatchSuppressableTool { return; } - try { - final BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream())); - try { - String line; - while ((line = reader.readLine()) != null) { - line = line.trim(); - if (!line.isEmpty()) ourBlackList.add(line); - } - } - finally { - reader.close(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()))) { + String line; + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (!line.isEmpty()) ourBlackList.add(line); } } catch (IOException e) { From adbfcb2649696dd0fff0cd6ba02d7ab7fe2dfce3 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 11 Nov 2016 18:23:51 +0300 Subject: [PATCH 12/33] diagnostics --- .../intellij/openapi/command/impl/UndoManagerImpl.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoManagerImpl.java index 86fe872a120e..230e9cbb62ec 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoManagerImpl.java @@ -163,7 +163,7 @@ public class UndoManagerImpl extends UndoManager implements ProjectComponent, Ap runStartupActivity(); } else { - myStartupManager.registerStartupActivity(() -> runStartupActivity()); + myStartupManager.registerStartupActivity(this::runStartupActivity); } } @@ -591,7 +591,7 @@ public class UndoManagerImpl extends UndoManager implements ProjectComponent, Ap if (refs.size() <= FREE_QUEUES_LIMIT) return; DocumentReference[] backSorted = refs.toArray(new DocumentReference[refs.size()]); - Arrays.sort(backSorted, (a, b) -> getLastCommandTimestamp(a) - getLastCommandTimestamp(b)); + Arrays.sort(backSorted, Comparator.comparingInt(this::getLastCommandTimestamp)); for (int i = 0; i < backSorted.length - FREE_QUEUES_LIMIT; i++) { DocumentReference each = backSorted[i]; @@ -634,7 +634,7 @@ public class UndoManagerImpl extends UndoManager implements ProjectComponent, Ap @TestOnly public void dropHistoryInTests() { flushMergers(); - LOG.assertTrue(myCommandLevel == 0); + LOG.assertTrue(myCommandLevel == 0, myCommandLevel); myUndoStacksHolder.clearAllStacksInTests(); myRedoStacksHolder.clearAllStacksInTests(); @@ -643,8 +643,7 @@ public class UndoManagerImpl extends UndoManager implements ProjectComponent, Ap @TestOnly private void flushMergers() { // Run dummy command in order to flush all mergers... - CommandProcessor.getInstance() - .executeCommand(myProject, EmptyRunnable.getInstance(), CommonBundle.message("drop.undo.history.command.name"), null); + CommandProcessor.getInstance().executeCommand(myProject, EmptyRunnable.getInstance(), CommonBundle.message("drop.undo.history.command.name"), null); } @TestOnly From 2999ef114a4bd59ac28718e071f59815ae3b9176 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Sat, 12 Nov 2016 12:42:17 +0300 Subject: [PATCH 13/33] cancel earlier: even when isReadAccessAllowed() or nested runReadAction() are called under impatient reader with pending write --- .../application/impl/ReadMostlyRWLock.java | 12 ++++-- .../application/impl/ApplicationImplTest.java | 43 +++++++++++++++++++ 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/ReadMostlyRWLock.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/ReadMostlyRWLock.java index d80ab9524095..576a39db523b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/ReadMostlyRWLock.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/ReadMostlyRWLock.java @@ -86,6 +86,7 @@ class ReadMostlyRWLock { boolean isReadLockedByThisThread() { checkReadThreadAccess(); Reader status = R.get(); + throwIfImpatient(status); return status.readRequested; } @@ -107,9 +108,7 @@ class ReadMostlyRWLock { if (iteration > SPIN_TO_WAIT_FOR_LOCK) { status.blocked = true; try { - if (status.impatientReads) { - throw new ApplicationUtil.CannotRunReadActionException(); - } + throwIfImpatient(status); LockSupport.parkNanos(this, 1000000); // unparked by writeUnlock } finally { @@ -121,6 +120,13 @@ class ReadMostlyRWLock { } } + private void throwIfImpatient(Reader status) { + // when client explicitly runs in non-cancelable block do not throw from within nested read actions + if (status.impatientReads && writeRequested && !ProgressManager.getInstance().isInNonCancelableSection()) { + throw new ApplicationUtil.CannotRunReadActionException(); + } + } + /** * Executes a {@code runnable} in an "impatient" mode. * In this mode any attempt to grab read lock diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/ApplicationImplTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/ApplicationImplTest.java index 2c59780f1923..c69992f75048 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/ApplicationImplTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/ApplicationImplTest.java @@ -714,4 +714,47 @@ public class ApplicationImplTest extends LightPlatformTestCase { readAction2.get(); readAction1.get(); } + + public void testReadActionInImpatientModeMustNotThrowWhenThereIsAPendingWriteAndWeAreUnderNonCancelableSection() throws Exception { + AtomicBoolean stopRead = new AtomicBoolean(); + AtomicBoolean readAcquired = new AtomicBoolean(); + ApplicationImpl app = (ApplicationImpl)ApplicationManager.getApplication(); + Future readAction1 = app.executeOnPooledThread(() -> + app.runReadAction(() -> { + readAcquired.set(true); + try { + while (!stopRead.get()) ; + } + finally { + readAcquired.set(false); + } + }) + ); + while (!readAcquired.get()); + + AtomicBoolean executingImpatientReader = new AtomicBoolean(); + + Future readAction2 = app.executeOnPooledThread(() -> { + // wait for write action attempt to start + while (!app.isWriteActionPending()); + ProgressManager.getInstance().executeNonCancelableSection(()->{ + app.executeByImpatientReader(() -> { + executingImpatientReader.set(true); + app.runReadAction(EmptyRunnable.getInstance()); + // must not throw + }); + }); + }); + + Future readAction1Canceler = app.executeOnPooledThread(() -> { + while (!executingImpatientReader.get()); + TimeoutUtil.sleep(300); // make sure readAction2 does call runReadAction() + stopRead.set(true); + }); + app.runWriteAction(EmptyRunnable.getInstance()); + + readAction1Canceler.get(); + readAction2.get(); + readAction1.get(); + } } From 94a666a0dfaa005602c910957c968def97d828a0 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Sat, 12 Nov 2016 14:47:36 +0300 Subject: [PATCH 14/33] annotations.xml syntax checker --- .../BaseExternalAnnotationsManager.java | 12 +- .../ExternalAnnotationsManagerTest.java | 186 ++++++++++++++++++ java/jdkAnnotations/java/awt/annotations.xml | 3 - .../java/awt/datatransfer/annotations.xml | 2 +- .../java/awt/event/annotations.xml | 2 +- java/jdkAnnotations/java/lang/annotations.xml | 9 - .../java/security/annotations.xml | 2 +- java/jdkAnnotations/java/sql/annotations.xml | 2 +- java/jdkAnnotations/java/util/annotations.xml | 15 -- .../javax/swing/annotations.xml | 20 +- .../javax/swing/plaf/basic/annotations.xml | 2 +- java/jdkAnnotations/org/jdom/annotations.xml | 6 +- .../intellij/testFramework/PsiTestUtil.java | 12 +- 13 files changed, 221 insertions(+), 52 deletions(-) create mode 100644 java/java-tests/testSrc/com/intellij/codeInsight/ExternalAnnotationsManagerTest.java diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java b/java/java-psi-impl/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java index fb18c6f4652c..836a222dcdf5 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/BaseExternalAnnotationsManager.java @@ -158,7 +158,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations } @NotNull - private MostlySingularMultiMap getDataFromFile(@NotNull PsiFile file) { + MostlySingularMultiMap getDataFromFile(@NotNull PsiFile file) { Pair, Long> cached = myAnnotationFileToDataAndModStampCache.get(file); long fileModificationStamp = file.getModificationStamp(); if (cached != null && cached.getSecond() == fileModificationStamp) { @@ -336,16 +336,16 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations throw new UnsupportedOperationException(); } - protected void cacheExternalAnnotations(@SuppressWarnings("UnusedParameters") @NotNull String packageName, - @NotNull PsiFile fromFile, - @NotNull List annotationFiles) { + void cacheExternalAnnotations(@SuppressWarnings("UnusedParameters") @NotNull String packageName, + @NotNull PsiFile fromFile, + @NotNull List annotationFiles) { VirtualFile virtualFile = fromFile.getVirtualFile(); if (virtualFile != null) { myExternalAnnotationsCache.put(virtualFile, annotationFiles); } } - private static class AnnotationData { + static class AnnotationData { private final String annotationClassFqName; private final String annotationParameters; @@ -357,7 +357,7 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations } @NotNull - private PsiAnnotation getAnnotation(@NotNull BaseExternalAnnotationsManager context) { + PsiAnnotation getAnnotation(@NotNull BaseExternalAnnotationsManager context) { PsiAnnotation a = myAnnotation; if (a == null) { String text = "@" + annotationClassFqName + (annotationParameters.isEmpty() ? "" : "(" + annotationParameters + ")"); diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/ExternalAnnotationsManagerTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/ExternalAnnotationsManagerTest.java new file mode 100644 index 000000000000..e3da0c5bd6a4 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/ExternalAnnotationsManagerTest.java @@ -0,0 +1,186 @@ +/* + * 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.codeInsight; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.application.ex.PathManagerEx; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl; +import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.*; +import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiFormatUtil; +import com.intellij.testFramework.IdeaTestCase; +import com.intellij.testFramework.PsiTestUtil; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MostlySingularMultiMap; +import com.intellij.xml.util.XmlUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +public class ExternalAnnotationsManagerTest extends IdeaTestCase { + @Override + protected Sdk getTestProjectJdk() { + Sdk jdk = JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk(); + Sdk sdk = PsiTestUtil.addJdkAnnotations(jdk); + String home = jdk.getHomeDirectory().getParent().getPath(); + String toolsPath = home + "/lib/tools.jar!/"; + VfsRootAccess.allowRootAccess(getTestRootDisposable(), home); + VirtualFile toolsJar = JarFileSystem.getInstance().findFileByPath(toolsPath); + + Sdk plusTools = PsiTestUtil.addRootsToJdk(sdk, OrderRootType.CLASSES, toolsJar); + + Collection utilClassPath = PathManager.getUtilClassPath(); + VirtualFile[] files = utilClassPath.stream() + .map(path -> path.endsWith(".jar") ? + JarFileSystem.getInstance() .findFileByPath(FileUtil.toSystemIndependentName(path) + "!/") : + LocalFileSystem.getInstance() .findFileByPath(FileUtil.toSystemIndependentName(path))) + .toArray(VirtualFile[]::new); + + Sdk result = PsiTestUtil.addRootsToJdk(plusTools, OrderRootType.CLASSES, files); + return result; + } + + public void testBundledAnnotationXmls() { + String root = PathManagerEx.getCommunityHomePath() + "/java/jdkAnnotations"; + findAnnotationsXmlAndCheck(root); + } + + private void findAnnotationsXmlAndCheck(String root) { + VirtualFile jdkAnnoRoot = LocalFileSystem.getInstance().findFileByPath(root); + VfsUtilCore.visitChildrenRecursively(jdkAnnoRoot, new VirtualFileVisitor() { + @Override + public boolean visitFile(@NotNull VirtualFile file) { + if (file.getName().equals("annotations.xml")) { + check(file); + } + return true; + } + }); + } + + // some android classes are missing in IDEA, e.g. android.support.annotation.NonNull + public void _testAndroidAnnotationsXml() { + VirtualFile lib = LocalFileSystem.getInstance().findFileByPath(PathManagerEx.getCommunityHomePath() + "/android/android/lib"); + VirtualFile[] androidJars = Arrays.stream(lib.getChildren()) + .map(file -> file.getName().endsWith(".jar") ? + JarFileSystem.getInstance().getJarRootForLocalFile(file) : + file) + .toArray(VirtualFile[]::new); + + ApplicationManager.getApplication().runWriteAction(() -> ProjectRootManager.getInstance(getProject()) + .setProjectSdk(PsiTestUtil.addRootsToJdk(getTestProjectJdk(), OrderRootType.CLASSES, androidJars))); + + String root = PathManagerEx.getCommunityHomePath() + "/android/android/annotations"; + findAnnotationsXmlAndCheck(root); + } + + private void check(VirtualFile file) { + //System.out.println("file = " + file); + ExternalAnnotationsManagerImpl manager = (ExternalAnnotationsManagerImpl)ExternalAnnotationsManager.getInstance(getProject()); + PsiFile psiFile = getPsiManager().findFile(file); + MostlySingularMultiMap map = manager.getDataFromFile(psiFile); + for (String externalName : map.keySet()) { + checkExternalName(psiFile, externalName); + + // 'annotation name="org.jetbrains.annotations.NotNull"' should have FQN + for (BaseExternalAnnotationsManager.AnnotationData annotationData : map.get(externalName)) { + PsiAnnotation annotation = annotationData.getAnnotation(manager); + String nameText = annotation.getNameReferenceElement().getText(); + assertClassFqn(nameText, psiFile, externalName); + } + } + } + + private PsiClass assertClassFqn(String text, PsiFile psiFile, String externalName) { + if (!PsiNameHelper.getInstance(getProject()).isQualifiedName(text) || !text.contains(".")) { + fail("'" + text + "' doesn't seem like a FQN", psiFile, externalName); + } + + PsiClass aClass = JavaPsiFacade.getInstance(getProject()).findClass(text, GlobalSearchScope.allScope(getProject())); + if (aClass == null) { + fail("'" + text + "' doesn't resolve to a class", psiFile, externalName); + } + return aClass; + } + + private static void fail(String error, PsiFile psiFile, String externalName) { + int offset = psiFile.getText().indexOf(XmlUtil.escape(externalName)); + int line = PsiDocumentManager.getInstance(psiFile.getProject()).getDocument(psiFile).getLineNumber(offset); + fail(error + "\nFile: " + psiFile.getVirtualFile().getPath() + ":" + (line+1) + " (offset: "+offset+")"); + } + + private void checkExternalName(PsiFile psiFile, String externalName) { + // 'item name="java.lang.ClassLoader java.net.URL getResource(java.lang.String) 0"' should have all FQNs + String unescaped = StringUtil.unescapeXml(externalName); + List words = StringUtil.split(unescaped, " "); + String className = words.get(0); + PsiClass aClass = assertClassFqn(className, psiFile, externalName); + if (words.size() == 1) return; + + String rest = unescaped.substring(className.length() + " ".length()); + + if (rest.indexOf('(') == -1) { + // field + String field = StringUtil.trim(rest); + PsiField psiField = aClass.findFieldByName(field, false); + if (psiField == null) { + fail("Field '"+field+"' not found in class '"+aClass.getQualifiedName()+"'", psiFile, externalName); + } + return; + } + String methodName = ContainerUtil.getLastItem(StringUtil.getWordsIn(rest.substring(0, rest.indexOf('(')))); + + String methodSignature = rest.substring(0, rest.indexOf(')') + 1); + String methodExternalName = className + " " + methodSignature; + + List methods = Arrays.stream(aClass.getMethods()) + .filter(method -> methodExternalName.equals(PsiFormatUtil.getExternalName(method, false, Integer.MAX_VALUE))) + .collect(Collectors.toList()); + boolean found = !methods.isEmpty(); + if (!found) { + List candidates = Arrays.stream(aClass.findMethodsByName(methodName, false)) + .map(method -> XmlUtil.escape(PsiFormatUtil.getExternalName(method, false, Integer.MAX_VALUE))) + .collect(Collectors.toList()); + String additionalMsg = candidates.isEmpty() ? "" : "\nMaybe you have meant one of these methods instead:\n"+StringUtil.join(candidates, "\n")+"\n"; + fail("This method was not found in class '"+aClass.getQualifiedName()+"':\n"+"'"+methodSignature+"'"+additionalMsg, psiFile, externalName); + } + + String parameterNumberText = StringUtil.trim(rest.substring(rest.indexOf(')') + 1)); + if (parameterNumberText.isEmpty()) return; + + try { + int paramNumber = Integer.parseInt(parameterNumberText); + PsiMethod method = methods.get(0); + if (method.getParameterList().getParametersCount() <= paramNumber) { + fail("Parameter number '"+paramNumber+"' is too big for a method '"+methodSignature+"'", psiFile, externalName); + } + } + catch (NumberFormatException e) { + fail("Parameter number is not an integer: '"+parameterNumberText+"'", psiFile, externalName); + } + } +} diff --git a/java/jdkAnnotations/java/awt/annotations.xml b/java/jdkAnnotations/java/awt/annotations.xml index 50d1f16ca819..74edac03f2b4 100644 --- a/java/jdkAnnotations/java/awt/annotations.xml +++ b/java/jdkAnnotations/java/awt/annotations.xml @@ -65,9 +65,6 @@ - - - diff --git a/java/jdkAnnotations/java/awt/datatransfer/annotations.xml b/java/jdkAnnotations/java/awt/datatransfer/annotations.xml index 329dbd0b61bf..afc6ca191604 100644 --- a/java/jdkAnnotations/java/awt/datatransfer/annotations.xml +++ b/java/jdkAnnotations/java/awt/datatransfer/annotations.xml @@ -2,7 +2,7 @@ - + diff --git a/java/jdkAnnotations/java/awt/event/annotations.xml b/java/jdkAnnotations/java/awt/event/annotations.xml index 8808ee8ae40c..00a5129e2911 100644 --- a/java/jdkAnnotations/java/awt/event/annotations.xml +++ b/java/jdkAnnotations/java/awt/event/annotations.xml @@ -30,7 +30,7 @@ - + diff --git a/java/jdkAnnotations/java/lang/annotations.xml b/java/jdkAnnotations/java/lang/annotations.xml index 7ce0469d41b8..572588fb1356 100644 --- a/java/jdkAnnotations/java/lang/annotations.xml +++ b/java/jdkAnnotations/java/lang/annotations.xml @@ -37,12 +37,6 @@ - - - - - - @@ -70,9 +64,6 @@ - - - diff --git a/java/jdkAnnotations/java/security/annotations.xml b/java/jdkAnnotations/java/security/annotations.xml index 96f7fd9febac..21f4ccae9a49 100644 --- a/java/jdkAnnotations/java/security/annotations.xml +++ b/java/jdkAnnotations/java/security/annotations.xml @@ -1,5 +1,5 @@ - + diff --git a/java/jdkAnnotations/java/sql/annotations.xml b/java/jdkAnnotations/java/sql/annotations.xml index 59c93eb08f23..b9c1e4a91140 100644 --- a/java/jdkAnnotations/java/sql/annotations.xml +++ b/java/jdkAnnotations/java/sql/annotations.xml @@ -120,7 +120,7 @@ - + diff --git a/java/jdkAnnotations/java/util/annotations.xml b/java/jdkAnnotations/java/util/annotations.xml index cbadb8b58ede..9301024eb2ab 100644 --- a/java/jdkAnnotations/java/util/annotations.xml +++ b/java/jdkAnnotations/java/util/annotations.xml @@ -612,21 +612,12 @@ - - - - - - - - - @@ -981,12 +972,6 @@ - - - - - - diff --git a/java/jdkAnnotations/javax/swing/annotations.xml b/java/jdkAnnotations/javax/swing/annotations.xml index 6d21cc9622e2..2dd236b883b6 100644 --- a/java/jdkAnnotations/javax/swing/annotations.xml +++ b/java/jdkAnnotations/javax/swing/annotations.xml @@ -322,6 +322,16 @@ + + + + + + + + + + @@ -331,16 +341,6 @@ - - - - - - - - - - diff --git a/java/jdkAnnotations/javax/swing/plaf/basic/annotations.xml b/java/jdkAnnotations/javax/swing/plaf/basic/annotations.xml index 420b4dcf6c75..f51d4d8ca431 100644 --- a/java/jdkAnnotations/javax/swing/plaf/basic/annotations.xml +++ b/java/jdkAnnotations/javax/swing/plaf/basic/annotations.xml @@ -10,7 +10,7 @@ - + diff --git a/java/jdkAnnotations/org/jdom/annotations.xml b/java/jdkAnnotations/org/jdom/annotations.xml index 02739c14c0c0..2171c711208e 100644 --- a/java/jdkAnnotations/org/jdom/annotations.xml +++ b/java/jdkAnnotations/org/jdom/annotations.xml @@ -36,13 +36,13 @@ - + - + - + diff --git a/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java b/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java index 9a1404a94bc0..50f5c6b04007 100644 --- a/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java +++ b/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java @@ -373,6 +373,14 @@ public class PsiTestUtil { public static Sdk addJdkAnnotations(@NotNull Sdk sdk) { String path = FileUtil.toSystemIndependentName(PlatformTestUtil.getCommunityPath()) + "/java/jdkAnnotations"; VirtualFile root = LocalFileSystem.getInstance().findFileByPath(path); + return addRootsToJdk(sdk, AnnotationOrderRootType.getInstance(), root); + } + + @NotNull + @Contract(pure=true) + public static Sdk addRootsToJdk(@NotNull Sdk sdk, + @NotNull OrderRootType rootType, + @NotNull VirtualFile... roots) { Sdk clone; try { clone = (Sdk)sdk.clone(); @@ -381,7 +389,9 @@ public class PsiTestUtil { throw new RuntimeException(e); } SdkModificator sdkModificator = clone.getSdkModificator(); - sdkModificator.addRoot(root, AnnotationOrderRootType.getInstance()); + for (VirtualFile root : roots) { + sdkModificator.addRoot(root, rootType); + } sdkModificator.commitChanges(); return clone; } From 6e84cb0eb64e33e68dadd3fee7b8f6754eb64aa9 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Mon, 14 Nov 2016 13:11:47 +0100 Subject: [PATCH 15/33] more compact code --- .../src/com/intellij/util/indexing/VfsEventsMerger.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/util/indexing/VfsEventsMerger.java b/platform/lang-impl/src/com/intellij/util/indexing/VfsEventsMerger.java index 7bc171f5395b..dc8fef174a11 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/VfsEventsMerger.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/VfsEventsMerger.java @@ -38,9 +38,7 @@ public class VfsEventsMerger { while (true) { ChangeInfo existingChangeInfo = myChangeInfos.get(fileId); ChangeInfo newChangeInfo = new ChangeInfo(file, mask, existingChangeInfo); - boolean replaced = existingChangeInfo == null ? myChangeInfos.putIfAbsent(fileId, newChangeInfo) == null - : myChangeInfos.replace(fileId, existingChangeInfo, newChangeInfo); - if (replaced) break; + if(myChangeInfos.put(fileId, newChangeInfo) == existingChangeInfo) break; } } From 2d2806c12604662fd59b867a11256def46ea9886 Mon Sep 17 00:00:00 2001 From: Andrey Vokin Date: Fri, 11 Nov 2016 14:48:22 +0100 Subject: [PATCH 16/33] Ruby remote sdk configuration. Processing dialog from terminal + default password --- platform/platform-impl/src/com/intellij/ssh/SSHUtil.java | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/platform-impl/src/com/intellij/ssh/SSHUtil.java b/platform/platform-impl/src/com/intellij/ssh/SSHUtil.java index bb61cd084f19..9142b15d7041 100644 --- a/platform/platform-impl/src/com/intellij/ssh/SSHUtil.java +++ b/platform/platform-impl/src/com/intellij/ssh/SSHUtil.java @@ -21,4 +21,5 @@ public class SSHUtil { public static final Pattern PASSPHRASE_PROMPT = Pattern.compile("Enter passphrase for key \\'(.*)\\':\\s?"); public static final Pattern PASSWORD_PROMPT = Pattern.compile("(.*)\\'s password:\\s?"); public static final String PASSWORD_PROMPT_SUFFIX = "password:"; + public static final String CONFIRM_CONNECTION_PROMPT = "Are you sure you want to continue connecting"; } From ac041653218c9c59ac14e5a8dc555a52f0ac897a Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Fri, 11 Nov 2016 12:55:37 +0300 Subject: [PATCH 17/33] diff: separate tests for words and inner fragments --- .../diff/comparison/ComparisonUtilTestBase.kt | 22 +++- .../diff/comparison/WordComparisonUtilTest.kt | 102 +++++++++--------- 2 files changed, 69 insertions(+), 55 deletions(-) diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilTestBase.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilTestBase.kt index 1559310cec45..ed4e081b9541 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilTestBase.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilTestBase.kt @@ -32,16 +32,24 @@ abstract class ComparisonUtilTestBase : DiffTestCase() { if (expected != null) checkLineChanges(fragments, expected) } - private fun doWordTest(before: Document, after: Document, matchings: Couple?, expected: List?, policy: ComparisonPolicy) { + private fun doLineInnerTest(before: Document, after: Document, matchings: Couple?, expected: List?, policy: ComparisonPolicy) { val rawFragments = MANAGER.compareLinesInner(before.charsSequence, after.charsSequence, policy, INDICATOR) val fragments = MANAGER.squash(rawFragments) - checkConsistencyWord(fragments, before, after) + checkConsistencyLineInner(fragments, before, after) val diffFragments = fragments[0].innerFragments!! if (matchings != null) checkDiffMatching(diffFragments, matchings) if (expected != null) checkDiffChanges(diffFragments, expected) } + private fun doWordTest(before: Document, after: Document, matchings: Couple?, expected: List?, policy: ComparisonPolicy) { + val fragments = MANAGER.compareWords(before.charsSequence, after.charsSequence, policy, INDICATOR) + checkConsistency(fragments, before, after) + + if (matchings != null) checkDiffMatching(fragments, matchings) + if (expected != null) checkDiffChanges(fragments, expected) + } + private fun doCharTest(before: Document, after: Document, matchings: Couple?, expected: List?, policy: ComparisonPolicy) { val fragments = MANAGER.compareChars(before.charsSequence, after.charsSequence, policy, INDICATOR) checkConsistency(fragments, before, after) @@ -62,7 +70,7 @@ abstract class ComparisonUtilTestBase : DiffTestCase() { if (expected != null) checkLineChanges(fragments, expected) } - private fun checkConsistencyWord(fragments: List, before: Document, after: Document) { + private fun checkConsistencyLineInner(fragments: List, before: Document, after: Document) { assertTrue(fragments.size == 1) val fragment = fragments[0] @@ -169,7 +177,7 @@ abstract class ComparisonUtilTestBase : DiffTestCase() { // internal enum class TestType { - LINE, WORD, CHAR, SPLITTER + LINE, LINE_INNER, WORD, CHAR, SPLITTER } internal inner class TestBuilder(private val type: TestType) { @@ -215,6 +223,10 @@ abstract class ComparisonUtilTestBase : DiffTestCase() { when (type) { TestType.LINE -> doLineTest(before!!, after!!, matchings, change, policy) + TestType.LINE_INNER -> { + doLineInnerTest(before!!, after!!, matchings, change, policy) + doWordTest(before!!, after!!, matchings, change, policy) + } TestType.WORD -> doWordTest(before!!, after!!, matchings, change, policy) TestType.CHAR -> doCharTest(before!!, after!!, matchings, change, policy) TestType.SPLITTER -> { @@ -361,6 +373,8 @@ abstract class ComparisonUtilTestBase : DiffTestCase() { internal fun lines(f: TestBuilder.() -> Unit): Unit = doTest(TestType.LINE, f) + internal fun lines_inner(f: TestBuilder.() -> Unit): Unit = doTest(TestType.LINE_INNER, f) + internal fun words(f: TestBuilder.() -> Unit): Unit = doTest(TestType.WORD, f) internal fun chars(f: TestBuilder.() -> Unit): Unit = doTest(TestType.CHAR, f) diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/WordComparisonUtilTest.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/WordComparisonUtilTest.kt index 21407c1afabd..99d224a836de 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/WordComparisonUtilTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/WordComparisonUtilTest.kt @@ -17,72 +17,72 @@ package com.intellij.diff.comparison class WordComparisonUtilTest : ComparisonUtilTestBase() { fun testSimpleCases() { - words { + lines_inner { ("x z" - "y z") ("- " - "- ").default() testAll() } - words { + lines_inner { ("x z" - "y z") ("- " - "- ").default() testAll() } - words { + lines_inner { (" x z" - "y z") ("-- " - "- ").default() (" - " - "- ").trim() testAll() } - words { + lines_inner { ("x z " - "y z") ("- -" - "- ").default() ("- " - "- ").trim() testAll() } - words { + lines_inner { ("x z " - "y z") ("- -" - "- ").default() ("- " - "- ").trim() testAll() } - words { + lines_inner { ("x z" - " y z ") ("- " - "-- -").default() ("- " - " - ").trim() testAll() } - words { + lines_inner { ("x y" - "x z ") (" -" - " --").default() (" -" - " - ").trim() testAll() } - words { + lines_inner { ("x,y" - "x") (" --" - " ").default() testAll() } - words { + lines_inner { ("x,y" - "y") ("-- " - " ").default() testAll() } - words { + lines_inner { (".x=" - ".!=") (" - " - " - ").default() testAll() } - words { + lines_inner { ("X xyz1 Z" - "X xyz2 Z") (" ---- " - " ---- ").default() testAll() @@ -90,52 +90,52 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { } fun testPunctuation() { - words { + lines_inner { (" x.z.x " - "x..x") ("- - -" - " ").default() (" - " - " ").trim() testAll() } - words { + lines_inner { ("x..x" - " x.z.x ") (" " - "- - -").default() (" " - " - ").trim() testAll() } - words { + lines_inner { ("x ... z" - "y ... z") ("- " - "- ").default() testAll() } - words { + lines_inner { ("x ... z" - "x ... y") (" -" - " -").default() testAll() } - words { + lines_inner { ("x ,... z" - "x ... y") (" - -" - " -").default() testAll() } - words { + lines_inner { ("x . , .. z" - "x ... y") (" --- -" - " -").default() (" - -" - " -").ignore() testAll() } - words { + lines_inner { ("x==y==z" - "x====z") (" - " - " ").default() testAll() } - words { + lines_inner { ("x====z" - "x==t==z") (" " - " - ").default() testAll() @@ -143,13 +143,13 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { } fun testOldDiffBug() { - words { + lines_inner { ("x'y'>" - "x'>") (" -- " - " ").default() testAll() } - words { + lines_inner { ("x'>" - "x'y'>") (" " - " -- ").default() testAll() @@ -157,14 +157,14 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { } fun testWhitespaceOnlyChanges() { - words { + lines_inner { ("x =z" - "x= z") (" -- " - " -- ").default() testDefault() testTrim() } - words { + lines_inner { ("x =" - "x= z") (" -- " - " ---").default() (" " - " -").ignore() @@ -173,7 +173,7 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { } fun testNewlines() { - words { + lines_inner { (" x _ y _ z " - "x z") ("- ------ -" - " ").default() (" - " - " ").trim() @@ -181,7 +181,7 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { testAll() } - words { + lines_inner { ("x z" - " x _ y _ z ") (" " - "- ------ -").default() (" " - " - ").trim() @@ -191,7 +191,7 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { } fun testFixedBugs() { - words { + lines_inner { (".! " - ". y!") (" -" - " --- ").default() (" " - " --- ").trim() @@ -199,7 +199,7 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { testAll() } - words { + lines_inner { (" x n" - " y_ x m") (" -" - "---- -").default() (" -" - " - -").trim() @@ -207,7 +207,7 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { testAll() } - words { + lines_inner { ("x_" - "x! ") (" -" - " ---").default() (" " - " - ").trim() @@ -217,35 +217,35 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { } fun testInnerWhitespaces() { - words { + lines_inner { ("<< x >>" - "<.<>.>") (" --- " - " - - ").default() (" - " - " - - ").ignore() testAll() } - words { + lines_inner { ("<< x >>" - "y<>y") (" - - " - "- -").default() (" " - "- -").ignore() testAll() } - words { + lines_inner { ("x .. z" - "x y .. z") (" " - " -- ").default() // TODO: looks wrong (" " - " - ").ignore() testAll() } - words { + lines_inner { (" x..z" - "x..y ") ("-- -" - " ---").default() (" -" - " - ").trim() testAll() } - words { + lines_inner { (" x y x _ x z x " - "x x_x x") ("- -- - - -- -" - " ").default() (" -- -- " - " ").trim() @@ -256,28 +256,28 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { fun testAlgorithmSpecific() { // prefer words over punctuation - words { + lines_inner { ("...x" - "x...") ("--- " - " ---").default() testAll() } // prefer longer words sequences - words { + lines_inner { ("x x y" - "x y") ("-- " - " ").default() ("- " - " ").ignore() testAll() } - words { + lines_inner { ("y x x" - "y x") (" --" - " ").default() (" -" - " ").ignore() testAll() } - words { + lines_inner { ("A X A B" - "A B") ("---- " - " ").default() ("--- " - " ").ignore() @@ -285,14 +285,14 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { } // prefer less modified 'sentences' - words { + lines_inner { ("A.X A.Z" - "A.X A.Y A.Z") (" " - " ---- ").default() (" " - " --- ").ignore() testAll() } - words { + lines_inner { ("X.A Z.A" - "X.A Y.A Z.A") (" " - " ---- ").default() (" " - " --- ").ignore() @@ -300,7 +300,7 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { } // prefer punctuation over whitespaces - words { + lines_inner { (". " - " .") (" ---" - "--- ").default() testDefault() @@ -308,26 +308,26 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { } fun `test legacy cases from ByWordTest`() { - words { + lines_inner { ("abc def, 123" - "ab def, 12") ("--- ---" - "-- --").default() testAll() } - words { + lines_inner { (" a[xy]+1" - ",a[]+1") ("- -- " - "- ").default() (" -- " - "- ").trim() testAll() } - words { + lines_inner { ("0987_ a.g();_" - "yyyy_") ("------------- " - "---- ").default() testAll() } - words { + lines_inner { (" abc_2222_" - " x = abc_zzzz_") //(" ---- " - "-- ---- ---- ").legacy() (" ---- " - " ------ ---- ").default() @@ -335,7 +335,7 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { testAll() } - words { // Idea58505 + lines_inner { // Idea58505 (" if (eventMerger!=null && !dataSelection.getValueIsAdjusting()) {" - " if (eventMerger!=null && (dataSelection==null || !dataSelection.getValueIsAdjusting())) {") //(" - " - @@ -347,7 +347,7 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { testAll() } - words { // Idea56428 + lines_inner { // Idea56428 ("messageInsertStatement = connection.prepareStatement(\"INSERT INTO AUDIT (AUDIT_TYPE_ID, STATUS, SERVER_ID, INSTANCE_ID, REQUEST_ID) VALUES (?, ?, ?, ?, ?)\");" - "messageInsertStatement = connection.prepareStatement(\"INSERT INTO AUDIT (AUDIT_TYPE_ID, CREATION_TIMESTAMP, STATUS, SERVER_ID, INSTANCE_ID, REQUEST_ID) VALUES (?, ?, ?, ?, ?, ?)\");").plainSource() //(" . . " - @@ -359,14 +359,14 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { testAll() } - words { + lines_inner { ("f(a, b);" - "f(a,_ b);") (" " - " -- ").default() (" " - " ").trim() testAll() } - words { + lines_inner { (" o.f(a)" - "o. f( b)") ("- - " - " - -- ").default() (" - " - " - -- ").trim() @@ -374,7 +374,7 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { testAll() } - words { + lines_inner { (" 123 " - "xyz") (" --- " - "---").trim() testTrim() @@ -382,14 +382,14 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { } fun testEmptyRangePositions() { - words { + lines_inner { ("x? y" - "x y") (" - " - " ").default() default(del(1, 1, 1)) testAll() } - words { + lines_inner { ("x ?y" - "x y") (" - " - " ").default() default(del(2, 2, 1)) From de860b754bed185d71be8f7edcd3f1a522c6c984 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Mon, 14 Nov 2016 12:08:09 +0300 Subject: [PATCH 18/33] merge: EA-91241 - assert: DiffUtil.getMergeType * ensure that "conflict" block is not "unchanged" this might've happen for trim/ignore policies * add tests for three-side cases --- .../com/intellij/diff/comparison/ByWord.java | 12 +- .../intellij/diff/comparison/TrimUtil.java | 36 ++ .../tests/com/intellij/diff/DiffTestCase.kt | 49 ++- .../diff/comparison/ComparisonUtilAutoTest.kt | 77 +++-- .../diff/comparison/ComparisonUtilTestBase.kt | 312 ++++++++++++------ .../diff/comparison/MergeResolveUtilTest.kt | 34 +- .../diff/comparison/WordComparisonUtilTest.kt | 33 +- .../comparison/WordMergeComparisonUtilTest.kt | 108 ++++++ 8 files changed, 494 insertions(+), 167 deletions(-) create mode 100644 platform/diff-impl/tests/com/intellij/diff/comparison/WordMergeComparisonUtilTest.kt diff --git a/platform/diff-impl/src/com/intellij/diff/comparison/ByWord.java b/platform/diff-impl/src/com/intellij/diff/comparison/ByWord.java index eec26d3f1e25..a685de3484a9 100644 --- a/platform/diff-impl/src/com/intellij/diff/comparison/ByWord.java +++ b/platform/diff-impl/src/com/intellij/diff/comparison/ByWord.java @@ -600,7 +600,8 @@ public class ByWord { Range expanded = expandW(myText1, myText2, range); Range trimmed = trim(myText1, myText2, expanded); - if (!trimmed.isEmpty()) { + if (!trimmed.isEmpty() && + !isEqualsIW(myText1, myText2, trimmed)) { myChanges.add(trimmed); } } @@ -638,7 +639,8 @@ public class ByWord { MergeRange expanded = expandW(myText1, myText2, myText3, range); MergeRange trimmed = trim(myText1, myText2, myText3, expanded); - if (!trimmed.isEmpty()) { + if (!trimmed.isEmpty() && + !isEqualsIW(myText1, myText2, myText3, trimmed)) { myChanges.add(trimmed); } } @@ -690,7 +692,8 @@ public class ByWord { Range trimmed = new Range(start1, end1, start2, end2); - if (!trimmed.isEmpty()) { + if (!trimmed.isEmpty() && + !isEquals(myText1, myText2, trimmed)) { myChanges.add(trimmed); } } @@ -753,7 +756,8 @@ public class ByWord { MergeRange trimmed = new MergeRange(start1, end1, start2, end2, start3, end3); - if (!trimmed.isEmpty()) { + if (!trimmed.isEmpty() && + !isEquals(myText1, myText2, myText3, trimmed)) { myChanges.add(trimmed); } } diff --git a/platform/diff-impl/src/com/intellij/diff/comparison/TrimUtil.java b/platform/diff-impl/src/com/intellij/diff/comparison/TrimUtil.java index 7914771fad5c..48ed3629fc71 100644 --- a/platform/diff-impl/src/com/intellij/diff/comparison/TrimUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/comparison/TrimUtil.java @@ -384,4 +384,40 @@ public class TrimUtil { public static Range expandIW(@NotNull CharSequence text1, @NotNull CharSequence text2) { return expandIW(text1, text2, 0, 0, text1.length(), text2.length()); } + + // + // Equality + // + + public static boolean isEquals(@NotNull CharSequence text1, @NotNull CharSequence text2, + @NotNull Range range) { + CharSequence sequence1 = text1.subSequence(range.start1, range.end1); + CharSequence sequence2 = text2.subSequence(range.start2, range.end2); + return ComparisonUtil.isEquals(sequence1, sequence2, ComparisonPolicy.DEFAULT); + } + + public static boolean isEqualsIW(@NotNull CharSequence text1, @NotNull CharSequence text2, + @NotNull Range range) { + CharSequence sequence1 = text1.subSequence(range.start1, range.end1); + CharSequence sequence2 = text2.subSequence(range.start2, range.end2); + return ComparisonUtil.isEquals(sequence1, sequence2, ComparisonPolicy.IGNORE_WHITESPACES); + } + + public static boolean isEquals(@NotNull CharSequence text1, @NotNull CharSequence text2, @NotNull CharSequence text3, + @NotNull MergeRange range) { + CharSequence sequence1 = text1.subSequence(range.start1, range.end1); + CharSequence sequence2 = text2.subSequence(range.start2, range.end2); + CharSequence sequence3 = text3.subSequence(range.start3, range.end3); + return ComparisonUtil.isEquals(sequence2, sequence1, ComparisonPolicy.DEFAULT) && + ComparisonUtil.isEquals(sequence2, sequence3, ComparisonPolicy.DEFAULT); + } + + public static boolean isEqualsIW(@NotNull CharSequence text1, @NotNull CharSequence text2, @NotNull CharSequence text3, + @NotNull MergeRange range) { + CharSequence sequence1 = text1.subSequence(range.start1, range.end1); + CharSequence sequence2 = text2.subSequence(range.start2, range.end2); + CharSequence sequence3 = text3.subSequence(range.start3, range.end3); + return ComparisonUtil.isEquals(sequence2, sequence1, ComparisonPolicy.IGNORE_WHITESPACES) && + ComparisonUtil.isEquals(sequence2, sequence3, ComparisonPolicy.IGNORE_WHITESPACES); + } } diff --git a/platform/diff-impl/tests/com/intellij/diff/DiffTestCase.kt b/platform/diff-impl/tests/com/intellij/diff/DiffTestCase.kt index 8145b2a415a7..206d595853b4 100644 --- a/platform/diff-impl/tests/com/intellij/diff/DiffTestCase.kt +++ b/platform/diff-impl/tests/com/intellij/diff/DiffTestCase.kt @@ -21,7 +21,6 @@ import com.intellij.diff.util.ThreeSide import com.intellij.openapi.editor.Document import com.intellij.openapi.progress.DumbProgressIndicator import com.intellij.openapi.progress.ProgressIndicator -import com.intellij.openapi.util.Couple import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.util.text.StringUtil import com.intellij.testFramework.UsefulTestCase @@ -66,6 +65,10 @@ abstract class DiffTestCase : UsefulTestCase() { assertTrue(message, actual) } + fun assertFalse(actual: Boolean, message: String = "") { + assertFalse(message, actual) + } + fun assertEquals(expected: Any?, actual: Any?, message: String = "") { assertEquals(message, expected, actual) } @@ -75,17 +78,33 @@ abstract class DiffTestCase : UsefulTestCase() { } fun assertEqualsCharSequences(chunk1: CharSequence, chunk2: CharSequence, ignoreSpaces: Boolean, skipLastNewline: Boolean) { + if (skipLastNewline && !ignoreSpaces) { + assertTrue(StringUtil.equals(chunk1, chunk2) || + StringUtil.equals(stripNewline(chunk1), chunk2) || + StringUtil.equals(chunk1, stripNewline(chunk2))) + } + else { + assertTrue(isEqualsCharSequences(chunk1, chunk2, ignoreSpaces)) + } + } + + fun assertNotEqualsCharSequences(chunk1: CharSequence, chunk2: CharSequence, ignoreSpaces: Boolean, skipLastNewline: Boolean) { + if (skipLastNewline && !ignoreSpaces) { + assertTrue(!StringUtil.equals(chunk1, chunk2) || + !StringUtil.equals(stripNewline(chunk1), chunk2) || + !StringUtil.equals(chunk1, stripNewline(chunk2))) + } + else { + assertFalse(isEqualsCharSequences(chunk1, chunk2, ignoreSpaces)) + } + } + + fun isEqualsCharSequences(chunk1: CharSequence, chunk2: CharSequence, ignoreSpaces: Boolean): Boolean { if (ignoreSpaces) { - assertTrue(StringUtil.equalsIgnoreWhitespaces(chunk1, chunk2)) - } else { - if (skipLastNewline) { - if (StringUtil.equals(chunk1, chunk2)) return - if (StringUtil.equals(stripNewline(chunk1), chunk2)) return - if (StringUtil.equals(chunk1, stripNewline(chunk2))) return - assertTrue(false) - } else { - assertTrue(StringUtil.equals(chunk1, chunk2)) - } + return StringUtil.equalsIgnoreWhitespaces(chunk1, chunk2) + } + else { + return StringUtil.equals(chunk1, chunk2) } } @@ -202,7 +221,7 @@ abstract class DiffTestCase : UsefulTestCase() { // Helpers // - open class Trio(val data1: T, val data2: T, val data3: T) { + open class Trio(val data1: T, val data2: T, val data3: T) { companion object { fun from(f: (ThreeSide) -> V): Trio = Trio(f(ThreeSide.LEFT), f(ThreeSide.BASE), f(ThreeSide.RIGHT)) } @@ -228,7 +247,11 @@ abstract class DiffTestCase : UsefulTestCase() { } override fun hashCode(): Int { - return data1.hashCode() * 37 * 37 + data2.hashCode() * 37 + data3.hashCode() + var h = 0 + if (data1 != null) h = h * 31 + data1.hashCode() + if (data2 != null) h = h * 31 + data2.hashCode() + if (data3 != null) h = h * 31 + data3.hashCode() + return h } } } \ No newline at end of file diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilAutoTest.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilAutoTest.kt index 4de112cd371b..1422898a7d08 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilAutoTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilAutoTest.kt @@ -26,28 +26,31 @@ import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.util.text.StringUtil class ComparisonUtilAutoTest : DiffTestCase() { + val RUNS = 30 + val MAX_LENGTH = 300 + fun testChar() { - doTestChar(System.currentTimeMillis(), 30, 30) + doTestChar(System.currentTimeMillis(), RUNS, MAX_LENGTH) } fun testWord() { - doTestWord(System.currentTimeMillis(), 30, 300) + doTestWord(System.currentTimeMillis(), RUNS, MAX_LENGTH) } fun testLine() { - doTestLine(System.currentTimeMillis(), 30, 300) + doTestLine(System.currentTimeMillis(), RUNS, MAX_LENGTH) } fun testLineSquashed() { - doTestLineSquashed(System.currentTimeMillis(), 30, 300) + doTestLineSquashed(System.currentTimeMillis(), RUNS, MAX_LENGTH) } fun testLineTrimSquashed() { - doTestLineTrimSquashed(System.currentTimeMillis(), 30, 300) + doTestLineTrimSquashed(System.currentTimeMillis(), RUNS, MAX_LENGTH) } fun testMerge() { - doTestMerge(System.currentTimeMillis(), 30, 300) + doTestMerge(System.currentTimeMillis(), RUNS, MAX_LENGTH) } private fun doTestLine(seed: Long, runs: Int, maxLength: Int) { @@ -141,8 +144,8 @@ class ComparisonUtilAutoTest : DiffTestCase() { val chunk2 = DiffUtil.getLinesContent(text2, f.startLine2, f.endLine2) val chunk3 = DiffUtil.getLinesContent(text3, f.startLine3, f.endLine3) - val wordFragments = ByWord.compare(chunk1, chunk2, chunk3, policy, INDICATOR); - MergeLineFragmentImpl(f, wordFragments); + val wordFragments = ByWord.compare(chunk1, chunk2, chunk3, policy, INDICATOR) + MergeLineFragmentImpl(f, wordFragments) } debugData.put("Fragments", fineFragments) @@ -200,18 +203,18 @@ class ComparisonUtilAutoTest : DiffTestCase() { } } - checkUnchanged(text1.charsSequence, text2.charsSequence, fragments, policy, true) + checkValidRanges(text1.charsSequence, text2.charsSequence, fragments, policy, true) checkCantTrimLines(text1, text2, fragments, policy, allowNonSquashed) } private fun checkResultWord(text1: CharSequence, text2: CharSequence, fragments: List, policy: ComparisonPolicy) { checkDiffConsistency(fragments) - checkUnchanged(text1, text2, fragments, policy, false) + checkValidRanges(text1, text2, fragments, policy, false) } private fun checkResultChar(text1: CharSequence, text2: CharSequence, fragments: List, policy: ComparisonPolicy) { checkDiffConsistency(fragments) - checkUnchanged(text1, text2, fragments, policy, false) + checkValidRanges(text1, text2, fragments, policy, false) } private fun checkResultMerge(text1: Document, text2: Document, text3: Document, fragments: List, policy: ComparisonPolicy) { @@ -223,10 +226,10 @@ class ComparisonUtilAutoTest : DiffTestCase() { val chunk3 = DiffUtil.getLinesContent(text3, f.startLine3, f.endLine3) checkDiffConsistency3(f.innerFragments!!) - checkUnchanged3(chunk1, chunk2, chunk3, f.innerFragments!!, policy) + checkValidRanges3(chunk1, chunk2, chunk3, f.innerFragments!!, policy) } - checkUnchanged3(text1, text2, text3, fragments, policy) + checkValidRanges3(text1, text2, text3, fragments, policy) checkCantTrimLines3(text1, text2, text3, fragments, policy) } @@ -374,28 +377,39 @@ class ComparisonUtilAutoTest : DiffTestCase() { } } - private fun checkUnchanged(text1: CharSequence, text2: CharSequence, fragments: List, policy: ComparisonPolicy, skipNewline: Boolean) { + private fun checkValidRanges(text1: CharSequence, text2: CharSequence, fragments: List, policy: ComparisonPolicy, skipNewline: Boolean) { // TODO: better check for Trim spaces case ? - val ignoreSpaces = policy !== ComparisonPolicy.DEFAULT + val ignoreSpacesUnchanged = policy != ComparisonPolicy.DEFAULT + val ignoreSpacesChanged = policy == ComparisonPolicy.IGNORE_WHITESPACES var last1 = 0 var last2 = 0 for (fragment in fragments) { - val chunk1 = text1.subSequence(last1, fragment.startOffset1) - val chunk2 = text2.subSequence(last2, fragment.startOffset2) + val start1 = fragment.startOffset1 + val start2 = fragment.startOffset2 + val end1 = fragment.endOffset1 + val end2 = fragment.endOffset2 - assertEqualsCharSequences(chunk1, chunk2, ignoreSpaces, skipNewline) + val chunk1 = text1.subSequence(last1, start1) + val chunk2 = text2.subSequence(last2, start2) + assertEqualsCharSequences(chunk1, chunk2, ignoreSpacesUnchanged, skipNewline) + + val chunkContent1 = text1.subSequence(start1, end1) + val chunkContent2 = text2.subSequence(start2, end2) + if (!skipNewline) { + assertNotEqualsCharSequences(chunkContent1, chunkContent2, ignoreSpacesChanged, skipNewline) + } last1 = fragment.endOffset1 last2 = fragment.endOffset2 } val chunk1 = text1.subSequence(last1, text1.length) val chunk2 = text2.subSequence(last2, text2.length) - assertEqualsCharSequences(chunk1, chunk2, ignoreSpaces, skipNewline) + assertEqualsCharSequences(chunk1, chunk2, ignoreSpacesUnchanged, skipNewline) } - private fun checkUnchanged3(text1: Document, text2: Document, text3: Document, fragments: List, policy: ComparisonPolicy) { - val ignoreSpaces = policy !== ComparisonPolicy.DEFAULT + private fun checkValidRanges3(text1: Document, text2: Document, text3: Document, fragments: List, policy: ComparisonPolicy) { + val ignoreSpaces = policy != ComparisonPolicy.DEFAULT var last1 = 0 var last2 = 0 @@ -425,8 +439,9 @@ class ComparisonUtilAutoTest : DiffTestCase() { assertEqualsCharSequences(content2, content3, ignoreSpaces, false) } - private fun checkUnchanged3(text1: CharSequence, text2: CharSequence, text3: CharSequence, fragments: List, policy: ComparisonPolicy) { - val ignoreSpaces = policy !== ComparisonPolicy.DEFAULT + private fun checkValidRanges3(text1: CharSequence, text2: CharSequence, text3: CharSequence, fragments: List, policy: ComparisonPolicy) { + val ignoreSpacesUnchanged = policy != ComparisonPolicy.DEFAULT + val ignoreSpacesChanged = policy == ComparisonPolicy.IGNORE_WHITESPACES var last1 = 0 var last2 = 0 @@ -435,13 +450,21 @@ class ComparisonUtilAutoTest : DiffTestCase() { val start1 = fragment.startOffset1 val start2 = fragment.startOffset2 val start3 = fragment.startOffset3 + val end1 = fragment.endOffset1 + val end2 = fragment.endOffset2 + val end3 = fragment.endOffset3 val content1 = text1.subSequence(last1, start1) val content2 = text2.subSequence(last2, start2) val content3 = text3.subSequence(last3, start3) + assertEqualsCharSequences(content2, content1, ignoreSpacesUnchanged, false) + assertEqualsCharSequences(content2, content3, ignoreSpacesUnchanged, false) - assertEqualsCharSequences(content2, content1, ignoreSpaces, false) - assertEqualsCharSequences(content2, content3, ignoreSpaces, false) + val chunkContent1 = text1.subSequence(start1, end1) + val chunkContent2 = text2.subSequence(start2, end2) + val chunkContent3 = text3.subSequence(start3, end3) + assertFalse(isEqualsCharSequences(chunkContent2, chunkContent1, ignoreSpacesChanged) && + isEqualsCharSequences(chunkContent2, chunkContent3, ignoreSpacesChanged)) last1 = fragment.endOffset1 last2 = fragment.endOffset2 @@ -452,8 +475,8 @@ class ComparisonUtilAutoTest : DiffTestCase() { val content2 = text2.subSequence(last2, text2.length) val content3 = text3.subSequence(last3, text3.length) - assertEqualsCharSequences(content2, content1, ignoreSpaces, false) - assertEqualsCharSequences(content2, content3, ignoreSpaces, false) + assertEqualsCharSequences(content2, content1, ignoreSpacesUnchanged, false) + assertEqualsCharSequences(content2, content3, ignoreSpacesUnchanged, false) } private fun checkCantTrimLines(text1: Document, text2: Document, fragments: List, policy: ComparisonPolicy, allowNonSquashed: Boolean) { diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilTestBase.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilTestBase.kt index ed4e081b9541..ec32651c510b 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilTestBase.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilTestBase.kt @@ -18,6 +18,9 @@ package com.intellij.diff.comparison import com.intellij.diff.DiffTestCase import com.intellij.diff.fragments.DiffFragment import com.intellij.diff.fragments.LineFragment +import com.intellij.diff.fragments.MergeWordFragment +import com.intellij.diff.util.IntPair +import com.intellij.diff.util.ThreeSide import com.intellij.openapi.editor.Document import com.intellij.openapi.editor.impl.DocumentImpl import com.intellij.openapi.util.Couple @@ -25,14 +28,18 @@ import com.intellij.util.containers.ContainerUtil import java.util.* abstract class ComparisonUtilTestBase : DiffTestCase() { - private fun doLineTest(before: Document, after: Document, matchings: Couple?, expected: List?, policy: ComparisonPolicy) { + private fun doLineTest(text: Couple, matchings: Couple?, expected: List>?, policy: ComparisonPolicy) { + val before = text.first + val after = text.second val fragments = MANAGER.compareLines(before.charsSequence, after.charsSequence, policy, INDICATOR) checkConsistency(fragments, before, after) if (matchings != null) checkLineMatching(fragments, matchings) if (expected != null) checkLineChanges(fragments, expected) } - private fun doLineInnerTest(before: Document, after: Document, matchings: Couple?, expected: List?, policy: ComparisonPolicy) { + private fun doLineInnerTest(text: Couple, matchings: Couple?, expected: List>?, policy: ComparisonPolicy) { + val before = text.first + val after = text.second val rawFragments = MANAGER.compareLinesInner(before.charsSequence, after.charsSequence, policy, INDICATOR) val fragments = MANAGER.squash(rawFragments) checkConsistencyLineInner(fragments, before, after) @@ -42,7 +49,9 @@ abstract class ComparisonUtilTestBase : DiffTestCase() { if (expected != null) checkDiffChanges(diffFragments, expected) } - private fun doWordTest(before: Document, after: Document, matchings: Couple?, expected: List?, policy: ComparisonPolicy) { + private fun doWordTest(text: Couple, matchings: Couple?, expected: List>?, policy: ComparisonPolicy) { + val before = text.first + val after = text.second val fragments = MANAGER.compareWords(before.charsSequence, after.charsSequence, policy, INDICATOR) checkConsistency(fragments, before, after) @@ -50,14 +59,33 @@ abstract class ComparisonUtilTestBase : DiffTestCase() { if (expected != null) checkDiffChanges(fragments, expected) } - private fun doCharTest(before: Document, after: Document, matchings: Couple?, expected: List?, policy: ComparisonPolicy) { + private fun doWordTest(text: Trio, matchings: Trio?, expected: List>?, policy: ComparisonPolicy) { + val before = text.data1 + val base = text.data2 + val after = text.data3 + val fragments = ByWord.compare(before.charsSequence, base.charsSequence, after.charsSequence, policy, INDICATOR) + checkConsistency(fragments) + + if (matchings != null) checkMergeMatching(fragments, matchings) + if (expected != null) checkMergeChanges(fragments, expected) + } + + private fun doCharTest(text: Couple, matchings: Couple?, expected: List>?, policy: ComparisonPolicy) { + val before = text.first + val after = text.second val fragments = MANAGER.compareChars(before.charsSequence, after.charsSequence, policy, INDICATOR) checkConsistency(fragments, before, after) if (matchings != null) checkDiffMatching(fragments, matchings) if (expected != null) checkDiffChanges(fragments, expected) } - private fun doSplitterTest(before: Document, after: Document, squash: Boolean, trim: Boolean, expected: List?, policy: ComparisonPolicy) { + private fun doSplitterTest(text: Couple, + squash: Boolean, + trim: Boolean, + expected: List>?, + policy: ComparisonPolicy) { + val before = text.first + val after = text.second val text1 = before.charsSequence val text2 = after.charsSequence @@ -110,16 +138,33 @@ abstract class ComparisonUtilTestBase : DiffTestCase() { } } - private fun checkLineChanges(fragments: List, expected: List) { + private fun checkConsistency(fragments: List) { + for (fragment in fragments) { + assertTrue(fragment.getStartOffset(ThreeSide.LEFT) <= fragment.getEndOffset(ThreeSide.LEFT)) + assertTrue(fragment.getStartOffset(ThreeSide.BASE) <= fragment.getEndOffset(ThreeSide.BASE)) + assertTrue(fragment.getStartOffset(ThreeSide.RIGHT) <= fragment.getEndOffset(ThreeSide.RIGHT)) + + assertTrue(fragment.getStartOffset(ThreeSide.LEFT) != fragment.getEndOffset(ThreeSide.LEFT) || + fragment.getStartOffset(ThreeSide.BASE) != fragment.getEndOffset(ThreeSide.BASE) || + fragment.getStartOffset(ThreeSide.RIGHT) != fragment.getEndOffset(ThreeSide.RIGHT)) + } + } + + private fun checkLineChanges(fragments: List, expected: List>) { val changes = convertLineFragments(fragments) assertOrderedEquals(changes, expected) } - private fun checkDiffChanges(fragments: List, expected: List) { + private fun checkDiffChanges(fragments: List, expected: List>) { val changes = convertDiffFragments(fragments) assertOrderedEquals(changes, expected) } + private fun checkMergeChanges(fragments: List, expected: List>) { + val changes = convertMergeFragments(fragments) + assertOrderedEquals(changes, expected) + } + private fun checkLineMatching(fragments: List, matchings: Couple) { val set1 = BitSet() val set2 = BitSet() @@ -128,8 +173,8 @@ abstract class ComparisonUtilTestBase : DiffTestCase() { set2.set(fragment.startLine2, fragment.endLine2) } - assertEquals(matchings.first, set1) - assertEquals(matchings.second, set2) + assertEquals(matchings.first, set1, "Before") + assertEquals(matchings.second, set2, "After") } private fun checkDiffMatching(fragments: List, matchings: Couple) { @@ -140,16 +185,39 @@ abstract class ComparisonUtilTestBase : DiffTestCase() { set2.set(fragment.startOffset2, fragment.endOffset2) } - assertEquals(matchings.first, set1) - assertEquals(matchings.second, set2) + assertEquals(matchings.first, set1, "Before") + assertEquals(matchings.second, set2, "After") } - private fun convertDiffFragments(fragments: List): List { - return fragments.map { Change(it.startOffset1, it.endOffset1, it.startOffset2, it.endOffset2) } + private fun checkMergeMatching(fragments: List, matchings: Trio) { + val set1 = BitSet() + val set2 = BitSet() + val set3 = BitSet() + for (fragment in fragments) { + set1.set(fragment.getStartOffset(ThreeSide.LEFT), fragment.getEndOffset(ThreeSide.LEFT)) + set2.set(fragment.getStartOffset(ThreeSide.BASE), fragment.getEndOffset(ThreeSide.BASE)) + set3.set(fragment.getStartOffset(ThreeSide.RIGHT), fragment.getEndOffset(ThreeSide.RIGHT)) + } + + assertEquals(matchings.data1, set1, "Before") + assertEquals(matchings.data2, set2, "Base") + assertEquals(matchings.data3, set3, "After") } - private fun convertLineFragments(fragments: List): List { - return fragments.map { Change(it.startLine1, it.endLine1, it.startLine2, it.endLine2) } + private fun convertDiffFragments(fragments: List): List> { + return fragments.map { Couple(IntPair(it.startOffset1, it.endOffset1), IntPair(it.startOffset2, it.endOffset2)) } + } + + private fun convertLineFragments(fragments: List): List> { + return fragments.map { Couple(IntPair(it.startLine1, it.endLine1), IntPair(it.startLine2, it.endLine2)) } + } + + private fun convertMergeFragments(fragments: List): List> { + return fragments.map { + Trio(IntPair(it.getStartOffset(ThreeSide.LEFT), it.getEndOffset(ThreeSide.LEFT)), + IntPair(it.getStartOffset(ThreeSide.BASE), it.getEndOffset(ThreeSide.BASE)), + IntPair(it.getStartOffset(ThreeSide.RIGHT), it.getEndOffset(ThreeSide.RIGHT))) + } } private fun checkLineOffsets(fragment: LineFragment, before: Document, after: Document) { @@ -176,6 +244,37 @@ abstract class ComparisonUtilTestBase : DiffTestCase() { // Test Builder // + private fun parseLineMatching(matching: String, document: Document): BitSet { + assertEquals(matching.length, document.textLength) + + val lines1 = matching.split('_', '*') + val lines2 = document.charsSequence.split('\n') + assertEquals(lines1.size, lines2.size) + for (i in 0..lines1.size - 1) { + assertEquals(lines1[i].length, lines2[i].length, "line $i") + } + + + val set = BitSet() + + var index = 0 + var lineNumber = 0 + while (index < matching.length) { + var end = matching.indexOfAny(listOf("_", "*"), index) + 1 + if (end == 0) end = matching.length + + val line = matching.subSequence(index, end) + if (line.find { it != ' ' && it != '_' } != null) { + assert(!line.contains(' ')) + set.set(lineNumber) + } + lineNumber++ + index = end + } + + return set + } + internal enum class TestType { LINE, LINE_INNER, WORD, CHAR, SPLITTER } @@ -183,32 +282,13 @@ abstract class ComparisonUtilTestBase : DiffTestCase() { internal inner class TestBuilder(private val type: TestType) { private var isExecuted: Boolean = false - private var before: Document? = null - private var after: Document? = null - - private var defaultChanges: List? = null - private var trimChanges: List? = null - private var ignoreChanges: List? = null - - private var defaultMatching: Couple? = null - private var trimMatching: Couple? = null - private var ignoreMatching: Couple? = null + private var text: Data = Data() + private var changes: PolicyData>> = PolicyData() + private var matchings: PolicyData> = PolicyData() private var shouldSquash: Boolean = false private var shouldTrim: Boolean = false - private fun changes(policy: ComparisonPolicy): List? = when (policy) { - ComparisonPolicy.IGNORE_WHITESPACES -> ignoreChanges ?: trimChanges ?: defaultChanges - ComparisonPolicy.TRIM_WHITESPACES -> trimChanges ?: defaultChanges - ComparisonPolicy.DEFAULT -> defaultChanges - } - - private fun matchings(policy: ComparisonPolicy): Couple? = when (policy) { - ComparisonPolicy.IGNORE_WHITESPACES -> ignoreMatching ?: trimMatching ?: defaultMatching - ComparisonPolicy.TRIM_WHITESPACES -> trimMatching ?: defaultMatching - ComparisonPolicy.DEFAULT -> defaultMatching - } - fun assertExecuted() { assertTrue(isExecuted) } @@ -217,21 +297,36 @@ abstract class ComparisonUtilTestBase : DiffTestCase() { try { isExecuted = true - val change = changes(policy) - val matchings = matchings(policy) - assertTrue(change != null || matchings != null) + if (text.isTwoSide()) { + val text = text.asCouple() + val changes = changes.get(policy)?.map { it.asCouple() } + val matchings = matchings.get(policy)?.asCouple() + assertTrue(changes != null || matchings != null) - when (type) { - TestType.LINE -> doLineTest(before!!, after!!, matchings, change, policy) - TestType.LINE_INNER -> { - doLineInnerTest(before!!, after!!, matchings, change, policy) - doWordTest(before!!, after!!, matchings, change, policy) + when (type) { + TestType.LINE -> doLineTest(text, matchings, changes, policy) + TestType.LINE_INNER -> { + doLineInnerTest(text, matchings, changes, policy) + doWordTest(text, matchings, changes, policy) + } + TestType.WORD -> doWordTest(text, matchings, changes, policy) + TestType.CHAR -> doCharTest(text, matchings, changes, policy) + TestType.SPLITTER -> { + assertNull(matchings) + doSplitterTest(text, shouldSquash, shouldTrim, changes, policy) + } + else -> assert(false) } - TestType.WORD -> doWordTest(before!!, after!!, matchings, change, policy) - TestType.CHAR -> doCharTest(before!!, after!!, matchings, change, policy) - TestType.SPLITTER -> { - assertNull(matchings) - doSplitterTest(before!!, after!!, shouldSquash, shouldTrim, change, policy) + } + else { + val text = text.asTrio() + val changes = changes.get(policy)?.map { it.asTrio() } + val matchings = matchings.get(policy)?.asTrio() + assertTrue(changes != null || matchings != null) + + when (type) { + TestType.WORD -> doWordTest(text, matchings, changes, policy) + else -> assert(false) } } } @@ -266,102 +361,84 @@ abstract class ComparisonUtilTestBase : DiffTestCase() { return Helper(this, v) } - inner class Helper(val before: String, val after: String) { + operator fun Helper.minus(v: String): Helper { + return Helper(before, v, after) + } + + inner class Helper(val before: String, val after: String, val base: String? = null) { init { val builder = this@TestBuilder - if (builder.before == null && builder.after == null) { - builder.before = DocumentImpl(parseSource(before)) - builder.after = DocumentImpl(parseSource(after)) + if (builder.text.before == null && builder.text.after == null || + base != null && builder.text.base == null) { + builder.text.before = DocumentImpl(parseSource(before)) + builder.text.after = DocumentImpl(parseSource(after)) + if (base != null) builder.text.base = DocumentImpl(parseSource(base)) } } fun plainSource() { val builder = this@TestBuilder - builder.before = DocumentImpl(before) - builder.after = DocumentImpl(after) + builder.text.before = DocumentImpl(before) + builder.text.after = DocumentImpl(after) + if (base != null) { + builder.text.base = DocumentImpl(base) + } } fun default() { - defaultMatching = parseMatching(before, after) + matchings.default = parseMatching(before, after, base) } fun trim() { - trimMatching = parseMatching(before, after) + matchings.trim = parseMatching(before, after, base) } fun ignore() { - ignoreMatching = parseMatching(before, after) + matchings.ignore = parseMatching(before, after, base) } - private fun parseMatching(before: String, after: String): Couple { + private fun parseMatching(before: String, after: String, base: String?): Data { if (type == TestType.LINE) { val builder = this@TestBuilder - return Couple.of(parseLineMatching(before, builder.before!!), parseLineMatching(after, builder.after!!)) + return Data(parseLineMatching(before, builder.text.before!!), + if (base != null) parseLineMatching(base, builder.text.base!!) else null, + parseLineMatching(after, builder.text.after!!)) } else { - return Couple.of(parseMatching(before), parseMatching(after)) + return Data(parseMatching(before), + if (base != null) parseMatching(base) else null, + parseMatching(after)) } } - - fun parseLineMatching(matching: String, document: Document): BitSet { - assertEquals(matching.length, document.textLength) - - val lines1 = matching.split('_', '*') - val lines2 = document.charsSequence.split('\n') - assertEquals(lines1.size, lines2.size) - for (i in 0..lines1.size - 1) { - assertEquals(lines1[i].length, lines2[i].length, "line $i") - } - - - val set = BitSet() - - var index = 0 - var lineNumber = 0 - while (index < matching.length) { - var end = matching.indexOfAny(listOf("_", "*"), index) + 1 - if (end == 0) end = matching.length - - val line = matching.subSequence(index, end) - if (line.find { it != ' ' && it != '_' } != null) { - assert(!line.contains(' ')) - set.set(lineNumber) - } - lineNumber++ - index = end - } - - return set - } } - fun default(vararg expected: Change): Unit { - defaultChanges = ContainerUtil.list(*expected) + fun default(vararg expected: Couple): Unit { + changes.default = ContainerUtil.list(*expected).map { Data(it.first, it.second) } } - fun trim(vararg expected: Change): Unit { - trimChanges = ContainerUtil.list(*expected) + fun trim(vararg expected: Couple): Unit { + changes.trim = ContainerUtil.list(*expected).map { Data(it.first, it.second) } } - fun ignore(vararg expected: Change): Unit { - ignoreChanges = ContainerUtil.list(*expected) + fun ignore(vararg expected: Couple): Unit { + changes.ignore = ContainerUtil.list(*expected).map { Data(it.first, it.second) } } - fun mod(line1: Int, line2: Int, count1: Int, count2: Int): Change { + fun mod(line1: Int, line2: Int, count1: Int, count2: Int): Couple { assert(count1 != 0) assert(count2 != 0) - return Change(line1, line1 + count1, line2, line2 + count2) + return Couple(IntPair(line1, line1 + count1), IntPair(line2, line2 + count2)) } - fun del(line1: Int, line2: Int, count1: Int): Change { + fun del(line1: Int, line2: Int, count1: Int): Couple { assert(count1 != 0) - return Change(line1, line1 + count1, line2, line2) + return Couple(IntPair(line1, line1 + count1), IntPair(line2, line2)) } - fun ins(line1: Int, line2: Int, count2: Int): Change { + fun ins(line1: Int, line2: Int, count2: Int): Couple { assert(count2 != 0) - return Change(line1, line1, line2, line2 + count2) + return Couple(IntPair(line1, line1), IntPair(line2, line2 + count2)) } @@ -396,9 +473,28 @@ abstract class ComparisonUtilTestBase : DiffTestCase() { // Helpers // - data class Change(val start1: Int, val end1: Int, val start2: Int, val end2: Int) { - override fun toString(): String { - return "($start1, $end1) - ($start2, $end2)" + private data class Data(var before: T?, var base: T?, var after: T?) { + constructor() : this(null, null, null) + constructor(before: T?, after : T?) : this(before, null, after) + fun isTwoSide(): Boolean = before != null && after != null && base == null + fun isThreeSide(): Boolean = before != null && after != null && base != null + fun asCouple(): Couple { + assert(isTwoSide()) + return Couple(before!!, after!!) + } + + fun asTrio(): Trio { + assert(isThreeSide()) + return Trio(before!!, base!!, after!!) } } + + private data class PolicyData(var default: T? = null, var trim: T? = null, var ignore: T? = null) { + fun get(policy: ComparisonPolicy): T? = + when (policy) { + ComparisonPolicy.IGNORE_WHITESPACES -> ignore ?: trim ?: default + ComparisonPolicy.TRIM_WHITESPACES -> trim ?: default + ComparisonPolicy.DEFAULT -> default + } + } } diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/MergeResolveUtilTest.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/MergeResolveUtilTest.kt index c5c299bd593a..ef427342cbdb 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/MergeResolveUtilTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/MergeResolveUtilTest.kt @@ -187,25 +187,31 @@ class MergeResolveUtilTest : DiffTestCase() { ) } + fun testRegressions() { + test( + "i\n", + "i", + "\ni", + "i\n", + "i" + ) + } + private fun testGreedy(base: String, left: String, right: String, expected: String?) { test(base, left, right, expected, true); } private fun test(base: String, left: String, right: String, expected: String?, isGreedy: Boolean = false) { - val simpleResult = MergeResolveUtil.tryResolve(left, base, right) - val magicResult = MergeResolveUtil.tryGreedyResolve(left, base, right); + val expectedSimple = if (isGreedy) null else expected + val expectedGreedy = expected + test(base, left, right, expectedSimple, expectedGreedy) + } - if (expected == null) { - assertNull(simpleResult) - assertNull(magicResult) - } - else if (isGreedy) { - assertNull(simpleResult) - assertEquals(expected, magicResult) - } - else { - assertEquals(expected, simpleResult) - assertEquals(expected, magicResult) - } + private fun test(base: String, left: String, right: String, expectedSimple: String?, expectedGreedy: String?) { + val simpleResult = MergeResolveUtil.tryResolve(left, base, right) + val greedyResult = MergeResolveUtil.tryGreedyResolve(left, base, right); + + assertEquals(expectedSimple, simpleResult, "Simple") + assertEquals(expectedGreedy, greedyResult, "Greedy") } } diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/WordComparisonUtilTest.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/WordComparisonUtilTest.kt index 99d224a836de..c6c1e115a1ed 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/WordComparisonUtilTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/WordComparisonUtilTest.kt @@ -188,6 +188,37 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { (" " - " - ").ignore() testAll() } + + words { + ("_i" - "i_") + ("- " - " -").default() + (" " - " ").trim() + testAll() + } + + words { + ("i_" - "_i") + ("- " - " -").default() // TODO + testAll() + } + + words { + ("x_y" - "xy") + (" " - " ").ignore() + testIgnore() + } + + words { + ("A x_y B" - "a xy b") + ("-------" - "------").ignore() + testIgnore() + } + + words { + ("A xy B" - "a xy b") + ("- -" - "- -").ignore() + testIgnore() + } } fun testFixedBugs() { @@ -233,7 +264,7 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { lines_inner { ("x .. z" - "x y .. z") - (" " - " -- ").default() // TODO: looks wrong + (" " - " -- ").default() (" " - " - ").ignore() testAll() } diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/WordMergeComparisonUtilTest.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/WordMergeComparisonUtilTest.kt new file mode 100644 index 000000000000..9268734d7b8f --- /dev/null +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/WordMergeComparisonUtilTest.kt @@ -0,0 +1,108 @@ +/* + * 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.diff.comparison + +class WordMergeComparisonUtilTest : ComparisonUtilTestBase() { + fun testSimple() { + words { + ("" - "" - "") + ("" - "" - "").default() + testAll() + } + + words { + ("" - "X" - "") + ("" - "-" - "").default() + testAll() + } + + words { + ("X" - "" - "") + ("-" - "" - "").default() + testAll() + } + + words { + ("a b" - "a b" - "a b") + (" " - " " - " ").default() + testAll() + } + + words { + ("A b c" - "a b c" - "a b C") + ("- -" - "- -" - "- -").default() + testAll() + } + + words { + ("a c" - "a c" - "a X c") + (" " - " " - " -- ").default() + (" " - " " - " - ").ignore() + testAll() + } + + words { + ("a X c" - "a X c" - "a c") + (" -- " - " -- " - " ").default() + (" - " - " - " - " ").ignore() + testAll() + } + + words { + ("a X c" - "a c" - "a Y c") + (" -- " - " " - " -- ").default() + (" - " - " " - " - ").ignore() + testAll() + } + + words { + ("a c" - "a X c" - "a Y c") + (" " - " -- " - " -- ").default() + (" " - " - " - " - ").ignore() + testAll() + } + } + + fun testNewlines() { + words { + ("i" - "i_" - "_i") + ("-" - "--" - "--").default() // TODO + (" " - " " - " ").trim() + testAll() + } + + words { + ("_i" - "i_" - "i") + ("--" - "--" - "-").default() + (" " - " " - " ").trim() + testAll() + } + + words { + ("i" - "_i" - "i_") + (" " - "- " - " -").default() + (" " - " " - " ").trim() + testAll() + } + + words { + ("_i" - "i" - "i_") + ("- " - " " - " -").default() + (" " - " " - " ").trim() + testAll() + } + } +} \ No newline at end of file From 5208b16c1d929d0349c8ad03e4c01015f0ea1ddd Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Mon, 14 Nov 2016 15:42:52 +0300 Subject: [PATCH 19/33] diff: cleanup tests --- .../tests/com/intellij/diff/DiffTestCase.kt | 6 +++--- .../diff/comparison/ComparisonUtilAutoTest.kt | 6 +----- .../intellij/diff/comparison/MergeResolveUtilTest.kt | 4 ++-- .../tests/com/intellij/diff/merge/MergeAutoTest.kt | 2 +- .../tests/com/intellij/diff/merge/MergeTestBase.kt | 12 ++++++------ .../fragmented/LineNumberConvertorCorrectorTest.kt | 1 + .../fragmented/UnifiedFragmentBuilderAutoTest.kt | 4 ++-- 7 files changed, 16 insertions(+), 19 deletions(-) diff --git a/platform/diff-impl/tests/com/intellij/diff/DiffTestCase.kt b/platform/diff-impl/tests/com/intellij/diff/DiffTestCase.kt index 206d595853b4..2172ab6d32de 100644 --- a/platform/diff-impl/tests/com/intellij/diff/DiffTestCase.kt +++ b/platform/diff-impl/tests/com/intellij/diff/DiffTestCase.kt @@ -223,12 +223,12 @@ abstract class DiffTestCase : UsefulTestCase() { open class Trio(val data1: T, val data2: T, val data3: T) { companion object { - fun from(f: (ThreeSide) -> V): Trio = Trio(f(ThreeSide.LEFT), f(ThreeSide.BASE), f(ThreeSide.RIGHT)) + fun from(f: (ThreeSide) -> V): Trio = Trio(f(ThreeSide.LEFT), f(ThreeSide.BASE), f(ThreeSide.RIGHT)) } - fun map(f: (T) -> V): Trio = Trio(f(data1), f(data2), f(data3)) + fun map(f: (T) -> V): Trio = Trio(f(data1), f(data2), f(data3)) - fun map(f: (T, ThreeSide) -> V): Trio = Trio(f(data1, ThreeSide.LEFT), f(data2, ThreeSide.BASE), f(data3, ThreeSide.RIGHT)) + fun map(f: (T, ThreeSide) -> V): Trio = Trio(f(data1, ThreeSide.LEFT), f(data2, ThreeSide.BASE), f(data3, ThreeSide.RIGHT)) fun forEach(f: (T, ThreeSide) -> Unit): Unit { f(data1, ThreeSide.LEFT) diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilAutoTest.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilAutoTest.kt index 1422898a7d08..f46fc7f5ea35 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilAutoTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilAutoTest.kt @@ -514,11 +514,7 @@ class ComparisonUtilAutoTest : DiffTestCase() { } private fun countNonWhitespaceCharacters(line: CharSequence): Int { - var count = 0 - for (i in 0 until line.length) { - if (!StringUtil.isWhiteSpace(line[i])) count++ - } - return count + return (0 until line.length).count { !StringUtil.isWhiteSpace(line[it]) } } private fun getFirstLastLines(text: Document, start: Int, end: Int): Couple? { diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/MergeResolveUtilTest.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/MergeResolveUtilTest.kt index ef427342cbdb..17511441264e 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/MergeResolveUtilTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/MergeResolveUtilTest.kt @@ -198,7 +198,7 @@ class MergeResolveUtilTest : DiffTestCase() { } private fun testGreedy(base: String, left: String, right: String, expected: String?) { - test(base, left, right, expected, true); + test(base, left, right, expected, true) } private fun test(base: String, left: String, right: String, expected: String?, isGreedy: Boolean = false) { @@ -209,7 +209,7 @@ class MergeResolveUtilTest : DiffTestCase() { private fun test(base: String, left: String, right: String, expectedSimple: String?, expectedGreedy: String?) { val simpleResult = MergeResolveUtil.tryResolve(left, base, right) - val greedyResult = MergeResolveUtil.tryGreedyResolve(left, base, right); + val greedyResult = MergeResolveUtil.tryGreedyResolve(left, base, right) assertEquals(expectedSimple, simpleResult, "Simple") assertEquals(expectedGreedy, greedyResult, "Greedy") diff --git a/platform/diff-impl/tests/com/intellij/diff/merge/MergeAutoTest.kt b/platform/diff-impl/tests/com/intellij/diff/merge/MergeAutoTest.kt index 9002b6adf776..cf09a556ec03 100644 --- a/platform/diff-impl/tests/com/intellij/diff/merge/MergeAutoTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/merge/MergeAutoTest.kt @@ -32,7 +32,7 @@ class MergeAutoTest : MergeTestBase() { private fun doUndoTest(seed: Long, runs: Int, maxLength: Int) { doTest(seed, runs, maxLength) { text1, text2, text3, debugData -> testN(text1, text2, text3) { - if (changes.size == 0) { + if (changes.isEmpty()) { assertEquals(text1, text2) assertEquals(text1, text3) assertEquals(text2, text3) diff --git a/platform/diff-impl/tests/com/intellij/diff/merge/MergeTestBase.kt b/platform/diff-impl/tests/com/intellij/diff/merge/MergeTestBase.kt index 132870446311..aa12d8eea670 100644 --- a/platform/diff-impl/tests/com/intellij/diff/merge/MergeTestBase.kt +++ b/platform/diff-impl/tests/com/intellij/diff/merge/MergeTestBase.kt @@ -99,7 +99,7 @@ abstract class MergeTestBase : DiffTestCase() { val editor: EditorEx = viewer.editor val document: Document = editor.document - private val textEditor = TextEditorProvider.getInstance().getTextEditor(editor); + private val textEditor = TextEditorProvider.getInstance().getTextEditor(editor) private val undoManager = UndoManager.getInstance(project!!) fun change(num: Int): TextMergeChange { @@ -114,7 +114,7 @@ abstract class MergeTestBase : DiffTestCase() { // fun runActionByTitle(name: String): Boolean { - val action = actions.filter { name.equals(it.templatePresentation.text) } + val action = actions.filter { name == it.templatePresentation.text } assertTrue(action.size == 1, action.toString()) return runAction(action[0]) } @@ -396,7 +396,7 @@ abstract class MergeTestBase : DiffTestCase() { if (other !is ViewerState) return false if (!StringUtil.equals(content, other.content)) return false - if (!changes.equals(other.changes)) return false + if (changes != other.changes) return false return true } @@ -411,9 +411,9 @@ abstract class MergeTestBase : DiffTestCase() { if (other !is ChangeState) return false if (!StringUtil.equals(content, other.content)) return false - if (!starts.equals(other.starts)) return false - if (!ends.equals(other.ends)) return false - if (!resolved.equals(other.resolved)) return false + if (starts != other.starts) return false + if (ends != other.ends) return false + if (resolved != other.resolved) return false return true } diff --git a/platform/diff-impl/tests/com/intellij/diff/tools/fragmented/LineNumberConvertorCorrectorTest.kt b/platform/diff-impl/tests/com/intellij/diff/tools/fragmented/LineNumberConvertorCorrectorTest.kt index 14b14fe41e93..82d67e94e224 100644 --- a/platform/diff-impl/tests/com/intellij/diff/tools/fragmented/LineNumberConvertorCorrectorTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/tools/fragmented/LineNumberConvertorCorrectorTest.kt @@ -175,6 +175,7 @@ class LineNumberConvertorCorrectorTest : UsefulTestCase() { assertEquals(minimumMatched2, counter2) } + @Suppress("unused") fun printMatchings() { for (i in 0..length * 2 - 1) { val value = convertor.convert1(i) diff --git a/platform/diff-impl/tests/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilderAutoTest.kt b/platform/diff-impl/tests/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilderAutoTest.kt index 3d8dea4609b4..6f729d03a726 100644 --- a/platform/diff-impl/tests/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilderAutoTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilderAutoTest.kt @@ -34,8 +34,8 @@ class UnifiedFragmentBuilderAutoTest : DiffTestCase() { doAutoTest(seed, runs) { debugData -> debugData.put("MaxLength", maxLength) - var text1 = DocumentImpl(generateText(maxLength)) - var text2 = DocumentImpl(generateText(maxLength)) + val text1 = DocumentImpl(generateText(maxLength)) + val text2 = DocumentImpl(generateText(maxLength)) debugData.put("Text1", textToReadableFormat(text1.charsSequence)) debugData.put("Text2", textToReadableFormat(text2.charsSequence)) From d32718a288ef2d4de9951980e345e6d27d742f9c Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Mon, 14 Nov 2016 15:57:39 +0300 Subject: [PATCH 20/33] IDEA-163033 ui: fix plugin update dialog undex HiDPI --- .../openapi/updateSettings/impl/PluginUpdateInfoPanel.form | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginUpdateInfoPanel.form b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginUpdateInfoPanel.form index 0e86badb08c5..0408bfaeabc8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginUpdateInfoPanel.form +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/PluginUpdateInfoPanel.form @@ -2,7 +2,7 @@
- + @@ -13,7 +13,6 @@ - From 3153722b84e34d64f6c011f7dcde18ca136ca3f5 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Mon, 14 Nov 2016 15:46:34 +0300 Subject: [PATCH 21/33] [Parameter Name Hints] added diagnostic --- .../codeInsight/daemon/inlays/InlayParameterHintsTest.kt | 6 +++--- .../codeInsight/daemon/inlays/JavaParameterNameHintsTest.kt | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/InlayParameterHintsTest.kt b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/InlayParameterHintsTest.kt index ceba1745faa7..d60c2a6dd8b1 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/InlayParameterHintsTest.kt +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/InlayParameterHintsTest.kt @@ -88,7 +88,8 @@ class InlayAssert(private val file: PsiFile, val inlays: List) { val hintOffsets = hints.map { it.first } val hintNames = hints.map { it.second } - assertThat(hints.size).isEqualTo(expectedInlays.size) + val elements = hintOffsets.mapNotNull { file.findElementAt(it) } + assertThat(hints.size).isEqualTo(expectedInlays.size).withFailMessage("Element at offsets: ${elements.joinToString(", ")}") val expect = expectedInlays.map { it.substringBefore("->") to it.substringAfter("->") } val expectedHintNames = expect.map { it.first } @@ -96,8 +97,7 @@ class InlayAssert(private val file: PsiFile, val inlays: List) { assertThat(hintNames).isEqualTo(expectedHintNames) - val wordsAfter = hintOffsets.mapNotNull { file.findElementAt(it) }.map { it.text } - + val wordsAfter = elements.map { it.text } assertThat(wordsAfter).isEqualTo(expectedWordsAfter) } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/JavaParameterNameHintsTest.kt b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/JavaParameterNameHintsTest.kt index d695a36345be..ff2227789255 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/JavaParameterNameHintsTest.kt +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/JavaParameterNameHintsTest.kt @@ -708,13 +708,16 @@ class Key { setup(""" class Test { void test() { + xxx(100); check(1 + 1); int i=1; check(1 + 1 + 1); } void check(int isShow) {} + void xxx(int followTheSum) {} } """) - + + onLineStartingWith("xxx").assertInlays("followTheSum->100") onLineStartingWith("check").assertInlays("isShow->1") onLineStartingWith("int").assertInlays("isShow->1") } From b7790b86749dfc4b5fbb4cd735931f2a2c175a88 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Mon, 14 Nov 2016 14:48:59 +0100 Subject: [PATCH 22/33] [tests] comments --- .../testSrc/com/intellij/ide/updates/UpdateStrategyTest.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateStrategyTest.kt b/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateStrategyTest.kt index a3c7e4764dde..bf3e0a1d0e0d 100644 --- a/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateStrategyTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/ide/updates/UpdateStrategyTest.kt @@ -23,6 +23,7 @@ import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertNull +// unless stated otherwise, the behavior described in cases is true for 162+ class UpdateStrategyTest { @Test fun `channel contains no builds`() { val result = check("IU-145.258", ChannelStatus.RELEASE, """""") @@ -150,6 +151,7 @@ class UpdateStrategyTest { assertBuild("143.2332", result.newBuild) } + // since 163 @Test fun `updates from the same baseline are preferred (per-release channels)`() { val result = check("IU-143.2287", ChannelStatus.EAP, """ From b8b17786ae4b3b19183323f454e1c43c6a29bfcf Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Mon, 14 Nov 2016 15:13:27 +0100 Subject: [PATCH 23/33] Fix saving the shell path project setting (IDEA-163474) --- .../TerminalProjectOptionsProvider.kt | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalProjectOptionsProvider.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalProjectOptionsProvider.kt index ea080be20ace..1e9ca678be60 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalProjectOptionsProvider.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalProjectOptionsProvider.kt @@ -24,7 +24,7 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ProjectRootManager import com.intellij.openapi.util.SystemInfo import java.io.File -import kotlin.reflect.KProperty +import kotlin.reflect.* /** * @author traff @@ -39,18 +39,19 @@ class TerminalProjectOptionsProvider(private val myProject: Project) : Persisten } override fun loadState(state: State) { - shellPath = state.myShellPath + myState.myShellPath = state.myShellPath myState.myStartingDirectory = state.myStartingDirectory } class State { var myShellPath: String? = null + var myStartingDirectory: String? = null } - var shellPath: String? by ValueWithDefault { defaultShellPath } + var shellPath: String? by ValueWithDefault(State::myShellPath, myState) { defaultShellPath } - var startingDirectory: String? by ValueWithDefault { defaultStartingDirectory } + var startingDirectory: String? by ValueWithDefault(State::myStartingDirectory, myState) { defaultStartingDirectory } val defaultStartingDirectory: String? get() { @@ -114,15 +115,15 @@ class TerminalProjectOptionsProvider(private val myProject: Project) : Persisten } -class ValueWithDefault(val default: () -> String?) { - private var _value: String? = null - +// TODO: In Kotlin 1.1 it will be possible to pass references to instance properties. Until then we need 'state' argument as a reciever for +// to property to apply +class ValueWithDefault(val prop: KMutableProperty1, val state: TerminalProjectOptionsProvider.State, val default: () -> String?) { operator fun getValue(thisRef: Any?, property: KProperty<*>): String? { - return if (_value !== null) _value else default() + return if (prop.get(state) !== null) prop.get(state) else default() } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String?) { - _value = if (value == default() || value.isNullOrEmpty()) null else value + prop.set(state, if (value == default() || value.isNullOrEmpty()) null else value) } } From 60d6743debfee75536a259c287a1bd55cf5ccf41 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Mon, 14 Nov 2016 17:23:14 +0300 Subject: [PATCH 24/33] cancel emulation in progress --- .../debugger/ui/breakpoints/MethodBreakpoint.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java index 509e119d1086..85067752fc01 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java @@ -150,14 +150,17 @@ public class MethodBreakpoint extends BreakpointWithHighlighter indicatorRef = new AtomicReference<>(); - ApplicationManager.getApplication().invokeAndWait(() -> indicatorRef.set(new ProgressWindowWithNotification(true, myProject))); + ApplicationManager.getApplication() + .invokeAndWait(() -> indicatorRef.set(new ProgressWindowWithNotification(true, false, myProject, "Cancel emulation"))); ProgressIndicator indicator = indicatorRef.get(); ProgressManager.getInstance().executeProcessUnderProgress( () -> processPreparedSubTypes(baseType, subType -> createRequestForPreparedClassEmulated(debugProcess, subType, false), indicator), indicator); if (indicator.isCanceled()) { - ApplicationManager.getApplication().invokeLater( - () -> DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().removeBreakpoint(this)); + ApplicationManager.getApplication().invokeLater(() -> { + getProperties().EMULATED = false; + fireBreakpointChanged(); + }); } } From c5467b78cdcd35e4e4541e3e2f274743e0d449fb Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Mon, 14 Nov 2016 16:31:01 +0300 Subject: [PATCH 25/33] [Parameter Name Hints] missed part of previous diagnostic commit --- .../daemon/inlays/InlayParameterHintsTest.kt | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/InlayParameterHintsTest.kt b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/InlayParameterHintsTest.kt index d60c2a6dd8b1..186c008816d3 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/InlayParameterHintsTest.kt +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/inlays/InlayParameterHintsTest.kt @@ -20,7 +20,9 @@ import com.intellij.codeInsight.hints.settings.ParameterNameHintsSettings import com.intellij.openapi.editor.Inlay import com.intellij.openapi.editor.ex.EditorSettingsExternalizable import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiFile +import com.intellij.psi.impl.DebugUtil import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase import com.intellij.util.DocumentUtil import org.assertj.core.api.Assertions.assertThat @@ -89,7 +91,12 @@ class InlayAssert(private val file: PsiFile, val inlays: List) { val hintNames = hints.map { it.second } val elements = hintOffsets.mapNotNull { file.findElementAt(it) } - assertThat(hints.size).isEqualTo(expectedInlays.size).withFailMessage("Element at offsets: ${elements.joinToString(", ")}") + assertThat(hints.size) + .withFailMessage("Expected ${expectedInlays.size} elements with hints, Actual elements count ${hints.size}" + + ": ${elements.joinToString(", ")}, file text: \n\n ${file.text} \n\n isCommitted ${isCommitted(file)} \n\n" + + "Psi: \n ${DebugUtil.psiToString(file, true)}") + + .isEqualTo(expectedInlays.size) val expect = expectedInlays.map { it.substringBefore("->") to it.substringAfter("->") } val expectedHintNames = expect.map { it.first } @@ -101,4 +108,12 @@ class InlayAssert(private val file: PsiFile, val inlays: List) { assertThat(wordsAfter).isEqualTo(expectedWordsAfter) } + private fun isCommitted(file: PsiFile): Boolean { + val manager = PsiDocumentManager.getInstance(file.project) + val document = manager.getDocument(file) + + assertThat(document).isNotNull() + return manager.isCommitted(document!!) + } + } \ No newline at end of file From 683716ac352f056e26cc2e0cefc1eb210f0e6208 Mon Sep 17 00:00:00 2001 From: Dennis Ushakov Date: Mon, 14 Nov 2016 17:57:16 +0300 Subject: [PATCH 26/33] validate console components to make sure layered pane gets proper size from editor (WEB-12508) --- .../intellij/execution/console/LanguageConsoleBuilder.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleBuilder.java b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleBuilder.java index c7da1cea0b20..f1f77a5e8a0e 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleBuilder.java +++ b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleBuilder.java @@ -326,6 +326,12 @@ public final class LanguageConsoleBuilder { super.dispose(); } + @Override + public void scrollToEnd() { + getComponent().validate(); + super.scrollToEnd(); + } + private final class GutterUpdateScheduler extends DocumentAdapter implements DocumentBulkUpdateListener { private final ConsoleGutterComponent lineStartGutter; private final ConsoleGutterComponent lineEndGutter; From d3d435d617f99198dff3d6226738ea90d4fa5627 Mon Sep 17 00:00:00 2001 From: Elizaveta Shashkova Date: Mon, 14 Nov 2016 18:19:43 +0300 Subject: [PATCH 27/33] Fix script for tests in case if the directory exists, but it's empty --- python/setup-test-environment/build.gradle | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/python/setup-test-environment/build.gradle b/python/setup-test-environment/build.gradle index 6b04e96b8d4e..2d1523c477f4 100644 --- a/python/setup-test-environment/build.gradle +++ b/python/setup-test-environment/build.gradle @@ -18,8 +18,8 @@ envs { conda "django18", "2.7", ["django==1.6"], false textfile "django18/tags.txt", "django" -// conda "django17", "2.7", ["django==1.7"], false -// textfile "django17/tags.txt", "django\nskeletons" + conda "django17", "2.7", ["django==1.7"], false + textfile "django17/tags.txt", "django\nskeletons" conda "python34", "3.4", ["ipython==2.1", "django==1.6", "behave", "jinja2", "tox>=2.0", "pandas"], true textfile "python34/tags.txt", "python3\nipython\nipython200\nskeletons\ndjango\nbehave\ntox\njinja2\npython34\npackaging\npandas" @@ -43,8 +43,12 @@ if (new File(envs.envsDirectory, "django_latest").exists() && delete new File(envs.envsDirectory, "django_latest") } -if (System.getenv("NO_CLEAN") == null && envs.envsDirectory.exists() && - envs.envsDirectory.lastModified() Date: Mon, 14 Nov 2016 17:48:18 +0300 Subject: [PATCH 28/33] test breakpoints code cleanup --- .../ui/breakpoints/BreakpointManager.java | 2 +- .../ExecutionWithDebuggerToolsTestCase.java | 22 +++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManager.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManager.java index 092538a3d789..c50ad2d05ffb 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManager.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManager.java @@ -245,7 +245,7 @@ public class BreakpointManager { return null; } - @NotNull + @Nullable public ExceptionBreakpoint addExceptionBreakpoint(@NotNull final String exceptionClassName, final String packageName) { ApplicationManager.getApplication().assertIsDispatchThread(); final JavaExceptionBreakpointType type = XDebuggerUtil.getInstance().findBreakpointType(JavaExceptionBreakpointType.class); diff --git a/java/testFramework/src/com/intellij/debugger/ExecutionWithDebuggerToolsTestCase.java b/java/testFramework/src/com/intellij/debugger/ExecutionWithDebuggerToolsTestCase.java index bbc8d7000597..2c2bfe9ec2f6 100644 --- a/java/testFramework/src/com/intellij/debugger/ExecutionWithDebuggerToolsTestCase.java +++ b/java/testFramework/src/com/intellij/debugger/ExecutionWithDebuggerToolsTestCase.java @@ -21,7 +21,6 @@ import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.TextWithImportsImpl; import com.intellij.debugger.engine.events.DebuggerCommandImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; -import com.intellij.debugger.impl.DebuggerManagerImpl; import com.intellij.debugger.impl.PositionUtil; import com.intellij.debugger.impl.PrioritizedTask; import com.intellij.debugger.impl.SynchronizationBasedSemaphore; @@ -47,7 +46,6 @@ import com.intellij.util.SmartList; import com.intellij.util.TimeoutUtil; import com.intellij.util.lang.CompoundRuntimeException; import com.intellij.util.ui.UIUtil; -import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.sun.jdi.Method; import com.sun.jdi.ThreadReference; @@ -379,33 +377,34 @@ public abstract class ExecutionWithDebuggerToolsTestCase extends ExecutionTestCa public void createBreakpoints(final PsiFile file) { Runnable runnable = () -> { - BreakpointManager breakpointManager = DebuggerManagerImpl.getInstanceEx(myProject).getBreakpointManager(); + BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager(); Document document = PsiDocumentManager.getInstance(myProject).getDocument(file); + String text = document.getText(); int offset = -1; - for (; ;) { - offset = document.getText().indexOf("Breakpoint!", offset + 1); + while (true) { + offset = text.indexOf("Breakpoint!", offset + 1); if (offset == -1) break; int commentLine = document.getLineNumber(offset); - String comment = document.getText().substring(document.getLineStartOffset(commentLine), document.getLineEndOffset(commentLine)); + String comment = text.substring(document.getLineStartOffset(commentLine), document.getLineEndOffset(commentLine)); Breakpoint breakpoint; - if (comment.indexOf("Method") != -1) { + if (comment.contains("Method")) { breakpoint = breakpointManager.addMethodBreakpoint(document, commentLine + 1); if (breakpoint != null) { println("MethodBreakpoint created at " + file.getVirtualFile().getName() + ":" + (commentLine + 2), ProcessOutputTypes.SYSTEM); } } - else if (comment.indexOf("Field") != -1) { + else if (comment.contains("Field")) { breakpoint = breakpointManager.addFieldBreakpoint(document, commentLine + 1, readValue(comment, "Field")); if (breakpoint != null) { println("FieldBreakpoint created at " + file.getVirtualFile().getName() + ":" + (commentLine + 2), ProcessOutputTypes.SYSTEM); } } - else if (comment.indexOf("Exception") != -1) { + else if (comment.contains("Exception")) { breakpoint = breakpointManager.addExceptionBreakpoint(readValue(comment, "Exception"), ""); if (breakpoint != null) { println("ExceptionBreakpoint created at " + file.getVirtualFile().getName() + ":" + (commentLine + 2), @@ -419,6 +418,11 @@ public abstract class ExecutionWithDebuggerToolsTestCase extends ExecutionTestCa } } + if (breakpoint == null) { + LOG.error("Unable to set a breakpoint at line " + (commentLine + 1)); + continue; + } + String suspendPolicy = readValue(comment, "suspendPolicy"); if (suspendPolicy != null) { //breakpoint.setSuspend(!DebuggerSettings.SUSPEND_NONE.equals(suspendPolicy)); From 6fe5463700684eac229cfe2793ecd68e0e001024 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Mon, 14 Nov 2016 18:34:14 +0300 Subject: [PATCH 29/33] support emulated flag in breakpoints tests --- .../debugger/ExecutionWithDebuggerToolsTestCase.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/java/testFramework/src/com/intellij/debugger/ExecutionWithDebuggerToolsTestCase.java b/java/testFramework/src/com/intellij/debugger/ExecutionWithDebuggerToolsTestCase.java index 2c2bfe9ec2f6..df42d962bf85 100644 --- a/java/testFramework/src/com/intellij/debugger/ExecutionWithDebuggerToolsTestCase.java +++ b/java/testFramework/src/com/intellij/debugger/ExecutionWithDebuggerToolsTestCase.java @@ -48,6 +48,7 @@ import com.intellij.util.lang.CompoundRuntimeException; import com.intellij.util.ui.UIUtil; import com.sun.jdi.Method; import com.sun.jdi.ThreadReference; +import org.jetbrains.java.debugger.breakpoints.properties.JavaMethodBreakpointProperties; import javax.swing.*; import java.util.ArrayList; @@ -396,6 +397,13 @@ public abstract class ExecutionWithDebuggerToolsTestCase extends ExecutionTestCa if (breakpoint != null) { println("MethodBreakpoint created at " + file.getVirtualFile().getName() + ":" + (commentLine + 2), ProcessOutputTypes.SYSTEM); + + String emulated = readValue(comment, "Emulated"); + if (emulated != null) { + ((JavaMethodBreakpointProperties)breakpoint.getXBreakpoint().getProperties()).EMULATED = Boolean.valueOf(emulated); + println("Emulated = " + emulated, ProcessOutputTypes.SYSTEM); + } + } } else if (comment.contains("Field")) { From 9ec7d79729a496805865ba77c5c784b7fb5c3526 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Mon, 14 Nov 2016 18:34:57 +0300 Subject: [PATCH 30/33] show emulated checkbox (disabled by default for now) --- .../intellij/debugger/ui/breakpoints/MethodBreakpoint.java | 3 +-- .../ui/breakpoints/MethodBreakpointPropertiesPanel.java | 4 +--- platform/util/resources/misc/registry.properties | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java index 85067752fc01..edb693357da6 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java @@ -47,7 +47,6 @@ import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.JDOMExternalizerUtil; import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.registry.Registry; import com.intellij.psi.*; import com.intellij.util.StringBuilderSpinAllocator; import com.intellij.util.containers.ContainerUtil; @@ -496,7 +495,7 @@ public class MethodBreakpoint extends BreakpointWithHighlighter breakpoint) { - myEmulatedCheckBox.setVisible( - Registry.is("debugger.emulate.method.breakpoints") && breakpoint.getType() instanceof JavaMethodBreakpointType); + myEmulatedCheckBox.setVisible(breakpoint.getType() instanceof JavaMethodBreakpointType); myEmulatedCheckBox.setSelected(breakpoint.getProperties().EMULATED); myWatchEntryCheckBox.setSelected(breakpoint.getProperties().WATCH_ENTRY); diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index e18b56e2c4a9..85b136989656 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -242,7 +242,7 @@ debugger.renderers.file.description=Enable file object renderer debugger.single.smart.step.force=true debugger.single.smart.step.force.description=Do force step into on single variant smart step into debugger.emulate.method.breakpoints=false -debugger.emulate.method.breakpoints.description=Emulate method breakpoints with line breakpoints +debugger.emulate.method.breakpoints.description=Emulate method breakpoints with line breakpoints by default debugger.intern.string.literals=false debugger.intern.string.literals.description=Make string literal refer to the same instance of class String From 6e3ff33e0df42c2f1624458a4802e426c4db8967 Mon Sep 17 00:00:00 2001 From: Elizaveta Shashkova Date: Mon, 14 Nov 2016 19:06:39 +0300 Subject: [PATCH 31/33] Minor: rename variable (PY-21373) --- .../src/com/jetbrains/python/console/PydevConsoleRunner.java | 2 +- .../com/jetbrains/python/console/PydevConsoleRunnerImpl.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java index 272b1e997a3b..ed552d30cc9c 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java @@ -151,7 +151,7 @@ public interface PydevConsoleRunner { } })); - return command.replace(PydevConsoleRunnerImpl.WORKING_DIR_AND_PY_PATHS_ENV, path); + return command.replace(PydevConsoleRunnerImpl.WORKING_DIR_AND_PYTHON_PATHS, path); } static Map addDefaultEnvironments(Sdk sdk, Map envs, @NotNull Project project) { diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java b/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java index 5b9385486be0..119a5db33531 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java @@ -127,9 +127,9 @@ import static com.intellij.execution.runners.AbstractConsoleRunnerWithHistory.re * @author traff, oleg */ public class PydevConsoleRunnerImpl implements PydevConsoleRunner { - public static final String WORKING_DIR_AND_PY_PATHS_ENV = "WORKING_DIR_AND_PYTHON_PATHS"; + public static final String WORKING_DIR_AND_PYTHON_PATHS = "WORKING_DIR_AND_PYTHON_PATHS"; public static final String CONSOLE_START_COMMAND = "import sys; print('Python %s on %s' % (sys.version, sys.platform))\n" + - "sys.path.extend([" + WORKING_DIR_AND_PY_PATHS_ENV + "])\n"; + "sys.path.extend([" + WORKING_DIR_AND_PYTHON_PATHS + "])\n"; private static final Logger LOG = Logger.getInstance(PydevConsoleRunnerImpl.class.getName()); @SuppressWarnings("SpellCheckingInspection") public static final String PYDEV_PYDEVCONSOLE_PY = "pydev/pydevconsole.py"; From ea6a02880f8587baa985fcb0d43da9617da44176 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Mon, 14 Nov 2016 19:04:48 +0300 Subject: [PATCH 32/33] EA-88548 - IAE: TObjectHash.throwObjectContractViolation --- .../vcs/changes/actions/diff/ChangeDiffRequestProducer.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/ChangeDiffRequestProducer.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/ChangeDiffRequestProducer.java index 74ff182f3e92..3e12b05ca98d 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/ChangeDiffRequestProducer.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/ChangeDiffRequestProducer.java @@ -90,6 +90,12 @@ public class ChangeDiffRequestProducer implements DiffRequestProducer { } public static boolean isEquals(@NotNull Change change1, @NotNull Change change2) { + if (!Comparing.equal(ChangesUtil.getBeforePath(change1), ChangesUtil.getBeforePath(change2)) || + !Comparing.equal(ChangesUtil.getAfterPath(change1), ChangesUtil.getAfterPath(change2))) { + // we use Change.hashCode(), so removing this check might violate comparison contract + return false; + } + for (ChangeDiffViewerWrapperProvider provider : ChangeDiffViewerWrapperProvider.EP_NAME.getExtensions()) { ThreeState equals = provider.isEquals(change1, change2); if (equals == ThreeState.NO) return false; From ecb2bab2cb0634febf779868990cf51007381b89 Mon Sep 17 00:00:00 2001 From: Alexander Doroshko Date: Mon, 14 Nov 2016 19:23:09 +0300 Subject: [PATCH 33/33] Make IndentCalculator class public --- .../psi/impl/source/codeStyle/lineIndent/IndentCalculator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/lineIndent/IndentCalculator.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/lineIndent/IndentCalculator.java index 92d6c3c35a42..ddd3564b8943 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/lineIndent/IndentCalculator.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/lineIndent/IndentCalculator.java @@ -34,7 +34,7 @@ import org.jetbrains.annotations.Nullable; import static com.intellij.formatting.Indent.Type.CONTINUATION; import static com.intellij.formatting.Indent.Type.NORMAL; -class IndentCalculator { +public class IndentCalculator { private @NotNull final Project myProject; private @NotNull final Editor myEditor;