From 4de06c31fbd8984c6fc0c59c12f95892a8e8802b Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Tue, 9 Jun 2015 12:47:55 +0200 Subject: [PATCH 01/67] SSR: keep order of modifiers when replacing annotation --- .../com/intellij/structuralsearch/JavaReplaceHandler.java | 2 +- .../intellij/structuralsearch/StructuralReplaceTest.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaReplaceHandler.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaReplaceHandler.java index b95b01fe08d0..2ae17d8756a9 100644 --- a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaReplaceHandler.java +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaReplaceHandler.java @@ -269,7 +269,7 @@ public class JavaReplaceHandler extends StructuralReplaceHandler { if (firstChild instanceof PsiModifierList) { final PsiModifierList modifierList = (PsiModifierList)firstChild; for (PsiElement child : modifierList.getChildren()) { - elementParent.add(child); + elementParent.addBefore(child, elementToReplace); } } } diff --git a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralReplaceTest.java b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralReplaceTest.java index 6c204de6ea05..8086ffc0d86a 100644 --- a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralReplaceTest.java +++ b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralReplaceTest.java @@ -2259,15 +2259,15 @@ public class StructuralReplaceTest extends StructuralReplaceTestCase { public void testReplaceAnnotation() { String in = "@SuppressWarnings(\"ALL\")\n" + - "class A {}"; + "public class A {}"; String what = "@SuppressWarnings(\"ALL\")"; final String by1 = ""; - assertEquals("class A {}", replacer.testReplace(in, what, by1, options, false)); + assertEquals("public class A {}", replacer.testReplace(in, what, by1, options, false)); final String by2 = "@SuppressWarnings(\"NONE\") @Deprecated"; assertEquals("@SuppressWarnings(\"NONE\") @Deprecated\n" + - "class A {}", replacer.testReplace(in, what, by2, options, false)); + "public class A {}", replacer.testReplace(in, what, by2, options, false)); } public void testReplacePolyadicExpression() { From daad338196544c0e603edbb4e5ef270e882e8927 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Fri, 12 Jun 2015 17:34:58 +0200 Subject: [PATCH 02/67] EA-69413 (AIOOBE: AbstractMethodOverridesAbstractMethodInspection$AbstractMethodOverridesAbstractMethodVisitor.methodsHaveSameAnnotations) --- .../AbstractMethodOverridesAbstractMethodInspection.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/AbstractMethodOverridesAbstractMethodInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/AbstractMethodOverridesAbstractMethodInspection.java index 137374f12006..aeb7fc2e69b5 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/AbstractMethodOverridesAbstractMethodInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/AbstractMethodOverridesAbstractMethodInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2012 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2015 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -148,6 +148,9 @@ public class AbstractMethodOverridesAbstractMethodInspection extends BaseInspect final PsiParameter[] superParameters = superParameterList.getParameters(); final PsiParameterList parameterList = method.getParameterList(); final PsiParameter[] parameters = parameterList.getParameters(); + if (parameters.length != superParameters.length) { + return false; + } for (int i = 0, length = superParameters.length; i < length; i++) { final PsiParameter superParameter = superParameters[i]; final PsiParameter parameter = parameters[i]; @@ -171,7 +174,7 @@ public class AbstractMethodOverridesAbstractMethodInspection extends BaseInspect final Set annotationsSet = new HashSet(Arrays.asList(superAnnotations)); for (PsiAnnotation annotation : annotations) { final String qualifiedName = annotation.getQualifiedName(); - if ("java.lang.Override".equals(qualifiedName)) { + if (CommonClassNames.JAVA_LANG_OVERRIDE.equals(qualifiedName)) { continue; } if (!annotationsSet.contains(annotation)) { From d8078802d3d2cbc393049cf51a6f7f38c4f32fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yann=20C=C3=A9bron?= Date: Fri, 12 Jun 2015 17:39:22 +0200 Subject: [PATCH 03/67] JavaParametersUtil.getClasspathType: add @MagicConstant to return value --- .../impl/src/com/intellij/execution/util/JavaParametersUtil.java | 1 + 1 file changed, 1 insertion(+) diff --git a/java/execution/impl/src/com/intellij/execution/util/JavaParametersUtil.java b/java/execution/impl/src/com/intellij/execution/util/JavaParametersUtil.java index eede2edda506..0fe20076493b 100644 --- a/java/execution/impl/src/com/intellij/execution/util/JavaParametersUtil.java +++ b/java/execution/impl/src/com/intellij/execution/util/JavaParametersUtil.java @@ -68,6 +68,7 @@ public class JavaParametersUtil { parameters.getVMParametersList().addParametersString(vmParameters); } + @MagicConstant(valuesFromClass = JavaParameters.class) public static int getClasspathType(final RunConfigurationModule configurationModule, final String mainClassName, final boolean classMustHaveSource) throws CantRunException { final Module module = configurationModule.getModule(); From fab15d134c11d5d279ca382b56a1223f6a347a86 Mon Sep 17 00:00:00 2001 From: Dennis Ushakov Date: Fri, 12 Jun 2015 18:52:31 +0300 Subject: [PATCH 04/67] methods to prevent App Nap when needed --- .../util/resources/misc/registry.properties | 3 +++ .../intellij/ui/mac/foundation/MacUtil.java | 26 +++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 9f8ea66e2b7b..941f2f758cc6 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -74,6 +74,9 @@ idea.fix.mac.env=true idea.fix.mac.env.restartRequired=true idea.fix.mac.env.description=On Mac, use shell environment for external processes. +idea.mac.prevent.app.nap=false +idea.mac.prevent.app.nap.description=Prevent app nap during indexing and inspection + ide.x11.override.wm=true ide.appIcon.progress=true diff --git a/platform/util/src/com/intellij/ui/mac/foundation/MacUtil.java b/platform/util/src/com/intellij/ui/mac/foundation/MacUtil.java index 8ee9ee9206fb..58b6cdd53591 100644 --- a/platform/util/src/com/intellij/ui/mac/foundation/MacUtil.java +++ b/platform/util/src/com/intellij/ui/mac/foundation/MacUtil.java @@ -33,8 +33,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.concurrent.atomic.AtomicBoolean; -import static com.intellij.ui.mac.foundation.Foundation.invoke; -import static com.intellij.ui.mac.foundation.Foundation.toStringViaUTF8; +import static com.intellij.ui.mac.foundation.Foundation.*; /** * @author pegov @@ -192,4 +191,27 @@ public class MacUtil { } return windowTitle; } + + public static Object wakeUpNeo(String reason) { + // http://lists.apple.com/archives/java-dev/2014/Feb/msg00053.html + // https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Reference/Foundation/Classes/NSProcessInfo_Class/index.html#//apple_ref/c/tdef/NSActivityOptions + if (SystemInfo.isMacOSMavericks && Registry.is("idea.mac.prevent.app.nap")) { + ID processInfo = invoke("NSProcessInfo", "processInfo"); + ID activity = invoke(processInfo, "beginActivityWithOptions:reason:", + (0x00FFFFFFL & ~(1L << 20)) /* NSActivityUserInitiatedAllowingIdleSystemSleep */ | + 0xFF00000000L /* NSActivityLatencyCritical */, + nsString(reason)); + cfRetain(activity); + return activity; + } + return null; + } + + public static void matrixHasYou(Object activity) { + if (activity != null) { + ID processInfo = invoke("NSProcessInfo", "processInfo"); + invoke(processInfo, "endActivity:", activity); + cfRelease((ID)activity); + } + } } From 2123f478a4c002f3a3da50fc669c4be9143bc4f3 Mon Sep 17 00:00:00 2001 From: Dennis Ushakov Date: Fri, 12 Jun 2015 20:27:06 +0300 Subject: [PATCH 05/67] prevent App Nap during progress indicators --- .../openapi/progress/util/AbstractProgressIndicatorBase.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/platform/core-impl/src/com/intellij/openapi/progress/util/AbstractProgressIndicatorBase.java b/platform/core-impl/src/com/intellij/openapi/progress/util/AbstractProgressIndicatorBase.java index af8546cb6147..2cf43d397fd4 100644 --- a/platform/core-impl/src/com/intellij/openapi/progress/util/AbstractProgressIndicatorBase.java +++ b/platform/core-impl/src/com/intellij/openapi/progress/util/AbstractProgressIndicatorBase.java @@ -23,6 +23,7 @@ import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.UserDataHolderBase; +import com.intellij.ui.mac.foundation.MacUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.DoubleArrayList; import com.intellij.util.containers.Stack; @@ -43,6 +44,7 @@ public class AbstractProgressIndicatorBase extends UserDataHolderBase implements private volatile boolean myFinished; private volatile boolean myIndeterminate; + private volatile Object myMacActivity; private Stack myTextStack; private DoubleArrayList myFractionStack; @@ -68,6 +70,7 @@ public class AbstractProgressIndicatorBase extends UserDataHolderBase implements myText = ""; myFraction = 0; myText2 = ""; + myMacActivity = MacUtil.wakeUpNeo(toString()); myRunning = true; } @@ -82,6 +85,8 @@ public class AbstractProgressIndicatorBase extends UserDataHolderBase implements LOG.assertTrue(myRunning, "stop() should be called only if start() called before"); myRunning = false; myFinished = true; + MacUtil.matrixHasYou(myMacActivity); + myMacActivity = null; } @Override From f97b4434e7f43828bdb64748221fae530f3def2e Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Sat, 13 Jun 2015 21:51:23 +0200 Subject: [PATCH 06/67] semi-transparent focus border looks bad (artifacts at the bottom) with editor rectangle --- .../intellij/ide/ui/laf/darcula/ui/DarculaComboBoxUI.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaComboBoxUI.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaComboBoxUI.java index ea36ede553c8..1d129e4beb0c 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaComboBoxUI.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaComboBoxUI.java @@ -342,6 +342,9 @@ public class DarculaComboBoxUI extends BasicComboBoxUI implements Border { g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); final int R = JBUI.scale(5); + if (hasFocus) { + g.setClip(2, 2, comboBox.getWidth()- 4, comboBox.getHeight() - 4); //todo[kb] check HiDPI + } if (editor != null && comboBox.isEditable()) { ((JComponent)editor).setBorder(null); g.setColor(editor.getBackground()); @@ -351,11 +354,11 @@ public class DarculaComboBoxUI extends BasicComboBoxUI implements Border { g.setColor(editor.getBackground()); g.fillRect(xxx, y + 1, 5, H); } else { - g.setColor(comboBox.getBackground()); + g.setColor(UIUtil.getPanelBackground()); g.fillRoundRect(x + 1, y + 1, W, H, R, R); g.setColor(getArrowButtonFillColor(arrowButton.getBackground())); g.fillRoundRect(xxx, y + 1, width - xxx, H, R, R); - g.setColor(comboBox.getBackground()); + g.setColor(UIUtil.getPanelBackground()); g.fillRect(xxx, y + 1, 5, H); } final Color borderColor = getBorderColor();//ColorUtil.shift(UIUtil.getBorderColor(), 4); @@ -368,6 +371,7 @@ public class DarculaComboBoxUI extends BasicComboBoxUI implements Border { paintCurrentValue(g, r, false); if (hasFocus) { + g.setClip(0, 0, comboBox.getWidth(), comboBox.getHeight()); DarculaUIUtil.paintFocusRing(g, JBUI.scale(2), JBUI.scale(2), width - JBUI.scale(4), height - JBUI.scale(4)); } else { From e1075a48ab007755ef270908450fedb448d8f2ce Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Sat, 13 Jun 2015 22:15:52 +0200 Subject: [PATCH 07/67] fix artifacts with painting when combobox height is more than arrow button height --- .../ide/ui/laf/intellij/MacIntelliJComboBoxUI.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJComboBoxUI.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJComboBoxUI.java index 4a48ffdcfe82..b0c4cc52b53c 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJComboBoxUI.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJComboBoxUI.java @@ -189,6 +189,7 @@ public class MacIntelliJComboBoxUI extends BasicComboBoxUI implements Border, UI @Override protected Rectangle rectangleForCurrentValue() { Rectangle rect = super.rectangleForCurrentValue(); + rect.height=Math.min(rect.height, COMBOBOX.getIconHeight()); rect.y+=2; rect.x+=5; rect.height-=4; @@ -262,12 +263,13 @@ public class MacIntelliJComboBoxUI extends BasicComboBoxUI implements Border, UI public void paint(Graphics g, JComponent c) { super.paint(g, c); - int stop = arrowButton.getBounds().x; + Rectangle r = arrowButton.getBounds(); + int stop = r.x; g.setClip(0,0, stop, COMBOBOX.getIconHeight()); - COMBOBOX_LEFT.paintIcon(c,g,0,0); + COMBOBOX_LEFT.paintIcon(c,g,0,r.y); int x = COMBOBOX_LEFT.getIconWidth(); while (x < stop) { - COMBOBOX_TOP_BOTTOM.paintIcon(c, g, x, 0); + COMBOBOX_TOP_BOTTOM.paintIcon(c, g, x, r.y); x+=COMBOBOX_TOP_BOTTOM.getIconWidth(); } ((Graphics2D)g).scale(0.5d, 0.5d); From a0815608f9dff966f6d10bfcd58eb9ce9b681636 Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Sat, 13 Jun 2015 15:56:05 +0300 Subject: [PATCH 08/67] soft wrap model change validation --- .../LineOrientedDocumentChangeAdapter.java | 83 ------------------- .../mapping/CachingSoftWrapDataMapper.java | 23 ++--- 2 files changed, 12 insertions(+), 94 deletions(-) delete mode 100644 platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/LineOrientedDocumentChangeAdapter.java diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/LineOrientedDocumentChangeAdapter.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/LineOrientedDocumentChangeAdapter.java deleted file mode 100644 index 3a715233d044..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/LineOrientedDocumentChangeAdapter.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2000-2010 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.openapi.editor.impl.softwrap; - -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.event.DocumentEvent; -import com.intellij.openapi.editor.event.DocumentListener; -import com.intellij.openapi.editor.ex.PrioritizedDocumentListener; - -/** - * Adapter class for {@link DocumentListener} interface that allows to represent document change events - * in terms of logical lines affected by it. - * - * @author Denis Zhdanov - * @since Jul 7, 2010 4:24:52 PM - */ -public abstract class LineOrientedDocumentChangeAdapter implements PrioritizedDocumentListener { - - @Override - public void beforeDocumentChange(DocumentEvent event) { - Document document = event.getDocument(); - int startLine = document.getLineNumber(normalize(event.getDocument(), event.getOffset())); - int endLine = document.getLineNumber(normalize(event.getDocument(), event.getOffset() + event.getOldLength())); - int symbolsDifference = event.getNewLength() - event.getOldLength(); - beforeDocumentChange(startLine, endLine, symbolsDifference); - } - - @Override - public void documentChanged(DocumentEvent event) { - Document document = event.getDocument(); - int startLine = document.getLineNumber(normalize(event.getDocument(), event.getOffset())); - int endLine = document.getLineNumber(normalize(event.getDocument(), event.getOffset() + event.getNewLength())); - int symbolsDifference = event.getNewLength() - event.getOldLength(); - afterDocumentChange(startLine, endLine, symbolsDifference); - } - - @Override - public int getPriority() { - return Integer.MAX_VALUE; - } - - /** - * Callback adapter method for {@link DocumentListener#beforeDocumentChange(DocumentEvent)} event. - * - * @param startLine first logical document line affected by the target event (inclusive) - * @param endLine old last logical document line affected by the target event (inclusive) - * @param symbolsDifference difference in number in symbols applied to the target document - */ - public abstract void beforeDocumentChange(int startLine, int endLine, int symbolsDifference); - - /** - * Callback adapter method for {@link DocumentListener#documentChanged(DocumentEvent)} event. - * - * @param startLine first logical document line affected by the target event (inclusive) - * @param endLine new last logical document line affected by the target event (inclusive) - * @param symbolsDifference difference in number in symbols applied to the target document - */ - public abstract void afterDocumentChange(int startLine, int endLine, int symbolsDifference); - - private static int normalize(Document document, int offset) { - if (offset < 0) { - return 0; - } - - if (offset >= document.getTextLength()) { - return Math.max(document.getTextLength() - 1, 0); - } - return offset; - } -} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CachingSoftWrapDataMapper.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CachingSoftWrapDataMapper.java index 67923b0e49a0..a2cda73530a4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CachingSoftWrapDataMapper.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/mapping/CachingSoftWrapDataMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -566,7 +566,8 @@ public class CachingSoftWrapDataMapper implements SoftWrapAwareDocumentParsingLi int borderSoftWrapColumnDiff = 0; int borderSoftWrapLinesBeforeDiff = 0; int borderSoftWrapLinesCurrentDiff = 0; - for (int i = 0; i < myAffectedByUpdateCacheEntries.size(); i++) { + int affectedEntriesCount = myAffectedByUpdateCacheEntries.size(); + for (int i = 0; i < affectedEntriesCount; i++) { CacheEntry entry = myAffectedByUpdateCacheEntries.get(i); if (firstIndex < 0) { if (entry.startOffset < recalcEndOffsetTranslated) { @@ -574,15 +575,12 @@ public class CachingSoftWrapDataMapper implements SoftWrapAwareDocumentParsingLi continue; } firstIndex = i; - if (lastEntry != null) { - borderLogicalLine = lastEntry.endLogicalLine; - if (entry.startLogicalLine + logicalLinesDiff == borderLogicalLine) { - borderColumnDiff = lastEntry.endLogicalColumn - entry.startLogicalColumn; - borderSoftWrapLinesBeforeDiff = lastEntry.endSoftWrapLinesBefore - entry.startSoftWrapLinesBefore; - borderSoftWrapLinesCurrentDiff = lastEntry.endSoftWrapLinesCurrent - entry.startSoftWrapLinesCurrent + 1; - borderFoldedColumnDiff = lastEntry.endFoldingColumnDiff - entry.startFoldingColumnDiff; - borderSoftWrapColumnDiff = - borderColumnDiff - borderFoldedColumnDiff; - } + if (lastEntry != null && entry.startLogicalLine + logicalLinesDiff == borderLogicalLine) { + borderColumnDiff = lastEntry.endLogicalColumn - entry.startLogicalColumn; + borderSoftWrapLinesBeforeDiff = lastEntry.endSoftWrapLinesBefore - entry.startSoftWrapLinesBefore; + borderSoftWrapLinesCurrentDiff = lastEntry.endSoftWrapLinesCurrent - entry.startSoftWrapLinesCurrent + 1; + borderFoldedColumnDiff = lastEntry.endFoldingColumnDiff - entry.startFoldingColumnDiff; + borderSoftWrapColumnDiff = -borderColumnDiff - borderFoldedColumnDiff; } if (lengthDiff == 0 && logicalLinesDiff == 0 && foldedLinesDiff == 0 && softWrappedLinesDiff == 0 && borderColumnDiff == 0 && borderSoftWrapColumnDiff == 0 && borderFoldedColumnDiff == 0 @@ -626,6 +624,9 @@ public class CachingSoftWrapDataMapper implements SoftWrapAwareDocumentParsingLi LOG.error("Invalid soft wrap cache update", new Attachment("state.txt", myEditor.getSoftWrapModel().toString())); } } + if (myAffectedByUpdateCacheEntries.get(affectedEntriesCount - 1).endOffset > myEditor.getDocument().getTextLength()) { + LOG.error("Invalid soft wrap cache entries emerged", new Attachment("state.txt", myEditor.getSoftWrapModel().toString())); + } myCache.addAll(myAffectedByUpdateCacheEntries.subList(firstIndex, myAffectedByUpdateCacheEntries.size())); } myAffectedByUpdateCacheEntries.clear(); From 94841d36b4b05c70e9874003ee0bd3d969550c30 Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Sat, 13 Jun 2015 21:59:52 +0300 Subject: [PATCH 09/67] fix mess in editor after reformat when soft wraps are enabled (make sure bulk mode start/finish events always come in proper order) --- .../openapi/editor/impl/DocumentImpl.java | 22 ++++++++++++----- .../ex/util/LexerEditorHighlighter.java | 24 ++++++++++--------- .../openapi/editor/impl/EditorImplTest.java | 12 +++++++++- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java index 74354e30a14a..ea6834e5695e 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -82,6 +82,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { private boolean myEventsHandling = false; private final boolean myAssertThreading; private volatile boolean myDoingBulkUpdate = false; + private boolean myUpdatingBulkModeStatus; private volatile boolean myAcceptSlashR = false; private boolean myChangeInProgress; private volatile int myBufferSize; @@ -971,12 +972,21 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { // do not fire listeners or otherwise updateStarted() will be called more times than updateFinished() return; } - myDoingBulkUpdate = value; - if (value) { - getPublisher().updateStarted(this); + if (myUpdatingBulkModeStatus) { + throw new IllegalStateException("Detected bulk mode status update from DocumentBulkUpdateListener"); } - else { - getPublisher().updateFinished(this); + myUpdatingBulkModeStatus = true; + try { + myDoingBulkUpdate = value; + if (value) { + getPublisher().updateStarted(this); + } + else { + getPublisher().updateFinished(this); + } + } + finally { + myUpdatingBulkModeStatus = false; } } diff --git a/platform/editor-ui-ex/src/com/intellij/openapi/editor/ex/util/LexerEditorHighlighter.java b/platform/editor-ui-ex/src/com/intellij/openapi/editor/ex/util/LexerEditorHighlighter.java index e51f2f81c7ea..84119ad2f0ec 100644 --- a/platform/editor-ui-ex/src/com/intellij/openapi/editor/ex/util/LexerEditorHighlighter.java +++ b/platform/editor-ui-ex/src/com/intellij/openapi/editor/ex/util/LexerEditorHighlighter.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -78,9 +78,7 @@ public class LexerEditorHighlighter implements EditorHighlighter, PrioritizedDoc public final synchronized boolean checkContentIsEqualTo(CharSequence sequence) { final Document document = getDocument(); - return document instanceof DocumentEx && - Comparing.equal(document.getImmutableCharSequence(), sequence) && - !((DocumentEx)document).isInBulkUpdate(); + return document != null && isInSyncWithDocument() && Comparing.equal(document.getImmutableCharSequence(), sequence); } public EditorColorsScheme getScheme() { @@ -107,13 +105,12 @@ public class LexerEditorHighlighter implements EditorHighlighter, PrioritizedDoc @Override public HighlighterIterator createIterator(int startOffset) { synchronized (this) { - final Document document = getDocument(); - if(document instanceof DocumentEx && ((DocumentEx)document).isInBulkUpdate()) { - ((DocumentEx)document).setInBulkUpdate(false); // bulk mode failed - } - - if (mySegments.getSegmentCount() == 0 && document != null && document.getTextLength() > 0) { - // bulk mode was reset + if (!isInSyncWithDocument()) { + final Document document = getDocument(); + assert document != null; + if(document instanceof DocumentEx && ((DocumentEx)document).isInBulkUpdate()) { + ((DocumentEx)document).setInBulkUpdate(false); // bulk mode failed + } doSetText(document.getCharsSequence()); } @@ -131,6 +128,11 @@ public class LexerEditorHighlighter implements EditorHighlighter, PrioritizedDoc Project project = myEditor.getProject(); return project != null && !project.isDisposed(); } + + private boolean isInSyncWithDocument() { + Document document = getDocument(); + return document == null || document.getTextLength() == 0 || mySegments.getSegmentCount() > 0; + } private static boolean isInitialState(int data) { return data >= 0; diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorImplTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorImplTest.java index 388b349b30a7..f97ea942da7f 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorImplTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/EditorImplTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -141,4 +141,14 @@ public class EditorImplTest extends AbstractEditorTest { assertEquals(new LogicalPosition(0, 3), myEditor.getCaretModel().getLogicalPosition()); assertEquals(new VisualPosition(0, 3), myEditor.getCaretModel().getVisualPosition()); } + + public void testSoftWrapModeUpdateDuringBulkModeChange() throws Exception { + initText("long long line"); + configureSoftWraps(12); + DocumentEx document = (DocumentEx)myEditor.getDocument(); + document.setInBulkUpdate(true); + document.replaceString(4, 5, "-"); + document.setInBulkUpdate(false); + assertEquals(new VisualPosition(1, 5), myEditor.getCaretModel().getVisualPosition()); + } } From 775701c687655e3b87faddbf32b81bc66b2e8b88 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Sat, 13 Jun 2015 23:35:38 +0200 Subject: [PATCH 10/67] can't read value of focused combobox --- .../intellij/ide/ui/laf/intellij/MacIntelliJComboBoxUI.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJComboBoxUI.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJComboBoxUI.java index b0c4cc52b53c..975fdf129803 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJComboBoxUI.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellij/MacIntelliJComboBoxUI.java @@ -258,6 +258,9 @@ public class MacIntelliJComboBoxUI extends BasicComboBoxUI implements Border, UI g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); } + public void paintCurrentValue(Graphics g,Rectangle bounds,boolean hasFocus) { + super.paintCurrentValue(g, bounds, comboBox.isPopupVisible()); + } @Override public void paint(Graphics g, JComponent c) { From 49ec73af7f34c03eb03a9964628bebc5c8af4f24 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Fri, 12 Jun 2015 16:26:10 +0300 Subject: [PATCH 11/67] util: optimal string alignment calculation --- .../util/text/LevenshteinDistance.java | 29 ++------ .../com/intellij/util/text/EditDistance.java | 67 +++++++++++++++++++ .../intellij/util/text/EditDistanceTest.java | 56 ++++++++++++++++ .../spellchecker/engine/BaseSpellChecker.java | 7 +- 4 files changed, 132 insertions(+), 27 deletions(-) create mode 100644 platform/util/src/com/intellij/util/text/EditDistance.java create mode 100644 platform/util/testSrc/com/intellij/util/text/EditDistanceTest.java diff --git a/platform/util/src/com/intellij/openapi/util/text/LevenshteinDistance.java b/platform/util/src/com/intellij/openapi/util/text/LevenshteinDistance.java index 0f4e042048cd..b66d61ad0568 100644 --- a/platform/util/src/com/intellij/openapi/util/text/LevenshteinDistance.java +++ b/platform/util/src/com/intellij/openapi/util/text/LevenshteinDistance.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,29 +15,12 @@ */ package com.intellij.openapi.util.text; +import com.intellij.util.text.EditDistance; + +/** @deprecated use {@link EditDistance} (to be removed in IDEA 17) */ +@SuppressWarnings("unused") public class LevenshteinDistance { - - private static int minimum(int a, int b, int c) { - return Math.min(Math.min(a, b), c); - } - public int calculateMetrics(CharSequence str1, CharSequence str2) { - int[][] distance = new int[str1.length() + 1][str2.length() + 1]; - - for (int i = 0; i <= str1.length(); i++) { - distance[i][0] = i; - } - for (int j = 0; j <= str2.length(); j++) { - distance[0][j] = j; - } - - for (int i = 1; i <= str1.length(); i++) { - for (int j = 1; j <= str2.length(); j++) { - distance[i][j] = minimum(distance[i - 1][j] + 1, distance[i][j - 1] + 1, - distance[i - 1][j - 1] + ((str1.charAt(i - 1) == str2.charAt(j - 1)) ? 0 : 1)); - } - } - - return distance[str1.length()][str2.length()]; + return EditDistance.levenshtein(str1, str2, true); } } diff --git a/platform/util/src/com/intellij/util/text/EditDistance.java b/platform/util/src/com/intellij/util/text/EditDistance.java new file mode 100644 index 000000000000..e97da22aa108 --- /dev/null +++ b/platform/util/src/com/intellij/util/text/EditDistance.java @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util.text; + +import org.jetbrains.annotations.NotNull; + +public class EditDistance { + private EditDistance() { } + + public static int levenshtein(@NotNull CharSequence str1, @NotNull CharSequence str2, boolean caseSensitive) { + // Wagner-Fischer implementation of Levenshtein distance + // (http://en.wikipedia.org/wiki/Wagner-Fischer_algorithm) + int[][] d = prepare(str1.length(), str2.length()); + for (int i = 1; i <= str1.length(); i++) { + for (int j = 1; j <= str2.length(); j++) { + int cost = equal(str1.charAt(i - 1), str2.charAt(j - 1), caseSensitive) ? 0 : 1; + d[i][j] = min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost); + } + } + return d[str1.length()][str2.length()]; + } + + public static int optimalAlignment(@NotNull CharSequence str1, @NotNull CharSequence str2, boolean caseSensitive) { + // extension of the above with additional case of adjacent transpositions + // (http://en.wikipedia.org/wiki/Damerau-Levenshtein_distance#Optimal_string_alignment_distance) + int[][] d = prepare(str1.length(), str2.length()); + for (int i = 1; i <= str1.length(); i++) { + for (int j = 1; j <= str2.length(); j++) { + int cost = equal(str1.charAt(i - 1), str2.charAt(j - 1), caseSensitive) ? 0 : 1; + d[i][j] = min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost); + if (i > 1 && j > 1 && + equal(str1.charAt(i - 1), str2.charAt(j - 2), caseSensitive) && equal(str1.charAt(i - 2), str2.charAt(j - 1), caseSensitive)) { + d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + cost); + } + } + } + return d[str1.length()][str2.length()]; + } + + private static int[][] prepare(int length1, int length2) { + int[][] d = new int[length1 + 1][length2 + 1]; + for (int i = 0; i <= length1; i++) d[i][0] = i; + for (int j = 0; j <= length2; j++) d[0][j] = j; + return d; + } + + private static boolean equal(char c1, char c2, boolean caseSensitive) { + return caseSensitive ? c1 == c2 : Character.toLowerCase(c1) == Character.toLowerCase(c2); + } + + private static int min(int a, int b, int c) { + return Math.min(Math.min(a, b), c); + } +} diff --git a/platform/util/testSrc/com/intellij/util/text/EditDistanceTest.java b/platform/util/testSrc/com/intellij/util/text/EditDistanceTest.java new file mode 100644 index 000000000000..084d1791b007 --- /dev/null +++ b/platform/util/testSrc/com/intellij/util/text/EditDistanceTest.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util.text; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class EditDistanceTest { + @Test + public void levenshtein() { + assertEquals(0, EditDistance.levenshtein("", "", true)); + assertEquals(0, EditDistance.levenshtein("AA", "AA", true)); + assertEquals(1, EditDistance.levenshtein("AA", "Aa", true)); + assertEquals(2, EditDistance.levenshtein("AA", "aa", true)); + assertEquals(2, EditDistance.levenshtein("ab", "ba", true)); + } + + @Test + public void levenshteinCaseInsensitive() { + assertEquals(0, EditDistance.levenshtein("", "", false)); + assertEquals(0, EditDistance.levenshtein("AA", "AA", false)); + assertEquals(0, EditDistance.levenshtein("AA", "Aa", false)); + assertEquals(0, EditDistance.levenshtein("AA", "aa", false)); + assertEquals(2, EditDistance.levenshtein("ab", "ba", false)); + } + + @Test + public void optimalAlignment() { + assertEquals(1, EditDistance.optimalAlignment("ab", "ba", true)); + assertEquals(2, EditDistance.optimalAlignment("AB", "ba", true)); + assertEquals(3, EditDistance.optimalAlignment("ca", "abc", true)); + assertEquals(3, EditDistance.optimalAlignment("Ca", "abc", true)); + } + + @Test + public void optimalAlignmentCaseInsensitive() { + assertEquals(1, EditDistance.optimalAlignment("ab", "ba", false)); + assertEquals(1, EditDistance.optimalAlignment("AB", "ba", false)); + assertEquals(3, EditDistance.optimalAlignment("ca", "abc", false)); + assertEquals(3, EditDistance.optimalAlignment("Ca", "abc", false)); + } +} \ No newline at end of file diff --git a/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java b/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java index d5a0674f8e4c..c4ce805d014b 100644 --- a/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java +++ b/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.text.LevenshteinDistance; +import com.intellij.util.text.EditDistance; import com.intellij.openapi.util.text.StringUtil; import com.intellij.spellchecker.compress.CompressedDictionary; import com.intellij.spellchecker.dictionary.Dictionary; @@ -46,7 +46,6 @@ public class BaseSpellChecker implements SpellCheckerEngine { private final Set dictionaries = new HashSet(); private final List bundledDictionaries = ContainerUtil.createLockFreeCopyOnWriteList(); - private final LevenshteinDistance metrics = new LevenshteinDistance(); private final AtomicBoolean myLoadingDictionaries = new AtomicBoolean(false); private final List>> myDictionariesToLoad = ContainerUtil.createLockFreeCopyOnWriteList(); @@ -244,7 +243,7 @@ public class BaseSpellChecker implements SpellCheckerEngine { List rawSuggestions = restore(transformed.charAt(0), 0, Integer.MAX_VALUE, bundledDictionaries); rawSuggestions.addAll(restore(word.charAt(0), 0, Integer.MAX_VALUE, dictionaries)); for (String rawSuggestion : rawSuggestions) { - final int distance = metrics.calculateMetrics(transformed, rawSuggestion); + int distance = EditDistance.levenshtein(transformed, rawSuggestion, true); suggestions.add(new Suggestion(rawSuggestion, distance)); } List result = new ArrayList(); From 9486d4ef3e28692bd07df62f3cd52cf5915dee46 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Fri, 12 Jun 2015 23:17:22 +0300 Subject: [PATCH 12/67] Cleanup (typo) --- .../com/intellij/util/xmlb/PropertyAccessor.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/platform/util/src/com/intellij/util/xmlb/PropertyAccessor.java b/platform/util/src/com/intellij/util/xmlb/PropertyAccessor.java index 0bc808405bc9..60a7152a01ff 100644 --- a/platform/util/src/com/intellij/util/xmlb/PropertyAccessor.java +++ b/platform/util/src/com/intellij/util/xmlb/PropertyAccessor.java @@ -29,7 +29,7 @@ class PropertyAccessor implements MutableAccessor { private final String myName; private final Class myType; private final Method myReadMethod; - private final Method setter; + private final Method myWriteMethod; private final Type myGenericType; public PropertyAccessor(PropertyDescriptor descriptor) { @@ -40,15 +40,14 @@ class PropertyAccessor implements MutableAccessor { myName = name; myType = type; myReadMethod = readMethod; - setter = writeMethod; + myWriteMethod = writeMethod; myGenericType = myReadMethod.getGenericReturnType(); try { myReadMethod.setAccessible(true); - setter.setAccessible(true); - } - catch (SecurityException ignored) { + myWriteMethod.setAccessible(true); } + catch (SecurityException ignored) { } } @Override @@ -67,7 +66,7 @@ class PropertyAccessor implements MutableAccessor { @Override public void set(@NotNull Object host, @Nullable Object value) { try { - setter.invoke(host, value); + myWriteMethod.invoke(host, value); } catch (IllegalAccessException e) { throw new XmlSerializationException(e); @@ -110,7 +109,8 @@ class PropertyAccessor implements MutableAccessor { @Override public T getAnnotation(@NotNull Class annotationClass) { T annotation = myReadMethod.getAnnotation(annotationClass); - return annotation == null ? setter.getAnnotation(annotationClass) : annotation; + if (annotation == null) annotation = myWriteMethod.getAnnotation(annotationClass); + return annotation; } @Override From b0c4eca67ba6b7537f4b1494db5fa99307f8eeb2 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Sat, 13 Jun 2015 14:31:06 +0300 Subject: [PATCH 13/67] Cleanup (less collection allocations) --- .../spellchecker/SpellCheckerManager.java | 27 ++----- .../compress/CompressedDictionary.java | 35 +++++---- .../spellchecker/engine/BaseSpellChecker.java | 71 ++++++------------- 3 files changed, 49 insertions(+), 84 deletions(-) diff --git a/spellchecker/src/com/intellij/spellchecker/SpellCheckerManager.java b/spellchecker/src/com/intellij/spellchecker/SpellCheckerManager.java index 660990d53368..57106fecbdac 100644 --- a/spellchecker/src/com/intellij/spellchecker/SpellCheckerManager.java +++ b/spellchecker/src/com/intellij/spellchecker/SpellCheckerManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,22 +43,15 @@ import java.io.InputStream; import java.util.*; public class SpellCheckerManager { - private static final Logger LOG = Logger.getInstance("#com.intellij.spellchecker.SpellCheckerManager"); private static final int MAX_SUGGESTIONS_THRESHOLD = 5; private static final int MAX_METRICS = 1; private final Project project; - private SpellCheckerEngine spellChecker; - private EditableDictionary userDictionary; - - - @NotNull private final SuggestionProvider suggestionProvider = new BaseSuggestionProvider(this); - private final SpellCheckerSettings settings; public static SpellCheckerManager getInstance(Project project) { @@ -76,7 +69,6 @@ public class SpellCheckerManager { fillEngineDictionary(); } - public void updateBundledDictionaries(final List removedDictionaries) { for (BundledDictionaryProvider provider : Extensions.getExtensions(BundledDictionaryProvider.EP_NAME)) { for (String dictionary : provider.getBundledDictionaries()) { @@ -179,10 +171,8 @@ public class SpellCheckerManager { spellChecker.loadDictionary(loader); } userDictionary = stateLoader.getDictionary(); - } - public boolean hasProblem(@NotNull String word) { return !spellChecker.isCorrect(word); } @@ -202,8 +192,6 @@ public class SpellCheckerManager { restartInspections(); } - - @NotNull public static List getBundledDictionaries() { final ArrayList dictionaries = new ArrayList(); @@ -223,27 +211,24 @@ public class SpellCheckerManager { return suggestionProvider.getSuggestions(text); } - @NotNull protected List getRawSuggestions(@NotNull String word) { if (!spellChecker.isCorrect(word)) { List suggestions = spellChecker.getSuggestions(word, MAX_SUGGESTIONS_THRESHOLD, MAX_METRICS); if (!suggestions.isEmpty()) { - boolean capitalized = Strings.isCapitalized(word); - boolean upperCases = Strings.isUpperCase(word); - if (capitalized) { + if (Strings.isCapitalized(word)) { Strings.capitalize(suggestions); } - else if (upperCases) { + else if (Strings.isUpperCase(word)) { Strings.upperCase(suggestions); } + Set unique = new LinkedHashSet(suggestions); + return unique.size() < suggestions.size() ? new ArrayList(unique) : suggestions; } - return new ArrayList(new LinkedHashSet(suggestions)); } return Collections.emptyList(); } - public static void restartInspections() { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override @@ -257,6 +242,4 @@ public class SpellCheckerManager { } }); } - - } diff --git a/spellchecker/src/com/intellij/spellchecker/compress/CompressedDictionary.java b/spellchecker/src/com/intellij/spellchecker/compress/CompressedDictionary.java index 16ca7ca8b92a..7914aded01a8 100644 --- a/spellchecker/src/com/intellij/spellchecker/compress/CompressedDictionary.java +++ b/spellchecker/src/com/intellij/spellchecker/compress/CompressedDictionary.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -89,13 +89,26 @@ public final class CompressedDictionary implements Dictionary { return new TreeSet(COMPARATOR); } - @NotNull + /** @deprecated use {@link #getWords(char, int, int, Collection)} (to be removed in IDEA 17) */ + @SuppressWarnings("unused") public List getWords(char first, int minLength, int maxLength) { - int index = alphabet.getIndex(first, false); List result = new ArrayList(); - if (index == -1) { - return result; - } + getWords(first, minLength, maxLength, result); + return result; + } + + /** @deprecated use {@link #getWords(char, int, int, Collection)} (to be removed in IDEA 17) */ + @SuppressWarnings("unused") + public List getWords(char first) { + List result = new ArrayList(); + getWords(first, 0, Integer.MAX_VALUE, result); + return result; + } + + public void getWords(char first, int minLength, int maxLength, @NotNull Collection result) { + int index = alphabet.getIndex(first, false); + if (index == -1) return; + int i = 0; for (byte[] data : words) { int length = lengths[i]; @@ -110,12 +123,6 @@ public final class CompressedDictionary implements Dictionary { } i++; } - return result; - } - - @NotNull - public List getWords(char first) { - return getWords(first, 0, Integer.MAX_VALUE); } @NotNull @@ -149,9 +156,9 @@ public final class CompressedDictionary implements Dictionary { @Override public Set getWords() { Set words = new THashSet(); - for (int i=0; i<=alphabet.getLastIndexUsed();i++) { + for (int i = 0; i <= alphabet.getLastIndexUsed(); i++) { char letter = alphabet.getLetter(i); - words.addAll(getWords(letter)); + getWords(letter, 0, Integer.MAX_VALUE, words); } return words; } diff --git a/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java b/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java index c4ce805d014b..a62777dc4070 100644 --- a/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java +++ b/spellchecker/src/com/intellij/spellchecker/engine/BaseSpellChecker.java @@ -38,12 +38,10 @@ import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; - public class BaseSpellChecker implements SpellCheckerEngine { static final Logger LOG = Logger.getInstance("#com.intellij.spellchecker.engine.BaseSpellChecker"); private final Transformation transform = new Transformation(); - private final Set dictionaries = new HashSet(); private final List bundledDictionaries = ContainerUtil.createLockFreeCopyOnWriteList(); @@ -55,7 +53,6 @@ public class BaseSpellChecker implements SpellCheckerEngine { myProject = project; } - @Override public void loadDictionary(@NotNull Loader loader) { if (loader instanceof EditableDictionaryLoader) { @@ -138,7 +135,6 @@ public class BaseSpellChecker implements SpellCheckerEngine { } }; - StartupManager.getInstance(myProject).runWhenProjectIsInitialized(runnable); } @@ -160,37 +156,26 @@ public class BaseSpellChecker implements SpellCheckerEngine { return transform; } - @NotNull - private static List restore(char startFrom, int i, int j, @NotNull Collection dictionaries) { - List results = new ArrayList(); - + private static void restore(char startFrom, int i, int j, Collection dictionaries, Collection result) { for (Dictionary o : dictionaries) { - results.addAll(restore(startFrom, i, j, o)); + restore(startFrom, i, j, o, result); } - return results; } - @NotNull - private static List restore(final char first, final int i, final int j, @NotNull Dictionary dictionary) { - final List result = new ArrayList(); + private static void restore(final char first, final int i, final int j, Dictionary dictionary, final Collection result) { if (dictionary instanceof CompressedDictionary) { - result.addAll(((CompressedDictionary)dictionary).getWords(first, i, j)); + ((CompressedDictionary)dictionary).getWords(first, i, j, result); } else { dictionary.traverse(new Consumer() { @Override public void consume(String s) { - if (StringUtil.isEmpty(s)) { - return; - } - if (s.charAt(0) == first && s.length() >= i && s.length() <= j) { + if (!StringUtil.isEmpty(s) && s.charAt(0) == first && s.length() >= i && s.length() <= j) { result.add(s); } } }); } - - return result; } /** @@ -203,13 +188,10 @@ public class BaseSpellChecker implements SpellCheckerEngine { return -1; } - //System.out.println("dictionaries = " + dictionaries); int errors = 0; for (Dictionary dictionary : dictionaries) { if (dictionary == null) continue; - //System.out.print("\tBSC.isCorrect " + transformed + " " + dictionary); Boolean contains = dictionary.contains(transformed); - //System.out.println("\tcontains = " + contains); if (contains==null) ++errors; else if (contains) return 0; } @@ -219,57 +201,50 @@ public class BaseSpellChecker implements SpellCheckerEngine { @Override public boolean isCorrect(@NotNull String word) { - //System.out.println("---\n"+word); final String transformed = transform.transform(word); if (myLoadingDictionaries.get() || transformed == null) { return true; } int bundled = isCorrect(transformed, bundledDictionaries); int user = isCorrect(transformed, dictionaries); - //System.out.println("bundled = " + bundled); - //System.out.println("user = " + user); return bundled == 0 || user == 0 || bundled > 0 && user > 0; } - @Override @NotNull - public List getSuggestions(@NotNull final String word, int threshold, int quality) { - final String transformed = transform.transform(word); - if (transformed == null) { - return Collections.emptyList(); - } - final List suggestions = new ArrayList(); - List rawSuggestions = restore(transformed.charAt(0), 0, Integer.MAX_VALUE, bundledDictionaries); - rawSuggestions.addAll(restore(word.charAt(0), 0, Integer.MAX_VALUE, dictionaries)); + public List getSuggestions(@NotNull String word, int maxSuggestions, int quality) { + String transformed = transform.transform(word); + if (transformed == null) return Collections.emptyList(); + + List rawSuggestions = new ArrayList(); + restore(transformed.charAt(0), 0, Integer.MAX_VALUE, bundledDictionaries, rawSuggestions); + restore(word.charAt(0), 0, Integer.MAX_VALUE, dictionaries, rawSuggestions); + if (rawSuggestions.isEmpty()) return Collections.emptyList(); + + List suggestions = new ArrayList(rawSuggestions.size()); for (String rawSuggestion : rawSuggestions) { int distance = EditDistance.levenshtein(transformed, rawSuggestion, true); suggestions.add(new Suggestion(rawSuggestion, distance)); } - List result = new ArrayList(); - if (suggestions.isEmpty()) { - return result; - } - Collections.sort(suggestions); - int bestMetrics = suggestions.get(0).getMetrics(); - for (int i = 0; i < threshold; i++) { - if (suggestions.size() <= i || bestMetrics - suggestions.get(i).getMetrics() > quality) { + Collections.sort(suggestions); + int limit = Math.min(maxSuggestions, suggestions.size()); + List result = new ArrayList(limit); + int bestMetrics = suggestions.get(0).getMetrics(); + for (int i = 0; i < limit; i++) { + Suggestion suggestion = suggestions.get(i); + if (bestMetrics - suggestion.getMetrics() > quality) { break; } - result.add(i, suggestions.get(i).getWord()); + result.add(i, suggestion.getWord()); } return result; } - @Override @NotNull public List getVariants(@NotNull String prefix) { - //if (StringUtil.isEmpty(prefix)) { return Collections.emptyList(); - //} - } @Override From 3901c9aa150358afde9c90700776a4fdefc5c4a7 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Sat, 13 Jun 2015 17:32:52 +0300 Subject: [PATCH 14/67] Cleanup (converted to JUnit 4; sole Groovy test migrated to Java) --- .../intellij/spellchecker/StreamLoader.java | 35 +-- .../inspections/BaseSplitter.java | 19 +- .../spellchecker/compress/DictionaryTest.java | 223 +++++++----------- .../compress/EncodeAndCompressTest.java | 45 ++-- .../spellchecker/compress/EncoderTest.java | 21 +- .../spellchecker/compress/UnitBitSetTest.java | 11 +- .../SpellcheckerPerformanceTest.groovy | 51 ---- .../SpellcheckerPerformanceTest.java | 59 +++++ .../spellchecker/inspector/SplitterTest.java | 137 ++++++----- .../inspector/SuggestionTest.java | 36 +-- .../SpellCheckingEditorCustomizationTest.java | 12 +- 11 files changed, 308 insertions(+), 341 deletions(-) delete mode 100644 spellchecker/testSrc/com/intellij/spellchecker/inspection/SpellcheckerPerformanceTest.groovy create mode 100644 spellchecker/testSrc/com/intellij/spellchecker/inspection/SpellcheckerPerformanceTest.java diff --git a/spellchecker/src/com/intellij/spellchecker/StreamLoader.java b/spellchecker/src/com/intellij/spellchecker/StreamLoader.java index e94a9e2d7036..276b7db388f6 100644 --- a/spellchecker/src/com/intellij/spellchecker/StreamLoader.java +++ b/spellchecker/src/com/intellij/spellchecker/StreamLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,15 +24,12 @@ import org.jetbrains.annotations.NotNull; import java.io.*; public class StreamLoader implements Loader { - - private static final Logger LOG = Logger.getInstance("#com.intellij.spellchecker.StreamLoader"); - private final InputStream stream; private final String name; public StreamLoader(InputStream stream, String name) { this.stream = stream; - this.name=name; + this.name = name; } @Override @@ -42,28 +39,20 @@ public class StreamLoader implements Loader { @Override public void load(@NotNull Consumer consumer) { - DataInputStream in = new DataInputStream(stream); - BufferedReader br = null; - try { - br = new BufferedReader(new InputStreamReader(in, CharsetToolkit.UTF8_CHARSET)); - String strLine; - while ((strLine = br.readLine()) != null) { - consumer.consume(strLine); + BufferedReader br = new BufferedReader(new InputStreamReader(stream, CharsetToolkit.UTF8_CHARSET)); + try { + String line; + while ((line = br.readLine()) != null) { + consumer.consume(line); + } + } + finally { + br.close(); } } catch (Exception e) { - LOG.error(e); - } - finally { - try { - br.close(); - } - catch (IOException ignored) { - - } + Logger.getInstance(StreamLoader.class).error(e); } } - } - diff --git a/spellchecker/src/com/intellij/spellchecker/inspections/BaseSplitter.java b/spellchecker/src/com/intellij/spellchecker/inspections/BaseSplitter.java index 1fa96afac5a2..57449292cb55 100644 --- a/spellchecker/src/com/intellij/spellchecker/inspections/BaseSplitter.java +++ b/spellchecker/src/com/intellij/spellchecker/inspections/BaseSplitter.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ */ package com.intellij.spellchecker.inspections; -import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicatorProvider; import com.intellij.openapi.util.TextRange; @@ -32,14 +32,9 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; - public abstract class BaseSplitter implements Splitter { - - static final Logger LOG = Logger.getInstance("#com.intellij.spellchecker.inspections.BaseSplitter"); - public static final int MIN_RANGE_LENGTH = 3; - protected static void addWord(@NotNull Consumer consumer, boolean ignore, @Nullable TextRange found) { if (found == null || ignore) { return; @@ -51,7 +46,6 @@ public abstract class BaseSplitter implements Splitter { consumer.consume(found); } - protected static boolean isAllWordsAreUpperCased(@NotNull String text, @NotNull List words) { for (TextRange word : words) { CharacterIterator it = new StringCharacterIterator(text, word.getStartOffset(), word.getEndOffset(), word.getStartOffset()); @@ -62,7 +56,6 @@ public abstract class BaseSplitter implements Splitter { } } return true; - } protected static boolean containsShortWord(@NotNull List words) { @@ -130,15 +123,13 @@ public abstract class BaseSplitter implements Splitter { return toCheck; } catch (ProcessCanceledException e) { - //LOG.warn("Matching took too long: >>>" + range.substring(text) + "<<< " + toExclude); return Collections.singletonList(range); - //return Collections.emptyList(); } } public static void checkCancelled() { - ProgressIndicatorProvider.checkCanceled(); + if (ApplicationManager.getApplication() != null) { + ProgressIndicatorProvider.checkCanceled(); + } } - - } diff --git a/spellchecker/testSrc/com/intellij/spellchecker/compress/DictionaryTest.java b/spellchecker/testSrc/com/intellij/spellchecker/compress/DictionaryTest.java index c2ce1d39c64a..ea623553325e 100644 --- a/spellchecker/testSrc/com/intellij/spellchecker/compress/DictionaryTest.java +++ b/spellchecker/testSrc/com/intellij/spellchecker/compress/DictionaryTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2010 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,7 @@ package com.intellij.spellchecker.compress; import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.util.Ref; import com.intellij.spellchecker.DefaultBundledDictionariesProvider; import com.intellij.spellchecker.StreamLoader; import com.intellij.spellchecker.dictionary.Dictionary; @@ -27,172 +26,128 @@ import com.intellij.testFramework.PlatformTestUtil; import com.intellij.util.Consumer; import com.intellij.util.ThrowableRunnable; import gnu.trove.THashSet; -import junit.framework.TestCase; import org.jetbrains.annotations.NotNull; +import org.junit.Test; -import java.io.File; -import java.io.IOException; -import java.util.*; +import java.util.Set; -@SuppressWarnings({"JUnitTestCaseWithNonTrivialConstructors"}) -public class DictionaryTest extends TestCase { +import static com.intellij.openapi.util.Pair.pair; +import static org.junit.Assert.*; - private Dictionary dictionary; - - private final Map sizes = new HashMap(); - private final Map times = new HashMap(); +public class DictionaryTest { private static final String JETBRAINS_DIC = "jetbrains.dic"; private static final String ENGLISH_DIC = "english.dic"; - { - sizes.put(JETBRAINS_DIC, 1000); - sizes.put(ENGLISH_DIC, 140000); + private final Transformation myTransformation = new Transformation(); + + @Test + public void testJBDictionary() { + Dictionary dictionary = loadDictionaryPerformanceTest(JETBRAINS_DIC, 1000); + containsWordPerformanceTest(dictionary, 2000); + containsWordTest(dictionary); } - { - times.put(JETBRAINS_DIC, 1000); - times.put(ENGLISH_DIC, 50000); - } - - public void testDictionary() throws IOException { - final String[] names = {JETBRAINS_DIC, ENGLISH_DIC}; - for (String name : names) { - loadDictionaryTest(name, sizes.get(name)); - loadHalfDictionaryTest(name, 50000); - } + @Test + public void testEnglishDictionary() { + Dictionary dictionary = loadDictionaryPerformanceTest(ENGLISH_DIC, 50000); + containsWordPerformanceTest(dictionary, 2000); + containsWordTest(dictionary); } + @Test public void testDictionaryLoadedFully() { - //cleanupDictionary(); - final Transformation transform = new Transformation(); - CompressedDictionary dictionary = CompressedDictionary.create(englishLoader(), transform); - final Set onDisk = new THashSet(); - englishLoader().load(new Consumer() { + getLoader(JETBRAINS_DIC).load(new Consumer() { @Override public void consume(String s) { - assert s != null; - String t = transform.transform(s); - if (t == null) { - return; + assertNotNull(s); + String t = myTransformation.transform(s); + if (t != null) { + onDisk.add(t); } - onDisk.add(t); } }); - List odList = new ArrayList(onDisk); - Collections.sort(odList); - List loaded = new ArrayList(dictionary.getWords()); - Collections.sort(loaded); + Dictionary dictionary = CompressedDictionary.create(getLoader(JETBRAINS_DIC), myTransformation); - assertEquals(odList, loaded); + assertEquals(onDisk, dictionary.getWords()); } - public void cleanupDictionary() { - final Set onDisk = new THashSet(FileUtil.PATH_HASHING_STRATEGY); - englishLoader().load(new Consumer() { + private Dictionary loadDictionaryPerformanceTest(final String name, int time) { + final Ref ref = Ref.create(); + + PlatformTestUtil.startPerformanceTest("load dictionary", time, new ThrowableRunnable() { @Override - public void consume(String s) { - assert s != null; - onDisk.add(s); - } - }); - - List odList = new ArrayList(onDisk); - Collections.sort(odList); - - File file = new File("C:\\Work\\Idea\\community\\spellchecker\\src\\com\\intellij\\spellchecker\\english.2"); - try { - FileUtil.writeToFile(file, StringUtil.join(odList, "\n")); - } - catch (IOException e) { - throw new RuntimeException(e); - } - } - - private static StreamLoader englishLoader() { - return new StreamLoader(DefaultBundledDictionariesProvider.class.getResourceAsStream(ENGLISH_DIC), ENGLISH_DIC); - } - - public void loadDictionaryTest(@NotNull final String name, int wordCount) throws IOException { - final Transformation transform = new Transformation(); - PlatformTestUtil.startPerformanceTest("load dictionary", times.get(name), new ThrowableRunnable() { - @Override - public void run() throws Exception { - dictionary = CompressedDictionary - .create(new StreamLoader(DefaultBundledDictionariesProvider.class.getResourceAsStream(name), name), transform); + public void run() { + ref.set(CompressedDictionary.create(getLoader(name), myTransformation)); } }).cpuBound().assertTiming(); - final Set wordsToStoreAndCheck = createWordSets(name, 50000, 1).getFirst(); - PlatformTestUtil.startPerformanceTest("words contain",2000, new ThrowableRunnable() { + assertFalse(ref.isNull()); + return ref.get(); + } + + private void containsWordPerformanceTest(final Dictionary dictionary, int time) { + final Set wordsToCheck = createWordSets(dictionary, 50000, 1).first; + PlatformTestUtil.startPerformanceTest("contains word", time, new ThrowableRunnable() { @Override - public void run() throws Exception { - for (String s : wordsToStoreAndCheck) { - assertTrue(dictionary.contains(s)); + public void run() { + for (String s : wordsToCheck) { + assertEquals(Boolean.TRUE, dictionary.contains(s)); } } }).cpuBound().assertTiming(); } - private static Loader createLoader(final Set words) { - return new Loader() { - @Override - public void load(@NotNull Consumer consumer) { - for (String word : words) { - consumer.consume(word); - } - } - - @Override - public String getName() { - return "test"; - } - }; - } - - @SuppressWarnings({"unchecked"}) - private static Pair, Set> createWordSets(String name, final int maxCount, final int mod) { - Loader loader = new StreamLoader(DefaultBundledDictionariesProvider.class.getResourceAsStream(name), name); - final Set wordsToStore = new THashSet(); - final Set wordsToCheck = new THashSet(); - final Transformation transform = new Transformation(); - loader.load(new Consumer() { - private int counter = 0; - - @Override - public void consume(String s) { - if (counter > maxCount) { - return; - } - String transformed = transform.transform(s); - if (transformed != null) { - - if (counter % mod == 0) { - wordsToStore.add(transformed); - } - else { - wordsToCheck.add(transformed); - } - counter++; - } - } - }); - - return new Pair(wordsToStore, wordsToCheck); - } - - - public static void loadHalfDictionaryTest(final String name, final int maxCount) { - final Pair, Set> sets = createWordSets(name, maxCount, 2); - final Loader loader = createLoader(sets.getFirst()); - CompressedDictionary dictionary = CompressedDictionary.create(loader, new Transformation()); - for (String s : sets.getSecond()) { - if (!sets.getFirst().contains(s)) { - assertFalse(s, dictionary.contains(s)); + private void containsWordTest(Dictionary dictionary) { + Pair, Set> sets = createWordSets(dictionary, 50000, 2); + CompressedDictionary half = CompressedDictionary.create(new TestLoader(sets.first), myTransformation); + for (String s : sets.second) { + if (!sets.first.contains(s)) { + assertEquals(s, Boolean.FALSE, half.contains(s)); } } } + private static StreamLoader getLoader(@NotNull String name) { + return new StreamLoader(DefaultBundledDictionariesProvider.class.getResourceAsStream(name), name); + } + private Pair, Set> createWordSets(Dictionary dictionary, int maxCount, int mod) { + Set wordsToStore = new THashSet(); + Set wordsToCheck = new THashSet(); + + Set words = dictionary.getWords(); + assertNotNull(words); + int counter = 0; + for (String s : words) { + String transformed = myTransformation.transform(s); + if (transformed != null) { + (counter % mod == 0 ? wordsToStore : wordsToCheck).add(transformed); + if (++counter > maxCount) break; + } + } + + return pair(wordsToStore, wordsToCheck); + } + + private static class TestLoader implements Loader { + private final Set myWords; + + public TestLoader(Set words) { + myWords = words; + } + + @Override + public void load(@NotNull Consumer consumer) { + for (String word : myWords) { + consumer.consume(word); + } + } + + @Override + public String getName() { + return "test"; + } + } } diff --git a/spellchecker/testSrc/com/intellij/spellchecker/compress/EncodeAndCompressTest.java b/spellchecker/testSrc/com/intellij/spellchecker/compress/EncodeAndCompressTest.java index 96d70d802d70..9195a4c6e48e 100644 --- a/spellchecker/testSrc/com/intellij/spellchecker/compress/EncodeAndCompressTest.java +++ b/spellchecker/testSrc/com/intellij/spellchecker/compress/EncodeAndCompressTest.java @@ -1,19 +1,36 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.intellij.spellchecker.compress; -import junit.framework.TestCase; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; -public class EncodeAndCompressTest extends TestCase { - - public void testEncodeAndCompress() { - Encoder encoder = new Encoder(); - String word = "example"; - UnitBitSet bs = encoder.encode(word, true); - byte[] compressed = bs.pack(); - final String decompressed = UnitBitSet.decode(compressed, encoder.getAlphabet()); - assertEquals(word,decompressed); - String restored = encoder.decode(compressed); - assertEquals(word,restored); - } - +public class EncodeAndCompressTest { + @Test + public void testEncodeAndCompress() { + Encoder encoder = new Encoder(); + String word = "example"; + UnitBitSet bs = encoder.encode(word, true); + assertNotNull(bs); + byte[] compressed = bs.pack(); + String decompressed = UnitBitSet.decode(compressed, encoder.getAlphabet()); + assertEquals(word, decompressed); + String restored = encoder.decode(compressed); + assertEquals(word, restored); + } } diff --git a/spellchecker/testSrc/com/intellij/spellchecker/compress/EncoderTest.java b/spellchecker/testSrc/com/intellij/spellchecker/compress/EncoderTest.java index 1c90ddf0a6dd..8ad5e79847c9 100644 --- a/spellchecker/testSrc/com/intellij/spellchecker/compress/EncoderTest.java +++ b/spellchecker/testSrc/com/intellij/spellchecker/compress/EncoderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,9 +15,13 @@ */ package com.intellij.spellchecker.compress; -import junit.framework.TestCase; +import org.junit.Test; -public class EncoderTest extends TestCase { +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertEquals; + +public class EncoderTest { + @Test public void testSimple() { Encoder encoder = new Encoder(); final String wordToTest = "abc"; @@ -37,7 +41,7 @@ public class EncoderTest extends TestCase { } } - + @Test public void testDouble() { Encoder encoder = new Encoder(); final String wordToTest = "aaa"; @@ -49,6 +53,7 @@ public class EncoderTest extends TestCase { assertEquals(wordToTest, encoder.decode(bitSet.pack())); } + @Test public void testLetterRepetition() { Encoder encoder = new Encoder(); final String wordToTest = "aba"; @@ -60,6 +65,7 @@ public class EncoderTest extends TestCase { assertEquals(wordToTest, encoder.decode(bitSet.pack())); } + @Test public void testReverse() { Encoder encoder = new Encoder(); final String wordToTest1 = "abc"; @@ -83,19 +89,20 @@ public class EncoderTest extends TestCase { assertEquals(wordToTest2, encoder.decode(pack2)); } - + @Test public void testWithPredefinedAlphabet() { - Encoder encoder = new Encoder(new Alphabet("abcdefghijklmnopqrst")); + @SuppressWarnings("SpellCheckingInspection") Encoder encoder = new Encoder(new Alphabet("abcdefghijklmnopqrst")); final String wordToTest1 = "asia"; //letter 'a' will be added at the end final UnitBitSet bitSet = encoder.encode(wordToTest1, true); assertNotNull(bitSet); assertEquals(20, encoder.getAlphabet().getLastIndexUsed()); - assertIndices(bitSet, 1, 19, 9,1); + assertIndices(bitSet, 1, 19, 9, 1); assertEquals(wordToTest1, encoder.decode(bitSet.pack())); } + @Test public void testUnknown() { Encoder encoder = new Encoder(new Alphabet("abc")); final String wordToTest1 = "def"; diff --git a/spellchecker/testSrc/com/intellij/spellchecker/compress/UnitBitSetTest.java b/spellchecker/testSrc/com/intellij/spellchecker/compress/UnitBitSetTest.java index a4f6e2c4f310..be332bb395a6 100644 --- a/spellchecker/testSrc/com/intellij/spellchecker/compress/UnitBitSetTest.java +++ b/spellchecker/testSrc/com/intellij/spellchecker/compress/UnitBitSetTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2010 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,10 +15,12 @@ */ package com.intellij.spellchecker.compress; -import junit.framework.TestCase; +import org.junit.Test; -public class UnitBitSetTest extends TestCase { +import static org.junit.Assert.assertEquals; +public class UnitBitSetTest { + @Test public void testUnitValue() { int bitsPerUnit = 256; for (int i = 0; i < bitsPerUnit - 1; i++) { @@ -28,7 +30,4 @@ public class UnitBitSetTest extends TestCase { assertEquals(0, bs.getUnitValue(1)); } } - - - } diff --git a/spellchecker/testSrc/com/intellij/spellchecker/inspection/SpellcheckerPerformanceTest.groovy b/spellchecker/testSrc/com/intellij/spellchecker/inspection/SpellcheckerPerformanceTest.groovy deleted file mode 100644 index 87562183c66d..000000000000 --- a/spellchecker/testSrc/com/intellij/spellchecker/inspection/SpellcheckerPerformanceTest.groovy +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2000-2014 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.spellchecker.inspection - -import com.intellij.testFramework.PlatformTestUtil -/** - * @author peter - */ -public class SpellcheckerPerformanceTest extends SpellcheckerInspectionTestCase { - @Override - protected void setUp() throws Exception { - def start = System.currentTimeMillis() - super.setUp() - println("setUp took " + (System.currentTimeMillis() - start)) - } - - public void "test large text file with many typos"() { - int typoCount = 50000 - String text = "aaaaaaaaa " * typoCount // about 0.5M - - def start = System.currentTimeMillis() - def file = myFixture.addFileToProject("foo.txt", text).virtualFile - println("creation took " + (System.currentTimeMillis() - start)) - - start = System.currentTimeMillis() - myFixture.configureFromExistingVirtualFile(file) - println("configure took " + (System.currentTimeMillis() - start)) - - myFixture.enableInspections(inspectionTools) - - start = System.currentTimeMillis() - assertSize(typoCount, myFixture.doHighlighting()) - println("warmup took " + (System.currentTimeMillis() - start)) - - PlatformTestUtil.assertTiming("highlighting too long", 1000) { assertSize(typoCount, myFixture.doHighlighting()) } - } - -} diff --git a/spellchecker/testSrc/com/intellij/spellchecker/inspection/SpellcheckerPerformanceTest.java b/spellchecker/testSrc/com/intellij/spellchecker/inspection/SpellcheckerPerformanceTest.java new file mode 100644 index 000000000000..a1f19ea53d34 --- /dev/null +++ b/spellchecker/testSrc/com/intellij/spellchecker/inspection/SpellcheckerPerformanceTest.java @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.spellchecker.inspection; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.PlatformTestUtil; +import com.intellij.util.ThrowableRunnable; + +/** + * @author peter + */ +public class SpellcheckerPerformanceTest extends SpellcheckerInspectionTestCase { + @Override + protected void setUp() throws Exception { + long start = System.currentTimeMillis(); + super.setUp(); + System.out.println("setUp took " + (System.currentTimeMillis() - start) + " ms"); + } + + public void testLargeTextFileWithManyTypos() { + final int typoCount = 50000; + @SuppressWarnings("SpellCheckingInspection") String text = StringUtil.repeat("aaaaaaaaa ", typoCount); // about 0.5M + + long start = System.currentTimeMillis(); + VirtualFile file = myFixture.addFileToProject("foo.txt", text).getVirtualFile(); + System.out.println("creation took " + (System.currentTimeMillis() - start) + " ms"); + + start = System.currentTimeMillis(); + myFixture.configureFromExistingVirtualFile(file); + System.out.println("configure took " + (System.currentTimeMillis() - start) + " ms"); + + myFixture.enableInspections(getInspectionTools()); + + start = System.currentTimeMillis(); + assertSize(typoCount, myFixture.doHighlighting()); + System.out.println("warm-up took " + (System.currentTimeMillis() - start) + " ms"); + + PlatformTestUtil.startPerformanceTest("many typos highlighting", 1000, new ThrowableRunnable() { + @Override + public void run() { + assertSize(typoCount, myFixture.doHighlighting()); + } + }).cpuBound().assertTiming(); + } +} diff --git a/spellchecker/testSrc/com/intellij/spellchecker/inspector/SplitterTest.java b/spellchecker/testSrc/com/intellij/spellchecker/inspector/SplitterTest.java index 3562a59e2a8f..6f26abecf4ab 100644 --- a/spellchecker/testSrc/com/intellij/spellchecker/inspector/SplitterTest.java +++ b/spellchecker/testSrc/com/intellij/spellchecker/inspector/SplitterTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,136 +18,154 @@ package com.intellij.spellchecker.inspector; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.spellchecker.inspections.*; -import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase; import com.intellij.util.Consumer; -import junit.framework.Assert; import org.jetbrains.annotations.NotNull; +import org.junit.Test; import java.io.BufferedReader; -import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import static org.junit.Assert.assertEquals; -public class SplitterTest extends LightPlatformCodeInsightFixtureTestCase { +@SuppressWarnings("SpellCheckingInspection") +public class SplitterTest { + @Test public void testSplitSimpleCamelCase() { String text = "simpleCamelCase"; correctListToCheck(IdentifierSplitter.getInstance(), text, "simple", "Camel", "Case"); } + @Test public void testSplitCamelCaseWithUpperCasedWord() { String text = "camelCaseJSP"; correctListToCheck(IdentifierSplitter.getInstance(), text, "camel", "Case"); } + @Test public void testArrays() { String text = "Token[]"; correctListToCheck(IdentifierSplitter.getInstance(), text, "Token"); } + @Test public void testIdentifierInSingleQuotes() { String text = "'fill'"; correctListToCheck(IdentifierSplitter.getInstance(), text, "fill"); } - + @Test public void testWordsInSingleQuotesWithSep() { String text = "'test-something'"; correctListToCheck(PlainTextSplitter.getInstance(), text, "test", "something"); } + @Test public void testComplexWordsInQuotes() { String text = "\"test-customer's'\""; correctListToCheck(PlainTextSplitter.getInstance(), text, "test", "customer's"); } + @Test public void testCapitalizedWithShortWords() { String text = "IntelliJ"; correctListToCheck(IdentifierSplitter.getInstance(), text, "Intelli"); } + @Test public void testWords() { String text = "first-last"; correctListToCheck(IdentifierSplitter.getInstance(), text, "first", "last"); } + @Test public void testCapitalizedWithShortAndLongWords() { String text = "IntelliJTestTest"; correctListToCheck(IdentifierSplitter.getInstance(), text, "Intelli", "Test", "Test"); } + @Test public void testWordWithApostrophe1() { String text = "don't check"; correctListToCheck(PlainTextSplitter.getInstance(), text, "don't", "check"); } + @Test public void testHexInPlainText() { String text = "some text 0xacvfgt"; correctListToCheck(PlainTextSplitter.getInstance(), text, "some", "text"); } + @Test public void testHexInStringLiteral() { String text = "qwerty 0x12acfgt test"; correctListToCheck(PlainTextSplitter.getInstance(), text, "qwerty", "test"); } - + @Test public void testHex() { String text = "0xacvfgt"; correctListToCheck(WordSplitter.getInstance(), text); } + @Test public void testCheckXmlIgnored() { String text = "abcdef" + new String(new char[]{0xDC00}) + "test"; correctListToCheck(PlainTextSplitter.getInstance(), text); } - public void testIdentifiersWithNumbers() { + @Test + public void testIdentifiersWithNumbers() { String text = "result1"; - correctListToCheck(IdentifierSplitter.getInstance(), text, "result"); + correctListToCheck(IdentifierSplitter.getInstance(), text, "result"); } + @Test public void testIdentifiersWithNumbersInside() { String text = "result1result"; - correctListToCheck(IdentifierSplitter.getInstance(), text, "result","result"); + correctListToCheck(IdentifierSplitter.getInstance(), text, "result", "result"); } + @Test public void testWordWithApostrophe2() { String text = "customers'"; correctListToCheck(PlainTextSplitter.getInstance(), text, "customers"); } + @Test public void testWordWithApostrophe3() { String text = "customer's"; correctListToCheck(PlainTextSplitter.getInstance(), text, "customer's"); } - + @Test public void testWordWithApostrophe4() { String text = "we'll"; correctListToCheck(PlainTextSplitter.getInstance(), text, "we'll"); } + @Test public void testWordWithApostrophe5() { String text = "I'm you're we'll"; correctListToCheck(PlainTextSplitter.getInstance(), text, "you're", "we'll"); } + @Test public void testConstantName() { String text = "TEST_CONSTANT"; correctListToCheck(IdentifierSplitter.getInstance(), text, "TEST", "CONSTANT"); - } + @Test public void testLongConstantName() { String text = "TEST_VERY_VERY_LONG_AND_COMPLEX_CONSTANT"; correctListToCheck(IdentifierSplitter.getInstance(), text, "TEST", "VERY", "VERY", "LONG", "COMPLEX", "CONSTANT"); - } + @Test public void testJavaComments() { String text = "/*special symbols*/"; correctListToCheck(CommentSplitter.getInstance(), text, "special", "symbols"); @@ -157,203 +175,207 @@ public class SplitterTest extends LightPlatformCodeInsightFixtureTestCase { text = "// comment line which spell check not works: misttake"; correctListToCheck(CommentSplitter.getInstance(), text, "comment", "line", "which", "spell", "check", "works", "misttake"); - } - + @Test public void testXmlComments() { String text = ""; correctListToCheck(CommentSplitter.getInstance(), text, "special", "symbols"); - } + @Test public void testCamelCaseInXmlComments() { String text = ""; correctListToCheck(CommentSplitter.getInstance(), text, "special", "Case", "symbols"); - } + @Test public void testWordsWithNumbers() { String text = "testCamelCase123"; correctListToCheck(IdentifierSplitter.getInstance(), text, "test", "Camel", "Case"); - } + @Test public void testCommentsWithWordsWithNumbers() { String text = ""; correctListToCheck(CommentSplitter.getInstance(), text, "special", "Case", "symbols"); - } + @Test public void testCommentsWithAbr() { String text = ""; correctListToCheck(CommentSplitter.getInstance(), text, "Test", "Class"); - } + @Test public void testStringLiterals() { String text = "test\ntest\n"; correctListToCheck(PlainTextSplitter.getInstance(), text, "test", "test"); - } - + @Test public void testCommentWithHtml() { String text = ""; - correctListToCheck(CommentSplitter.getInstance(), text, "something", "here", "next", "content", "foooo", "barrrr", - "text"); - + correctListToCheck(CommentSplitter.getInstance(), text, "something", "here", "next", "content", "foooo", "barrrr", "text"); } + @Test public void testCommentWithHtmlTagsAndAtr() { String text = ""; correctListToCheck(CommentSplitter.getInstance(), text, "something", "here", "foooo", "barrrr", "text", "text"); - } + @Test public void testSpecial() { String text = "test   test"; correctListToCheck(PlainTextSplitter.getInstance(), text, "test", "test"); - } + @Test public void testColorUC() { String text = "#AABBFF"; correctListToCheck(WordSplitter.getInstance(), text); - } + @Test public void testColorUCSC() { String text = "#AABBFF;"; correctListToCheck(WordSplitter.getInstance(), text); - } + @Test public void testColorUCSurrounded() { String text = "\"#AABBFF\""; correctListToCheck(WordSplitter.getInstance(), text); - } + @Test public void testColorLC() { String text = "#fff"; correctListToCheck(TextSplitter.getInstance(), text); - } + @Test public void testTooShort() { String text = "bgColor carLight"; correctListToCheck(PlainTextSplitter.getInstance(), text, "Color", "Light"); - } + @Test public void testPhpVariableCorrectSimple() { String text = "$this"; correctListToCheck(IdentifierSplitter.getInstance(), text, "this"); - } + @Test public void testPhpVariableCorrect() { String text = "$this_this$this"; correctListToCheck(IdentifierSplitter.getInstance(), text, "this", "this", "this"); - } + @Test public void testEmail() { String text = "some text with email (shkate.test@gmail.com) inside"; correctListToCheck(PlainTextSplitter.getInstance(), text, "some", "text", "with", "email", "inside"); - } + @Test public void testEmailOnly() { String text = "shkate123-\u00DC.test@gmail.com"; correctListToCheck(PlainTextSplitter.getInstance(), text); - } + @Test public void testUrl() { String text = "https://www.jetbrains.com/idea"; correctListToCheck(PlainTextSplitter.getInstance(), text); } + @Test public void testUrlThenSpaces() { String text = "https://www.jetbrains.com/idea asdasdasd sdfsdf"; correctListToCheck(PlainTextSplitter.getInstance(), text, "asdasdasd", "sdfsdf"); } + @Test public void testWordBeforeDelimiter() { String text = "badd,"; correctListToCheck(PlainTextSplitter.getInstance(), text, "badd"); } + @Test public void testWordAfterDelimiter() { String text = ",badd"; correctListToCheck(PlainTextSplitter.getInstance(), text, "badd"); } + @Test public void testWordInCapsBeforeDelimiter() { String text = "BADD,"; correctListToCheck(PlainTextSplitter.getInstance(), text, "BADD"); - } + @Test public void testWordInCapsAfterDelimiter() { String text = ",BADD"; correctListToCheck(PlainTextSplitter.getInstance(), text, "BADD"); - } + @Test public void testWordInCapsAfterDelimiter2() { String text = "BADD;"; correctListToCheck(PlainTextSplitter.getInstance(), text, "BADD"); - } + @Test public void testWordInCapsAfterDelimiter3() { String text = ";BADD;"; correctListToCheck(PlainTextSplitter.getInstance(), text, "BADD"); } + @Test public void testWordWithUmlauts() { String text = "rechtsb\u00FCndig"; correctListToCheck(PlainTextSplitter.getInstance(), text, text); } + @Test public void testWordUpperCasedWithUmlauts() { String text = "RECHTSB\u00DCNDIG"; correctListToCheck(PlainTextSplitter.getInstance(), text, text); } + @Test public void testCommaSeparatedList() { String text = "properties,test,properties"; correctListToCheck(PlainTextSplitter.getInstance(), text, "properties", "test", "properties"); - } + @Test public void testSemicolonSeparatedList() { String text = "properties;test;properties"; correctListToCheck(PlainTextSplitter.getInstance(), text, "properties", "test", "properties"); - } + @Test public void testProperties1() { String text = "properties.test.properties"; correctListToCheck(PropertiesSplitter.getInstance(), text, "properties", "test", "properties"); } - + @Test public void testPropertiesWithCamelCase() { String text = "upgrade.testCommit.propertiesSomeNews"; - correctListToCheck(PropertiesSplitter.getInstance(), text, "upgrade", "test", "Commit", "properties", "Some", - "News"); + correctListToCheck(PropertiesSplitter.getInstance(), text, "upgrade", "test", "Commit", "properties", "Some", "News"); } + @Test public void testWordUpperCasedWithUmlautsInTheBeginning() { String text = "\u00DCNDIG"; correctListToCheck(PlainTextSplitter.getInstance(), text, text); } - + @Test public void testTCData() { final InputStream stream = SplitterTest.class.getResourceAsStream("contents.txt"); String text = convertStreamToString(stream); @@ -361,7 +383,6 @@ public class SplitterTest extends LightPlatformCodeInsightFixtureTestCase { assertEquals(0, words.size()); } - private static List wordsToCheck(Splitter splitter, final String text) { final List words = new ArrayList(); splitter.split(text, TextRange.allOf(text), new Consumer() { @@ -373,36 +394,32 @@ public class SplitterTest extends LightPlatformCodeInsightFixtureTestCase { return words; } - private static void correctListToCheck(Splitter splitter, String text, @NotNull String... expected) { List words = wordsToCheck(splitter, text); List expectedWords = Arrays.asList(expected); - Assert.assertEquals("Splitting:'" + text + "'", expectedWords.toString(), words!=null ? words.toString() : "[]"); + assertEquals("Splitting:'" + text + "'", expectedWords.toString(), words != null ? words.toString() : "[]"); } - - private String convertStreamToString(InputStream is) { + private static String convertStreamToString(InputStream is) { if (is != null) { StringBuilder sb = new StringBuilder(); - String line; try { BufferedReader reader = new BufferedReader(new InputStreamReader(is, CharsetToolkit.UTF8_CHARSET)); - while ((line = reader.readLine()) != null) { - sb.append(line).append("\n"); + try { + String line; + while ((line = reader.readLine()) != null) { + sb.append(line).append('\n'); + } + } + finally { + reader.close(); } } catch (Exception e) { throw new RuntimeException(e); } - finally { - try { - is.close(); - } - catch (IOException ignore) { - } - } return sb.toString(); } else { diff --git a/spellchecker/testSrc/com/intellij/spellchecker/inspector/SuggestionTest.java b/spellchecker/testSrc/com/intellij/spellchecker/inspector/SuggestionTest.java index 552e3eb22976..eba7304505f2 100644 --- a/spellchecker/testSrc/com/intellij/spellchecker/inspector/SuggestionTest.java +++ b/spellchecker/testSrc/com/intellij/spellchecker/inspector/SuggestionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,34 +20,14 @@ import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCa import java.util.List; - +@SuppressWarnings("SpellCheckingInspection") public class SuggestionTest extends LightPlatformCodeInsightFixtureTestCase { + public void testSuggestions() { doTest("upgade", "upgrade"); } + public void testFirstLetterUppercaseSuggestions() { doTest("Upgade", "Upgrade"); } + public void testCamelCaseSuggestions() { doTest("TestUpgade", "TestUpgrade"); } - private SpellCheckerManager spManager; - - private SpellCheckerManager getManager() { - if (spManager == null) { - spManager = SpellCheckerManager.getInstance(myFixture.getProject()); - } - assert spManager != null; - return spManager; - } - - public void testSuggestions() { - List result = getManager().getSuggestions("upgade"); - assertEquals("upgrade", result.get(0)); - } - - - public void testFirstLetterUppercaseSuggestions() { - List result = getManager().getSuggestions("Upgade"); - assertEquals("Upgrade", result.get(0)); - } - - public void testCamelCaseSuggestions() { - SpellCheckerManager manager = SpellCheckerManager.getInstance(myFixture.getProject()); - assert manager != null; - List result = manager.getSuggestions("TestUpgade"); - assertEquals("TestUpgrade", result.get(0)); + private void doTest(String word, String expected) { + List result = SpellCheckerManager.getInstance(myFixture.getProject()).getSuggestions(word); + assertEquals(expected, result.get(0)); } } diff --git a/spellchecker/testSrc/com/intellij/spellchecker/ui/SpellCheckingEditorCustomizationTest.java b/spellchecker/testSrc/com/intellij/spellchecker/ui/SpellCheckingEditorCustomizationTest.java index 32efe07e71b0..679b32f72f90 100644 --- a/spellchecker/testSrc/com/intellij/spellchecker/ui/SpellCheckingEditorCustomizationTest.java +++ b/spellchecker/testSrc/com/intellij/spellchecker/ui/SpellCheckingEditorCustomizationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,13 +22,15 @@ import com.intellij.openapi.fileTypes.PlainTextFileType; import com.intellij.spellchecker.inspections.SpellCheckingInspection; import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase; import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl; +import com.intellij.ui.EditorCustomization; +@SuppressWarnings("SpellCheckingInspection") public class SpellCheckingEditorCustomizationTest extends LightPlatformCodeInsightFixtureTestCase { - public void testEnabled() throws Exception { + public void testEnabled() { doTest(true, "missspelling"); } - public void testDisabled() throws Exception { + public void testDisabled() { doTest(false, "missspelling"); } @@ -48,7 +50,9 @@ public class SpellCheckingEditorCustomizationTest extends LightPlatformCodeInsig myFixture.configureByText(PlainTextFileType.INSTANCE, document); myFixture.enableInspections(new SpellCheckingInspection()); - SpellCheckingEditorCustomizationProvider.getInstance().getCustomization(enabled).customize((EditorEx)myFixture.getEditor()); + EditorCustomization customization = SpellCheckingEditorCustomizationProvider.getInstance().getCustomization(enabled); + assertNotNull(customization); + customization.customize((EditorEx)myFixture.getEditor()); myFixture.checkHighlighting(); } From 25053738f5fab62d2aa7f07797d0812b4e4ebfb1 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 11 Jun 2015 15:37:38 +0300 Subject: [PATCH 15/67] IDEA-118080 (ability to define custom headers for inspection) --- java/manifest/manifest.iml | 4 +- .../lang/manifest/ManifestBundle.properties | 5 +- .../lang/manifest/header/HeaderNameMatch.java | 81 ---------- .../header/HeaderParserRepository.java | 25 +--- .../AbstractManifestQuickFix.java | 33 ++++ .../MissingFinalNewlineInspection.java | 8 +- .../MisspelledHeaderInspection.java | 141 ++++++++++++++---- .../lang/manifest/ManifestPsiTest.java | 27 ++-- .../MisspelledHeaderInspectionTest.java | 52 ++++++- 9 files changed, 212 insertions(+), 164 deletions(-) delete mode 100644 java/manifest/src/org/jetbrains/lang/manifest/header/HeaderNameMatch.java create mode 100644 java/manifest/src/org/jetbrains/lang/manifest/highlighting/AbstractManifestQuickFix.java diff --git a/java/manifest/manifest.iml b/java/manifest/manifest.iml index 1c394ddffd93..212982b8ac5c 100644 --- a/java/manifest/manifest.iml +++ b/java/manifest/manifest.iml @@ -17,6 +17,6 @@ + - - + \ No newline at end of file diff --git a/java/manifest/src/org/jetbrains/lang/manifest/ManifestBundle.properties b/java/manifest/src/org/jetbrains/lang/manifest/ManifestBundle.properties index 845c88d11741..7fe98d7431e7 100644 --- a/java/manifest/src/org/jetbrains/lang/manifest/ManifestBundle.properties +++ b/java/manifest/src/org/jetbrains/lang/manifest/ManifestBundle.properties @@ -1,4 +1,3 @@ -manifest.unexpected.token=Unexpected token manifest.colon.expected=':' expected manifest.whitespace.expected=Whitespace expected manifest.header.expected=Header expected @@ -11,4 +10,6 @@ inspection.group=Manifest inspection.newline.message=Manifest file doesn't end with a final newline inspection.newline.fix=Add newline inspection.header.message=Header name is unknown or spelled incorrectly -inspection.header.fix=Change to ''{0}'' +inspection.header.ui.label=Custom headers: +inspection.header.rename.fix=Change to ''{0}'' +inspection.header.remember.fix=Add ''{0}'' to custom headers diff --git a/java/manifest/src/org/jetbrains/lang/manifest/header/HeaderNameMatch.java b/java/manifest/src/org/jetbrains/lang/manifest/header/HeaderNameMatch.java deleted file mode 100644 index 8ab3669ac539..000000000000 --- a/java/manifest/src/org/jetbrains/lang/manifest/header/HeaderNameMatch.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2007-2009, Osmorc Development Team - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright notice, this list - * of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, this - * list of conditions and the following disclaimer in the documentation and/or other - * materials provided with the distribution. - * * Neither the name of 'Osmorc Development Team' nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific - * prior written permission. - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT - * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.jetbrains.lang.manifest.header; - -import org.jetbrains.annotations.NotNull; - -/** - * A match describes how good a header known to a particular header provider matches a given header. - * The name of the given header may contain typos and so there may be no perfect match. A perfect match will - * have a Levenshtein distance of 0. Worse matches will have greater Levenshtein distances. - * - * @author Robert F. Beeger (robert@beeger.net) - */ -public class HeaderNameMatch implements Comparable { - private final int myDistance; - private final String myHeaderName; - - public HeaderNameMatch(int distance, @NotNull String headerName) { - myDistance = distance; - myHeaderName = headerName; - } - - public int getDistance() { - return myDistance; - } - - public String getHeaderName() { - return myHeaderName; - } - - /** - * Matches are compared based on their distance. - */ - @Override - public int compareTo(@NotNull HeaderNameMatch o) { - return getDistance() - o.getDistance(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - HeaderNameMatch that = (HeaderNameMatch)o; - - return myDistance == that.myDistance && myHeaderName.equals(that.myHeaderName); - } - - @Override - public int hashCode() { - int result = myDistance; - result = 31 * result + myHeaderName.hashCode(); - return result; - } -} diff --git a/java/manifest/src/org/jetbrains/lang/manifest/header/HeaderParserRepository.java b/java/manifest/src/org/jetbrains/lang/manifest/header/HeaderParserRepository.java index 27b80df35e1e..caab71dbf65a 100644 --- a/java/manifest/src/org/jetbrains/lang/manifest/header/HeaderParserRepository.java +++ b/java/manifest/src/org/jetbrains/lang/manifest/header/HeaderParserRepository.java @@ -27,19 +27,17 @@ package org.jetbrains.lang.manifest.header; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.util.NotNullLazyValue; -import com.intellij.openapi.util.text.LevenshteinDistance; import com.intellij.psi.PsiReference; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.text.CaseInsensitiveStringHashingStrategy; +import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.lang.manifest.psi.Header; import org.jetbrains.lang.manifest.psi.HeaderValuePart; -import java.util.Collection; import java.util.Map; import java.util.Set; -import java.util.TreeSet; /** * @author Robert F. Beeger (robert@beeger.net) @@ -53,7 +51,7 @@ public class HeaderParserRepository { @NotNull @Override protected Map compute() { - Map map = ContainerUtil.newHashMap(); + Map map = new THashMap(CaseInsensitiveStringHashingStrategy.INSTANCE); for (HeaderParserProvider provider : Extensions.getExtensions(HeaderParserProvider.EP_NAME)) { map.putAll(provider.getHeaderParsers()); } @@ -66,23 +64,6 @@ public class HeaderParserRepository { return myParsers.getValue().get(headerName); } - @NotNull - public Collection getMatches(@NotNull String headerName) { - HeaderParser parser = myParsers.getValue().get(headerName); - if (parser != null) { - return ContainerUtil.emptyList(); - } - - LevenshteinDistance distance = new LevenshteinDistance(); - Set result = new TreeSet(); - for (Map.Entry entry : myParsers.getValue().entrySet()) { - String otherName = entry.getKey(); - int dist = distance.calculateMetrics(headerName, otherName); - result.add(new HeaderNameMatch(dist, otherName)); - } - return result; - } - @NotNull public Set getAllHeaderNames() { return myParsers.getValue().keySet(); diff --git a/java/manifest/src/org/jetbrains/lang/manifest/highlighting/AbstractManifestQuickFix.java b/java/manifest/src/org/jetbrains/lang/manifest/highlighting/AbstractManifestQuickFix.java new file mode 100644 index 000000000000..c548ff918d1a --- /dev/null +++ b/java/manifest/src/org/jetbrains/lang/manifest/highlighting/AbstractManifestQuickFix.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.lang.manifest.highlighting; + +import com.intellij.codeInspection.LocalQuickFixOnPsiElement; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.lang.manifest.ManifestBundle; + +public abstract class AbstractManifestQuickFix extends LocalQuickFixOnPsiElement { + protected AbstractManifestQuickFix(@NotNull PsiElement element) { + super(element); + } + + @NotNull + @Override + public final String getFamilyName() { + return ManifestBundle.message("inspection.group"); + } +} diff --git a/java/manifest/src/org/jetbrains/lang/manifest/highlighting/MissingFinalNewlineInspection.java b/java/manifest/src/org/jetbrains/lang/manifest/highlighting/MissingFinalNewlineInspection.java index 60bfce5fd13c..3bdf451f8623 100644 --- a/java/manifest/src/org/jetbrains/lang/manifest/highlighting/MissingFinalNewlineInspection.java +++ b/java/manifest/src/org/jetbrains/lang/manifest/highlighting/MissingFinalNewlineInspection.java @@ -61,7 +61,7 @@ public class MissingFinalNewlineInspection extends LocalInspectionTool { return null; } - private static class AddNewlineQuickFix extends LocalQuickFixOnPsiElement { + private static class AddNewlineQuickFix extends AbstractManifestQuickFix { private AddNewlineQuickFix(Section section) { super(section); } @@ -72,12 +72,6 @@ public class MissingFinalNewlineInspection extends LocalInspectionTool { return ManifestBundle.message("inspection.newline.fix"); } - @NotNull - @Override - public String getFamilyName() { - return ManifestBundle.message("inspection.group"); - } - @Override public void invoke(@NotNull Project project, @NotNull PsiFile file, @NotNull PsiElement startElement, @NotNull PsiElement endElement) { PsiElement lastChild = startElement.getLastChild(); diff --git a/java/manifest/src/org/jetbrains/lang/manifest/highlighting/MisspelledHeaderInspection.java b/java/manifest/src/org/jetbrains/lang/manifest/highlighting/MisspelledHeaderInspection.java index 75f41764fe5c..98065392b352 100644 --- a/java/manifest/src/org/jetbrains/lang/manifest/highlighting/MisspelledHeaderInspection.java +++ b/java/manifest/src/org/jetbrains/lang/manifest/highlighting/MisspelledHeaderInspection.java @@ -26,25 +26,42 @@ package org.jetbrains.lang.manifest.highlighting; import com.intellij.codeInspection.*; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.profile.codeInspection.InspectionProfileManager; +import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.PsiFile; +import com.intellij.spellchecker.engine.Suggestion; +import com.intellij.ui.DocumentAdapter; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.text.CaseInsensitiveStringHashingStrategy; +import com.intellij.util.text.EditDistance; +import com.intellij.util.xmlb.annotations.AbstractCollection; +import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.lang.manifest.ManifestBundle; -import org.jetbrains.lang.manifest.header.HeaderNameMatch; import org.jetbrains.lang.manifest.header.HeaderParserRepository; import org.jetbrains.lang.manifest.psi.Header; -import java.util.Collection; +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import java.awt.*; +import java.util.*; import java.util.List; /** * @author Robert F. Beeger (robert@beeger.net) */ public class MisspelledHeaderInspection extends LocalInspectionTool { - private static final int MAX_SUGGESTIONS = 10; + private static final int MAX_SUGGESTIONS = 5; + private static final int MAX_DISTANCE = 4; + private static final int TYPO_DISTANCE = 2; - private HeaderParserRepository myRepository; + @AbstractCollection(surroundWithTag = false, elementTag = "header") + public final Set CUSTOM_HEADERS = new THashSet(CaseInsensitiveStringHashingStrategy.INSTANCE); + + private final HeaderParserRepository myRepository; public MisspelledHeaderInspection() { myRepository = HeaderParserRepository.getInstance(); @@ -58,49 +75,117 @@ public class MisspelledHeaderInspection extends LocalInspectionTool { public void visitElement(PsiElement element) { if (element instanceof Header) { Header header = (Header)element; - Collection matches = myRepository.getMatches(header.getName()); - if (!matches.isEmpty()) { - List fixes = ContainerUtil.newArrayListWithCapacity(MAX_SUGGESTIONS); - for (HeaderNameMatch match : matches) { - fixes.add(new HeaderNameSpellingQuickFix(header, match)); - if (fixes.size() == MAX_SUGGESTIONS) { - break; - } - } - holder.registerProblem( - header.getNameElement(), ManifestBundle.message("inspection.header.message"), - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, fixes.toArray(new HeaderNameSpellingQuickFix[fixes.size()]) - ); + String headerName = header.getName(); + + SortedSet matches = new TreeSet(); + addMatches(headerName, CUSTOM_HEADERS, matches); + addMatches(headerName, myRepository.getAllHeaderNames(), matches); + + Suggestion bestMatch = ContainerUtil.getFirstItem(matches); + if (bestMatch != null && headerName.equals(bestMatch.getWord())) { + return; + } + + List fixes = new ArrayList(); + for (Suggestion match : matches) { + fixes.add(new HeaderRenameQuickFix(header, match.getWord())); + if (fixes.size() == MAX_SUGGESTIONS) break; + } + if (bestMatch == null || bestMatch.getMetrics() > TYPO_DISTANCE) { + fixes.add(new CustomHeaderQuickFix(header, CUSTOM_HEADERS)); + } + holder.registerProblem( + header.getNameElement(), ManifestBundle.message("inspection.header.message"), + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, fixes.toArray(new LocalQuickFix[fixes.size()]) + ); + } + } + + private void addMatches(String headerName, Collection headers, SortedSet matches) { + for (String candidate : headers) { + int distance = EditDistance.optimalAlignment(headerName, candidate, false); + if (distance <= MAX_DISTANCE) { + matches.add(new Suggestion(candidate, distance)); } } } }; } - private static class HeaderNameSpellingQuickFix implements LocalQuickFix { - private final Header myHeader; + @Override + public JComponent createOptionsPanel() { + return new OptionsPanel(CUSTOM_HEADERS); + } + + private static class OptionsPanel extends JPanel { + public OptionsPanel(final Set headers) { + super(new BorderLayout(5, 5)); + + add(new JLabel(ManifestBundle.message("inspection.header.ui.label")), BorderLayout.NORTH); + + final JTextArea area = new JTextArea(""); + add(area, BorderLayout.CENTER); + if (!headers.isEmpty()) { + area.setText(StringUtil.join(new TreeSet(headers), "\n")); + } + + area.getDocument().addDocumentListener(new DocumentAdapter() { + @Override + protected void textChanged(DocumentEvent e) { + headers.clear(); + for (String line : StringUtil.split(area.getText(), "\n")) { + String header = line.trim(); + if (!header.isEmpty()) { + headers.add(header); + } + } + } + }); + } + } + + private static class HeaderRenameQuickFix extends AbstractManifestQuickFix { private final String myNewName; - private HeaderNameSpellingQuickFix(Header header, HeaderNameMatch match) { - myHeader = header; - myNewName = match.getHeaderName(); + private HeaderRenameQuickFix(Header header, String newName) { + super(header); + myNewName = newName; } @NotNull @Override - public String getName() { - return ManifestBundle.message("inspection.header.fix", myNewName); + public String getText() { + return ManifestBundle.message("inspection.header.rename.fix", myNewName); + } + + @Override + public void invoke(@NotNull Project project, @NotNull PsiFile file, @NotNull PsiElement startElement, @NotNull PsiElement endElement) { + ((Header)startElement).setName(myNewName); + } + } + + private static class CustomHeaderQuickFix extends AbstractManifestQuickFix { + private final String myHeaderName; + private final Collection myHeaders; + + private CustomHeaderQuickFix(Header header, Collection headers) { + super(header); + myHeaderName = header.getName(); + myHeaders = headers; } @NotNull @Override - public String getFamilyName() { - return ManifestBundle.message("inspection.group"); + public String getText() { + return ManifestBundle.message("inspection.header.remember.fix", myHeaderName); } @Override - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - myHeader.setName(myNewName); + public void invoke(@NotNull Project project, @NotNull PsiFile file, @NotNull PsiElement startElement, @NotNull PsiElement endElement) { + myHeaders.add(myHeaderName); + + InspectionProfile profile = InspectionProjectProfileManager.getInstance(project).getInspectionProfile(); + InspectionProfileManager.getInstance().fireProfileChanged(profile); } } } diff --git a/java/manifest/test/org/jetbrains/lang/manifest/ManifestPsiTest.java b/java/manifest/test/org/jetbrains/lang/manifest/ManifestPsiTest.java index 64d8fc27b1ed..7a82126508ee 100644 --- a/java/manifest/test/org/jetbrains/lang/manifest/ManifestPsiTest.java +++ b/java/manifest/test/org/jetbrains/lang/manifest/ManifestPsiTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ package org.jetbrains.lang.manifest; import com.intellij.psi.PsiFile; import com.intellij.testFramework.LightIdeaTestCase; import com.intellij.testFramework.LightPlatformTestCase; -import junit.framework.Assert; import org.jetbrains.annotations.Nullable; import org.jetbrains.lang.manifest.psi.Header; import org.jetbrains.lang.manifest.psi.HeaderValue; @@ -27,16 +26,16 @@ import org.jetbrains.lang.manifest.psi.ManifestFile; public class ManifestPsiTest extends LightIdeaTestCase { public void testFile() { ManifestFile file = createFile(""); - Assert.assertEquals(0, file.getSections().size()); - Assert.assertNull(file.getMainSection()); - Assert.assertEquals(0, file.getHeaders().size()); + assertEquals(0, file.getSections().size()); + assertNull(file.getMainSection()); + assertEquals(0, file.getHeaders().size()); file = createFile("Header: value\n\nAnother-Header: another value\n"); - Assert.assertEquals(2, file.getSections().size()); - Assert.assertNotNull(file.getMainSection()); - Assert.assertEquals(1, file.getHeaders().size()); - Assert.assertNotNull(file.getHeader("Header")); - Assert.assertNull(file.getHeader("Another-Header")); + assertEquals(2, file.getSections().size()); + assertNotNull(file.getMainSection()); + assertEquals(1, file.getHeaders().size()); + assertNotNull(file.getHeader("Header")); + assertNull(file.getHeader("Another-Header")); } public void testHeader() { @@ -54,15 +53,15 @@ public class ManifestPsiTest extends LightIdeaTestCase { private static void assertHeaderValue(ManifestFile file, String name, @Nullable String expected) { Header header = file.getHeader(name); - Assert.assertNotNull(header); + assertNotNull(header); HeaderValue value = header.getHeaderValue(); if (expected == null) { - Assert.assertNull(value); + assertNull(value); } else { - Assert.assertNotNull(value); - Assert.assertEquals(expected, value.getUnwrappedText()); + assertNotNull(value); + assertEquals(expected, value.getUnwrappedText()); } } } diff --git a/java/manifest/test/org/jetbrains/lang/manifest/MisspelledHeaderInspectionTest.java b/java/manifest/test/org/jetbrains/lang/manifest/MisspelledHeaderInspectionTest.java index c86149f15ca4..9ef828aefd61 100644 --- a/java/manifest/test/org/jetbrains/lang/manifest/MisspelledHeaderInspectionTest.java +++ b/java/manifest/test/org/jetbrains/lang/manifest/MisspelledHeaderInspectionTest.java @@ -19,25 +19,61 @@ import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; import org.jetbrains.lang.manifest.highlighting.MisspelledHeaderInspection; +import java.util.Collections; import java.util.List; public class MisspelledHeaderInspectionTest extends LightCodeInsightFixtureTestCase { - @Override - protected void setUp() throws Exception { - super.setUp(); - myFixture.enableInspections(new MisspelledHeaderInspection()); + public void testNoProblem() { + doTest("Manifest-Version: 1.0", 0); } - public void testNoProblem() { - myFixture.configureByText(ManifestFileTypeFactory.MANIFEST, "Manifest-Version: 1.0\n"); - assertEquals(0, myFixture.getAvailableIntentions().size()); + public void testMixedCase() { + doTest("manifest-version: 1.0", 1); + } + + public void testMissedDash() { + doTest("ManifestVersion: 1.0", 1); + } + + public void testMisspelled() { + doTest("MainFestVersion: 1.0", 1); + } + + public void testTotallyIncorrect() { + doTest("some_totally_impossible_header: -", 0); } public void testFix() { + myFixture.enableInspections(new MisspelledHeaderInspection()); myFixture.configureByText(ManifestFileTypeFactory.MANIFEST, "ManifestVersion: 1.0\n"); List intentions = myFixture.filterAvailableIntentions("Change to"); - assertTrue(intentions.size() > 0); + assertEquals(1, intentions.size()); myFixture.launchAction(intentions.get(0)); myFixture.checkResult("Manifest-Version: 1.0\n"); } + + public void testCustomHeader() { + MisspelledHeaderInspection inspection = new MisspelledHeaderInspection(); + inspection.CUSTOM_HEADERS.add("Custom-Header"); + myFixture.enableInspections(inspection); + myFixture.configureByText(ManifestFileTypeFactory.MANIFEST, "Custom-Header: -\n"); + myFixture.checkHighlighting(); + } + + public void testCustomHeaderFix() { + MisspelledHeaderInspection inspection = new MisspelledHeaderInspection(); + myFixture.enableInspections(inspection); + myFixture.configureByText(ManifestFileTypeFactory.MANIFEST, "Custom-Header: -\n"); + List intentions = myFixture.filterAvailableIntentions("Add "); + assertEquals(1, intentions.size()); + myFixture.launchAction(intentions.get(0)); + assertEquals(Collections.singleton("Custom-Header"), inspection.CUSTOM_HEADERS); + } + + private void doTest(String text, int expected) { + myFixture.enableInspections(new MisspelledHeaderInspection()); + myFixture.configureByText(ManifestFileTypeFactory.MANIFEST, text + "\n"); + myFixture.checkHighlighting(); + assertEquals(expected, myFixture.filterAvailableIntentions("Change to").size()); + } } From b4d925b0b08fa486cee354b2be9c3147f491ec63 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Sun, 14 Jun 2015 14:58:02 +0300 Subject: [PATCH 16/67] Don't throw IOException from dumpLogToStdout: wrap into RE in place To avoid extra wrapping in client test cases. --- .../testFramework/TestLoggerFactory.java | 21 ++++++++++++------- .../git4idea/GitCucumberWorld.java | 3 +-- .../tests/git4idea/test/GitPlatformTest.java | 12 +++-------- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/platform/testFramework/src/com/intellij/testFramework/TestLoggerFactory.java b/platform/testFramework/src/com/intellij/testFramework/TestLoggerFactory.java index 03771742ba3d..fa63dca8a260 100644 --- a/platform/testFramework/src/com/intellij/testFramework/TestLoggerFactory.java +++ b/platform/testFramework/src/com/intellij/testFramework/TestLoggerFactory.java @@ -95,16 +95,21 @@ public class TestLoggerFactory implements Logger.Factory { return PathManager.getSystemPath() + "/" + LOG_DIR; } - public static void dumpLogToStdout(@NotNull String testStartMarker) throws IOException { + public static void dumpLogToStdout(@NotNull String testStartMarker) { File ideaLog = new File(getTestLogDir(), "idea.log"); if (ideaLog.exists()) { - String logText = FileUtil.loadFile(ideaLog); - Pattern logStart = Pattern.compile("[0-9\\-, :\\[\\]]+(DEBUG|INFO|ERROR) - "); - System.out.println("\n\nIdea Log:"); - for (String line : StringUtil.splitByLines(logText.substring(Math.max(0, logText.lastIndexOf(testStartMarker))))) { - Matcher matcher = logStart.matcher(line); - int lineStart = matcher.lookingAt() ? matcher.end() : 0; - System.out.println(line.substring(lineStart)); + try { + String logText = FileUtil.loadFile(ideaLog); + Pattern logStart = Pattern.compile("[0-9\\-, :\\[\\]]+(DEBUG|INFO|ERROR) - "); + System.out.println("\n\nIdea Log:"); + for (String line : StringUtil.splitByLines(logText.substring(Math.max(0, logText.lastIndexOf(testStartMarker))))) { + Matcher matcher = logStart.matcher(line); + int lineStart = matcher.lookingAt() ? matcher.end() : 0; + System.out.println(line.substring(lineStart)); + } + } + catch (IOException e) { + throw new RuntimeException(e); } } } diff --git a/plugins/git4idea/test-stepdefs/git4idea/GitCucumberWorld.java b/plugins/git4idea/test-stepdefs/git4idea/GitCucumberWorld.java index 14c9ef89928d..e26aeb137997 100644 --- a/plugins/git4idea/test-stepdefs/git4idea/GitCucumberWorld.java +++ b/plugins/git4idea/test-stepdefs/git4idea/GitCucumberWorld.java @@ -49,7 +49,6 @@ import org.jetbrains.annotations.NotNull; import org.junit.Assert; import java.io.File; -import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -206,7 +205,7 @@ public class GitCucumberWorld { } @After(order = 0) - public void dumpToLog(@NotNull Scenario result) throws IOException { + public void dumpToLog(@NotNull Scenario result) { if (result.isFailed()) { TestLoggerFactory.dumpLogToStdout(getStartTestMarker()); } diff --git a/plugins/git4idea/tests/git4idea/test/GitPlatformTest.java b/plugins/git4idea/tests/git4idea/test/GitPlatformTest.java index 68b3b17651f3..7136e148b21b 100644 --- a/plugins/git4idea/tests/git4idea/test/GitPlatformTest.java +++ b/plugins/git4idea/tests/git4idea/test/GitPlatformTest.java @@ -43,7 +43,6 @@ import git4idea.repo.GitRepository; import git4idea.repo.GitRepositoryManager; import org.jetbrains.annotations.NotNull; -import java.io.IOException; import java.util.*; public abstract class GitPlatformTest extends UsefulTestCase { @@ -178,15 +177,10 @@ public abstract class GitPlatformTest extends UsefulTestCase { super.defaultRunBare(); } catch (Throwable throwable) { - try { - if (myTestStartedIndicator != null) { - TestLoggerFactory.dumpLogToStdout(myTestStartedIndicator); - } - throw throwable; - } - catch (IOException e) { - throw new RuntimeException(e); + if (myTestStartedIndicator != null) { + TestLoggerFactory.dumpLogToStdout(myTestStartedIndicator); } + throw throwable; } } From f96b2c8d06288d71950f080f48c951d1b1d68086 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Sun, 14 Jun 2015 15:06:48 +0300 Subject: [PATCH 17/67] [log test] dump debug log to stdout in case of test failure --- .../vcs/log/data/VcsLogRefresherTest.java | 9 ++++ .../vcs/log/impl/VcsLogPlatformTest.java | 52 +++++++++++++++++-- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/platform/vcs-log/impl/test/com/intellij/vcs/log/data/VcsLogRefresherTest.java b/platform/vcs-log/impl/test/com/intellij/vcs/log/data/VcsLogRefresherTest.java index 240d07c3b4c4..a5f46fb9cca5 100644 --- a/platform/vcs-log/impl/test/com/intellij/vcs/log/data/VcsLogRefresherTest.java +++ b/platform/vcs-log/impl/test/com/intellij/vcs/log/data/VcsLogRefresherTest.java @@ -87,6 +87,15 @@ public class VcsLogRefresherTest extends VcsLogPlatformTest { } } + @NotNull + @Override + protected Collection getDebugLogCategories() { + return Arrays.asList("#" + SingleTaskController.class.getName(), + "#" + VcsLogRefresherImpl.class.getName(), + "#" + VcsLogRefresherTest.class.getName(), + "#" + TestVcsLogProvider.class.getName()); + } + public void test_initialize_shows_short_history() throws InterruptedException, ExecutionException, TimeoutException { DataPack result = myLoader.readFirstBlock(); assertNotNull(result); diff --git a/platform/vcs-log/impl/test/com/intellij/vcs/log/impl/VcsLogPlatformTest.java b/platform/vcs-log/impl/test/com/intellij/vcs/log/impl/VcsLogPlatformTest.java index 1f21aaf5b79f..89c7bbb410a6 100644 --- a/platform/vcs-log/impl/test/com/intellij/vcs/log/impl/VcsLogPlatformTest.java +++ b/platform/vcs-log/impl/test/com/intellij/vcs/log/impl/VcsLogPlatformTest.java @@ -15,21 +15,34 @@ */ package com.intellij.vcs.log.impl; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.testFramework.PlatformTestCase; +import com.intellij.testFramework.TestLoggerFactory; import com.intellij.testFramework.UsefulTestCase; import com.intellij.testFramework.fixtures.IdeaProjectTestFixture; import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; +import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; +import java.util.Collection; +import java.util.Collections; + public abstract class VcsLogPlatformTest extends UsefulTestCase { - @NotNull protected Project myProject; - @NotNull protected VirtualFile myProjectRoot; - @NotNull protected String myProjectPath; + static { + Logger.setFactory(TestLoggerFactory.class); + } - @NotNull private IdeaProjectTestFixture myProjectFixture; + private static final Logger LOG = Logger.getInstance(VcsLogPlatformTest.class); + + protected Project myProject; + protected VirtualFile myProjectRoot; + protected String myProjectPath; + + private IdeaProjectTestFixture myProjectFixture; + private String myTestStartedIndicator; @SuppressWarnings({"JUnitTestCaseWithNonTrivialConstructors", "UnusedDeclaration"}) protected VcsLogPlatformTest() { @@ -39,6 +52,7 @@ public abstract class VcsLogPlatformTest extends UsefulTestCase { @Override protected void setUp() throws Exception { super.setUp(); + enableDebugLogging(); try { myProjectFixture = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(getTestName(true)).getFixture(); @@ -54,6 +68,22 @@ public abstract class VcsLogPlatformTest extends UsefulTestCase { myProjectPath = myProjectRoot.getPath(); } + private void enableDebugLogging() { + TestLoggerFactory.enableDebugLogging(myTestRootDisposable, ArrayUtil.toStringArray(getDebugLogCategories())); + myTestStartedIndicator = createTestStartedIndicator(); + LOG.info(myTestStartedIndicator); + } + + @NotNull + private String createTestStartedIndicator() { + return "Starting " + getClass().getName() + "." + getTestName(false) + Math.random(); + } + + @NotNull + protected Collection getDebugLogCategories() { + return Collections.emptyList(); + } + @Override public void tearDown() throws Exception { try { @@ -64,4 +94,16 @@ public abstract class VcsLogPlatformTest extends UsefulTestCase { } } -} + @Override + protected void defaultRunBare() throws Throwable { + try { + super.defaultRunBare(); + } + catch (Throwable throwable) { + if (myTestStartedIndicator != null) { + TestLoggerFactory.dumpLogToStdout(myTestStartedIndicator); + } + throw throwable; + } + } +} \ No newline at end of file From 1b4f4763395c29d95af496e3219719c4f07f3a53 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Sun, 14 Jun 2015 15:17:30 +0300 Subject: [PATCH 18/67] [log test] use AtomicInteger for the read first block counter The value returned to the test case might get out-of-date when int. Marking the field volatile seems to be enough, but let's make it AI to make the ++ operation synchronous if it will be needed in future. --- .../com/intellij/vcs/log/impl/TestVcsLogProvider.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/platform/vcs-log/impl/test/com/intellij/vcs/log/impl/TestVcsLogProvider.java b/platform/vcs-log/impl/test/com/intellij/vcs/log/impl/TestVcsLogProvider.java index 4728e2ea7ccd..c30d4c80fcbc 100644 --- a/platform/vcs-log/impl/test/com/intellij/vcs/log/impl/TestVcsLogProvider.java +++ b/platform/vcs-log/impl/test/com/intellij/vcs/log/impl/TestVcsLogProvider.java @@ -30,6 +30,7 @@ import java.awt.*; import java.util.*; import java.util.List; import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicInteger; import static org.junit.Assert.assertEquals; @@ -56,7 +57,7 @@ public class TestVcsLogProvider implements VcsLogProvider { @NotNull private final MockRefManager myRefManager; @NotNull private final ReducibleSemaphore myFullLogSemaphore; @NotNull private final ReducibleSemaphore myRefreshSemaphore; - private int myReadFirstBlockCounter; + @NotNull private AtomicInteger myReadFirstBlockCounter = new AtomicInteger(); private final Function myCommitToMetadataConvertor = new Function() { @@ -90,7 +91,7 @@ public class TestVcsLogProvider implements VcsLogProvider { throw new RuntimeException(e); } } - myReadFirstBlockCounter++; + myReadFirstBlockCounter.incrementAndGet(); assertRoot(root); List metadatas = ContainerUtil.map(myCommits.subList(0, requirements.getCommitCount()), myCommitToMetadataConvertor); @@ -197,11 +198,11 @@ public class TestVcsLogProvider implements VcsLogProvider { } public void resetReadFirstBlockCounter() { - myReadFirstBlockCounter = 0; + myReadFirstBlockCounter.set(0); } public int getReadFirstBlockCounter() { - return myReadFirstBlockCounter; + return myReadFirstBlockCounter.get(); } @Nullable From cbebf8b7157530b9a8d96e257ec17fb60b7c03ed Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Sun, 14 Jun 2015 15:11:56 +0300 Subject: [PATCH 19/67] [log] add some debug logging to catch the test blinking --- .../src/com/intellij/vcs/log/data/DataPack.java | 4 ++++ .../vcs/log/data/SingleTaskController.java | 9 +++++++++ .../vcs/log/data/VcsLogRefresherImpl.java | 15 ++++++++++++++- .../vcs/log/data/VcsLogRefresherTest.java | 5 +++++ .../intellij/vcs/log/impl/TestVcsLogProvider.java | 13 +++++++++---- 5 files changed, 41 insertions(+), 5 deletions(-) diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataPack.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataPack.java index 859552d6e6c9..e213d7b735fb 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataPack.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataPack.java @@ -116,4 +116,8 @@ public class DataPack { return myFull; } + @Override + public String toString() { + return "{DataPack. " + myPermanentGraph.getAllCommits().size() + " commits in " + myLogProviders.keySet().size() + " roots}"; + } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/SingleTaskController.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/SingleTaskController.java index 8d3e0d3fd9bf..0b9c0b540df0 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/SingleTaskController.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/SingleTaskController.java @@ -15,6 +15,7 @@ */ package com.intellij.vcs.log.data; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.util.Consumer; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -36,6 +37,8 @@ import java.util.List; */ public abstract class SingleTaskController { + private static final Logger LOG = Logger.getInstance(SingleTaskController.class); + @NotNull private final Consumer myResultHandler; @NotNull private final Object LOCK = new Object(); @@ -55,8 +58,10 @@ public abstract class SingleTaskController { public final void request(@NotNull Request requests) { synchronized (LOCK) { myAwaitingRequests.add(requests); + LOG.debug("Added requests: " + requests); if (!myActive) { startNewBackgroundTask(); + LOG.debug("Started a new bg task"); myActive = true; } } @@ -77,6 +82,7 @@ public abstract class SingleTaskController { synchronized (LOCK) { List requests = myAwaitingRequests; myAwaitingRequests = ContainerUtil.newArrayList(); + LOG.debug("Popped requests: " + requests); return requests; } } @@ -89,13 +95,16 @@ public abstract class SingleTaskController { protected final void taskCompleted(@Nullable Result result) { if (result != null) { myResultHandler.consume(result); + LOG.debug("Handled result: " + result); } synchronized (LOCK) { if (myAwaitingRequests.isEmpty()) { myActive = false; + LOG.debug("No more requests"); } else { startNewBackgroundTask(); + LOG.debug("Restarted a bg task"); } } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogRefresherImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogRefresherImpl.java index 65598d6914be..56f1d747b57d 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogRefresherImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogRefresherImpl.java @@ -91,6 +91,7 @@ public class VcsLogRefresherImpl implements VcsLogRefresher { UIUtil.invokeLaterIfNeeded(new Runnable() { @Override public void run() { + LOG.debug("Starting a background task..."); ((ProgressManagerImpl)ProgressManager.getInstance()).runProcessWithProgressAsynchronously(refreshTask); } }); @@ -208,11 +209,13 @@ public class VcsLogRefresherImpl implements VcsLogRefresher { @Override public void run(@NotNull ProgressIndicator indicator) { + LOG.debug("Refresh task started"); indicator.setIndeterminate(true); DataPack dataPack = myCurrentDataPack; while (true) { List requests = mySingleTaskController.popRequests(); Collection rootsToRefresh = getRootsToRefresh(requests); + LOG.debug("Requests: " + requests + ". roots to refresh: " + rootsToRefresh); if (rootsToRefresh.isEmpty()) { mySingleTaskController.taskCompleted(dataPack); break; @@ -371,12 +374,22 @@ public class VcsLogRefresherImpl implements VcsLogRefresher { } private static class RefreshRequest { - private static final RefreshRequest RELOAD_ALL = new RefreshRequest(Collections.emptyList()); + private static final RefreshRequest RELOAD_ALL = new RefreshRequest(Collections.emptyList()) { + @Override + public String toString() { + return "RELOAD_ALL"; + } + }; private final Collection rootsToRefresh; RefreshRequest(@NotNull Collection rootsToRefresh) { this.rootsToRefresh = rootsToRefresh; } + + @Override + public String toString() { + return "{" + rootsToRefresh + "}"; + } } private static abstract class ProviderIterator { diff --git a/platform/vcs-log/impl/test/com/intellij/vcs/log/data/VcsLogRefresherTest.java b/platform/vcs-log/impl/test/com/intellij/vcs/log/data/VcsLogRefresherTest.java index a5f46fb9cca5..acf26d51c312 100644 --- a/platform/vcs-log/impl/test/com/intellij/vcs/log/data/VcsLogRefresherTest.java +++ b/platform/vcs-log/impl/test/com/intellij/vcs/log/data/VcsLogRefresherTest.java @@ -16,6 +16,7 @@ package com.intellij.vcs.log.data; import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.progress.impl.ProgressManagerImpl; @@ -38,6 +39,8 @@ import static com.intellij.vcs.log.TimedCommitParser.log; public class VcsLogRefresherTest extends VcsLogPlatformTest { + private static final Logger LOG = Logger.getInstance(VcsLogRefresherTest.class); + private static final int RECENT_COMMITS_COUNT = 2; public static final Consumer FAILING_EXCEPTION_HANDLER = new Consumer() { @Override @@ -209,7 +212,9 @@ public class VcsLogRefresherTest extends VcsLogPlatformTest { UIUtil.invokeLaterIfNeeded(new Runnable() { @Override public void run() { + LOG.debug("Starting a background task..."); myStartedTasks.add(((ProgressManagerImpl)ProgressManager.getInstance()).runProcessWithProgressAsynchronously(refreshTask)); + LOG.debug(myStartedTasks.size() + " started tasks"); } }); } diff --git a/platform/vcs-log/impl/test/com/intellij/vcs/log/impl/TestVcsLogProvider.java b/platform/vcs-log/impl/test/com/intellij/vcs/log/impl/TestVcsLogProvider.java index c30d4c80fcbc..c4e76891a06f 100644 --- a/platform/vcs-log/impl/test/com/intellij/vcs/log/impl/TestVcsLogProvider.java +++ b/platform/vcs-log/impl/test/com/intellij/vcs/log/impl/TestVcsLogProvider.java @@ -15,6 +15,7 @@ */ package com.intellij.vcs.log.impl; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.VcsKey; import com.intellij.openapi.vcs.changes.committed.MockAbstractVcs; @@ -36,6 +37,8 @@ import static org.junit.Assert.assertEquals; public class TestVcsLogProvider implements VcsLogProvider { + private static final Logger LOG = Logger.getInstance(TestVcsLogProvider.class); + public static final VcsRefType BRANCH_TYPE = new VcsRefType() { @Override public boolean isBranch() { @@ -80,9 +83,8 @@ public class TestVcsLogProvider implements VcsLogProvider { @NotNull @Override - public DetailedLogData readFirstBlock(@NotNull final VirtualFile root, - @NotNull Requirements requirements) - throws VcsException { + public DetailedLogData readFirstBlock(@NotNull final VirtualFile root, @NotNull Requirements requirements) throws VcsException { + LOG.debug("readFirstBlock began"); if (requirements instanceof VcsLogProviderRequirementsEx && ((VcsLogProviderRequirementsEx)requirements).isRefresh()) { try { myRefreshSemaphore.acquire(); @@ -91,7 +93,8 @@ public class TestVcsLogProvider implements VcsLogProvider { throw new RuntimeException(e); } } - myReadFirstBlockCounter.incrementAndGet(); + int readFirstBlockCounter = myReadFirstBlockCounter.incrementAndGet(); + LOG.debug("readFirstBlock passed the semaphore: " + readFirstBlockCounter); assertRoot(root); List metadatas = ContainerUtil.map(myCommits.subList(0, requirements.getCommitCount()), myCommitToMetadataConvertor); @@ -101,12 +104,14 @@ public class TestVcsLogProvider implements VcsLogProvider { @NotNull @Override public LogData readAllHashes(@NotNull VirtualFile root, @NotNull Consumer commitConsumer) throws VcsException { + LOG.debug("readAllHashes"); try { myFullLogSemaphore.acquire(); } catch (InterruptedException e) { throw new RuntimeException(e); } + LOG.debug("readAllHashes passed the semaphore"); assertRoot(root); for (TimedVcsCommit commit : myCommits) { commitConsumer.consume(commit); From 3dfa1f665e5bf764dca188de85f0610910fb9a2d Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Sun, 14 Jun 2015 14:29:03 +0200 Subject: [PATCH 20/67] acceletatorSelectionForeground should be white --- .../src/com/intellij/ide/ui/laf/intellijlaf_mac.properties | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellijlaf_mac.properties b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellijlaf_mac.properties index a193b504a3f4..ab711d9ae3e6 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellijlaf_mac.properties +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellijlaf_mac.properties @@ -1,3 +1,4 @@ +# suppress inspection "UnusedProperty" for whole file intellijlaf.background=ececec window=ececec @@ -9,6 +10,7 @@ Spinner.background=ececec Spinner.darcula.disabledButtonColor=ececec ComboBoxUI=com.intellij.ide.ui.laf.intellij.MacIntelliJComboBoxUI +MenuItem.acceleratorSelectionForeground=ffffff SplitPane.highlight=ececec From 9e3afbc15a64637946c5cbec5430cafff74f7471 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Sun, 14 Jun 2015 14:30:17 +0200 Subject: [PATCH 21/67] new mac laf: patch all fonts to system font face, but munues to Lucida Grande --- .../com/intellij/ide/ui/laf/IntelliJLaf.java | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/IntelliJLaf.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/IntelliJLaf.java index 363e46d03ffa..ab038a3f386a 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/IntelliJLaf.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/IntelliJLaf.java @@ -22,6 +22,7 @@ import javax.swing.*; import javax.swing.plaf.FontUIResource; import javax.swing.plaf.metal.DefaultMetalTheme; import java.awt.*; +import java.util.HashSet; /** * @author Konstantin Bulenkov @@ -52,11 +53,23 @@ public class IntelliJLaf extends DarculaLaf { } private static void installMacOSXFonts(UIDefaults defaults) { - FontUIResource font = new FontUIResource("HelveticaNeue-CondensedBlack", Font.PLAIN, 13); - defaults.put("Label.font", font); - defaults.put("CheckBox.font", font); - defaults.put("RadioButton.font", font); - defaults.put("ComboBox.font", font); + String face = "HelveticaNeue-CondensedBlack"; + LafManagerImpl.initFontDefaults(defaults, face, 13); + for (Object key : new HashSet(defaults.keySet())) { + Object value = defaults.get(key); + if (value instanceof FontUIResource) { + FontUIResource font = (FontUIResource)value; + if (font.getFamily().equals("Lucida Grande") || font.getFamily().equals("Serif")) { + if (!key.toString().contains("Menu")) { + defaults.put(key, new FontUIResource(face, font.getStyle(), font.getSize())); + } + } + } + } + Font menuFont = new Font("Lucida Grande", Font.PLAIN, 14); + defaults.put("Menu.font", menuFont); + defaults.put("MenuItem.font", menuFont); + defaults.put("MenuItem.acceleratorFont", menuFont); } public static boolean isGraphite() { From f8e73a22e6c2134f456ce583e44df09556f3326d Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Sun, 14 Jun 2015 14:38:23 +0200 Subject: [PATCH 22/67] system colors for popup menu and other menues --- .../src/com/intellij/ide/ui/laf/intellijlaf_mac.properties | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellijlaf_mac.properties b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellijlaf_mac.properties index ab711d9ae3e6..751c0875964f 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellijlaf_mac.properties +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellijlaf_mac.properties @@ -11,6 +11,9 @@ Spinner.darcula.disabledButtonColor=ececec ComboBoxUI=com.intellij.ide.ui.laf.intellij.MacIntelliJComboBoxUI MenuItem.acceleratorSelectionForeground=ffffff +PopupMenu.background=f6f6f6 +MenuItem.background=f6f6f6 +Menu.background=f6f6f6 SplitPane.highlight=ececec From 149179a4e88ffedef046ef9d549fb31e047715de Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Sun, 14 Jun 2015 14:43:12 +0200 Subject: [PATCH 23/67] setup inactive backgrounds for all components --- .../src/com/intellij/ide/ui/laf/intellijlaf_mac.properties | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellijlaf_mac.properties b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellijlaf_mac.properties index 751c0875964f..07d3d6747b99 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellijlaf_mac.properties +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellijlaf_mac.properties @@ -1,5 +1,7 @@ # suppress inspection "UnusedProperty" for whole file intellijlaf.background=ececec +intellijlaf.selectionBackgroundInactive=dcdcdc +intellijlaf.selectionInactiveBackground=dcdcdc window=ececec CheckBoxUI=com.intellij.ide.ui.laf.intellij.MacIntelliJCheckBoxUI From d2aeaeb2d4c32ca47981dcf85968e82cce0b1fa4 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 12 Jun 2015 11:19:32 +0200 Subject: [PATCH 24/67] cleanup --- .../util/ui/tree/AbstractFileTreeTable.java | 39 +++++++------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/util/ui/tree/AbstractFileTreeTable.java b/platform/lang-impl/src/com/intellij/util/ui/tree/AbstractFileTreeTable.java index 416d584441b0..17d5951b93ad 100644 --- a/platform/lang-impl/src/com/intellij/util/ui/tree/AbstractFileTreeTable.java +++ b/platform/lang-impl/src/com/intellij/util/ui/tree/AbstractFileTreeTable.java @@ -60,6 +60,7 @@ public abstract class AbstractFileTreeTable extends TreeTable { super(new MyModel(project, valueClass, valueTitle, filter)); myProject = project; + //noinspection unchecked myModel = (MyModel)getTableModel(); myModel.setTreeTable(this); @@ -96,21 +97,13 @@ public abstract class AbstractFileTreeTable extends TreeTable { } FileNode fileNode = (FileNode)value; VirtualFile file = fileNode.getObject(); - if (fileNode.getParent() instanceof FileNode) { - setText(file.getName()); - } - else { - setText(file.getPresentableUrl()); - } - - Icon icon = file.isDirectory() ? PlatformIcons.DIRECTORY_CLOSED_ICON : IconUtil.getIcon(file, 0, null); - setIcon(icon); + setText(fileNode.getParent() instanceof FileNode ? file.getName() : file.getPresentableUrl()); + setIcon(file.isDirectory() ? PlatformIcons.DIRECTORY_CLOSED_ICON : IconUtil.getIcon(file, 0, null)); return this; } }); getTableHeader().setReorderingAllowed(false); - setSelectionMode(ListSelectionModel.SINGLE_SELECTION); setPreferredScrollableViewportSize(new Dimension(300, getRowHeight() * 10)); @@ -140,8 +133,7 @@ public abstract class AbstractFileTreeTable extends TreeTable { public static void press(final Container comboComponent) { if (comboComponent instanceof JButton) { - final JButton button = (JButton)comboComponent; - button.doClick(); + ((JButton)comboComponent).doClick(); } else { for (int i = 0; i < comboComponent.getComponentCount(); i++) { @@ -191,8 +183,7 @@ public abstract class AbstractFileTreeTable extends TreeTable { public void reset(@NotNull Map mappings) { myModel.reset(mappings); - final TreeNode root = (TreeNode)myModel.getRoot(); - myModel.nodeChanged(root); + myModel.nodeChanged((TreeNode)myModel.getRoot()); getTree().setModel(null); getTree().setModel(myModel); TreeUtil.expandRootChildIfOnlyOne(getTree()); @@ -223,7 +214,6 @@ public abstract class AbstractFileTreeTable extends TreeTable { } } - private static class MyModel extends DefaultTreeModel implements TreeTableModel { private final Map myCurrentMapping = new HashMap(); private final Class myValueClass; @@ -310,11 +300,14 @@ public abstract class AbstractFileTreeTable extends TreeTable { @Override public void setValueAt(final Object aValue, final Object node, final int column) { - final DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)node; - final Object userObject = treeNode.getUserObject(); - if (userObject instanceof Project) return; + final Object userObject = ((DefaultMutableTreeNode)node).getUserObject(); + if (userObject instanceof Project) { + return; + } + final VirtualFile file = (VirtualFile)userObject; - final T t = (T)aValue; + @SuppressWarnings("unchecked") + T t = (T)aValue; if (t == null || myTreeTable.isNullObject(t)) { myCurrentMapping.remove(file); } @@ -426,8 +419,8 @@ public abstract class AbstractFileTreeTable extends TreeTable { public void clearCachedChildren() { if (children != null) { for (Object child : children) { - ConvenientNode node = (ConvenientNode)child; - node.clearCachedChildren(); + //noinspection unchecked + ((ConvenientNode)child).clearCachedChildren(); } } removeAllChildren(); @@ -451,14 +444,12 @@ public abstract class AbstractFileTreeTable extends TreeTable { @Override protected void appendChildrenTo(@NotNull final Collection children) { - VirtualFile[] childrenf = getObject().getChildren(); ProjectFileIndex fileIndex = ProjectRootManager.getInstance(myProject).getFileIndex(); - for (VirtualFile child : childrenf) { + for (VirtualFile child : getObject().getChildren()) { if (myFilter.accept(child) && fileIndex.isInContent(child)) { children.add(new FileNode(child, myProject, myFilter)); } } } } - } From 95df71eac54ccb0a4c50adbfd226d2867facbb9b Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 12 Jun 2015 13:02:34 +0200 Subject: [PATCH 25/67] show excluded file in file tree (remote urls) --- .../util/ui/tree/AbstractFileTreeTable.java | 24 ++++++++-- .../util/ui/tree/NonContentFileFilter.java | 47 +++++++++++++++++++ 2 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/util/ui/tree/NonContentFileFilter.java diff --git a/platform/lang-impl/src/com/intellij/util/ui/tree/AbstractFileTreeTable.java b/platform/lang-impl/src/com/intellij/util/ui/tree/AbstractFileTreeTable.java index 17d5951b93ad..5287db9087ce 100644 --- a/platform/lang-impl/src/com/intellij/util/ui/tree/AbstractFileTreeTable.java +++ b/platform/lang-impl/src/com/intellij/util/ui/tree/AbstractFileTreeTable.java @@ -48,7 +48,7 @@ import java.awt.*; import java.util.*; import java.util.List; -public abstract class AbstractFileTreeTable extends TreeTable { +public class AbstractFileTreeTable extends TreeTable { private final MyModel myModel; private final Project myProject; @@ -57,7 +57,16 @@ public abstract class AbstractFileTreeTable extends TreeTable { @NotNull String valueTitle, @NotNull VirtualFileFilter filter, boolean showProjectNode) { - super(new MyModel(project, valueClass, valueTitle, filter)); + this(project, valueClass, valueTitle, filter, showProjectNode, true); + } + + public AbstractFileTreeTable(@NotNull Project project, + @NotNull Class valueClass, + @NotNull String valueTitle, + @NotNull VirtualFileFilter filter, + boolean showProjectNode, + boolean filterNonContentFiles) { + super(new MyModel(project, valueClass, valueTitle, filterNonContentFiles ? new NonContentFileFilter(project, filter) : filter)); myProject = project; //noinspection unchecked @@ -85,6 +94,7 @@ public abstract class AbstractFileTreeTable extends TreeTable { getTree().setShowsRootHandles(true); getTree().setLineStyleAngled(); getTree().setRootVisible(showProjectNode); + final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); getTree().setCellRenderer(new DefaultTreeCellRenderer() { @Override public Component getTreeCellRendererComponent(final JTree tree, final Object value, final boolean sel, final boolean expanded, @@ -98,7 +108,12 @@ public abstract class AbstractFileTreeTable extends TreeTable { FileNode fileNode = (FileNode)value; VirtualFile file = fileNode.getObject(); setText(fileNode.getParent() instanceof FileNode ? file.getName() : file.getPresentableUrl()); - setIcon(file.isDirectory() ? PlatformIcons.DIRECTORY_CLOSED_ICON : IconUtil.getIcon(file, 0, null)); + if (file.isDirectory()) { + setIcon(fileIndex.isExcluded(file) ? AllIcons.Modules.ExcludeRoot : PlatformIcons.DIRECTORY_CLOSED_ICON); + } + else { + setIcon(IconUtil.getIcon(file, 0, null)); + } return this; } }); @@ -444,9 +459,8 @@ public abstract class AbstractFileTreeTable extends TreeTable { @Override protected void appendChildrenTo(@NotNull final Collection children) { - ProjectFileIndex fileIndex = ProjectRootManager.getInstance(myProject).getFileIndex(); for (VirtualFile child : getObject().getChildren()) { - if (myFilter.accept(child) && fileIndex.isInContent(child)) { + if (myFilter.accept(child)) { children.add(new FileNode(child, myProject, myFilter)); } } diff --git a/platform/lang-impl/src/com/intellij/util/ui/tree/NonContentFileFilter.java b/platform/lang-impl/src/com/intellij/util/ui/tree/NonContentFileFilter.java new file mode 100644 index 000000000000..3eeef5869217 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/util/ui/tree/NonContentFileFilter.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util.ui.tree; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileFilter; +import org.jetbrains.annotations.NotNull; + +public class NonContentFileFilter implements VirtualFileFilter { + private final Project project; + private final VirtualFileFilter filter; + + private ProjectFileIndex fileIndex; + + public NonContentFileFilter(@NotNull Project project, @NotNull VirtualFileFilter filter) { + this.project = project; + this.filter = filter; + } + + @Override + public boolean accept(@NotNull VirtualFile file) { + if (!filter.accept(file)) { + return false; + } + + if (fileIndex == null) { + fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); + } + return fileIndex.isInContent(file); + } +} From 163c76b9bd60a96b1cfdf56f83b2a50eb75dee3e Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Sun, 14 Jun 2015 19:39:35 +0200 Subject: [PATCH 26/67] init WEB-16829 "too soon" typescript breakpoints are not hit --- .../src/com/intellij/util/UrlImpl.java | 4 +-- .../jetbrains/io/ChannelBufferToString.java | 17 ++----------- .../src/org/jetbrains/io/JsonReaderEx.java | 18 +++++++++++++ .../src/org/jetbrains/io/MessageDecoder.java | 2 +- .../jetbrains/debugger/DebugEventAdapter.java | 2 +- .../debugger/DebugEventListener.java | 2 +- .../org/jetbrains/debugger/ScriptManager.java | 4 +++ .../debugger/ScriptManagerBaseEx.java | 7 +++++- .../debugger/sourcemap/SourceMapDecoder.java | 17 +++---------- .../debugger/sourcemap/SourceResolver.java | 25 +++++++++++++------ 10 files changed, 56 insertions(+), 42 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/util/UrlImpl.java b/platform/platform-impl/src/com/intellij/util/UrlImpl.java index 023865164405..6876998e0a03 100644 --- a/platform/platform-impl/src/com/intellij/util/UrlImpl.java +++ b/platform/platform-impl/src/com/intellij/util/UrlImpl.java @@ -44,7 +44,7 @@ public final class UrlImpl implements Url { public UrlImpl(@Nullable String scheme, @Nullable String authority, @Nullable String path, @Nullable String parameters) { this.scheme = scheme; this.authority = authority; - this.path = StringUtil.isEmpty(path) && !StringUtil.isEmpty(authority) ? "/" : StringUtil.notNullize(path); + this.path = StringUtil.notNullize(path); this.parameters = StringUtil.nullize(parameters); } @@ -112,7 +112,7 @@ public final class UrlImpl implements Url { // relative path - special url, encoding is not required // authority is null in case of URI - if ((path.charAt(0) != '/' || authority == null) && !isInLocalFileSystem()) { + if ((authority == null || (!path.isEmpty() && path.charAt(0) != '/')) && !isInLocalFileSystem()) { return toDecodedForm(); } diff --git a/platform/platform-impl/src/org/jetbrains/io/ChannelBufferToString.java b/platform/platform-impl/src/org/jetbrains/io/ChannelBufferToString.java index df61a92f987e..9bf08d705a4e 100644 --- a/platform/platform-impl/src/org/jetbrains/io/ChannelBufferToString.java +++ b/platform/platform-impl/src/org/jetbrains/io/ChannelBufferToString.java @@ -1,6 +1,5 @@ package org.jetbrains.io; -import com.intellij.util.text.CharArrayCharSequence; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.buffer.ByteBufUtilEx; @@ -13,13 +12,13 @@ import java.nio.CharBuffer; public final class ChannelBufferToString { @NotNull public static CharSequence readChars(@NotNull ByteBuf buffer) throws IOException { - return new MyCharArrayCharSequence(readIntoCharBuffer(buffer, buffer.readableBytes(), null)); + return new JsonReaderEx.CharSequenceBackedByChars(readIntoCharBuffer(buffer, buffer.readableBytes(), null)); } @SuppressWarnings("unused") @NotNull public static CharSequence readChars(@NotNull ByteBuf buffer, int byteCount) throws IOException { - return new MyCharArrayCharSequence(readIntoCharBuffer(buffer, byteCount, null)); + return new JsonReaderEx.CharSequenceBackedByChars(readIntoCharBuffer(buffer, byteCount, null)); } @NotNull @@ -34,16 +33,4 @@ public final class ChannelBufferToString { public static void writeIntAsAscii(int value, @NotNull ByteBuf buffer) { ByteBufUtil.writeAscii(buffer, new StringBuilder().append(value)); } - - // we must return string on subSequence() - JsonReaderEx will call toString in any case - public static final class MyCharArrayCharSequence extends CharArrayCharSequence { - public MyCharArrayCharSequence(@NotNull CharBuffer charBuffer) { - super(charBuffer.array(), charBuffer.arrayOffset(), charBuffer.position()); - } - - @Override - public CharSequence subSequence(int start, int end) { - return start == 0 && end == length() ? this : new String(myChars, myStart + start, end - start); - } - } } \ No newline at end of file diff --git a/platform/platform-impl/src/org/jetbrains/io/JsonReaderEx.java b/platform/platform-impl/src/org/jetbrains/io/JsonReaderEx.java index 37cf6bf7066c..aa783b158764 100644 --- a/platform/platform-impl/src/org/jetbrains/io/JsonReaderEx.java +++ b/platform/platform-impl/src/org/jetbrains/io/JsonReaderEx.java @@ -17,10 +17,12 @@ package org.jetbrains.io; import com.google.gson.JsonParseException; import com.google.gson.stream.JsonToken; +import com.intellij.util.text.CharArrayCharSequence; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.Closeable; +import java.nio.CharBuffer; import java.util.Arrays; public final class JsonReaderEx implements Closeable { @@ -116,6 +118,22 @@ public final class JsonReaderEx implements Closeable { this.stack = stack; } + // we must return string on subSequence() - JsonReaderEx will call toString in any case + public static final class CharSequenceBackedByChars extends CharArrayCharSequence { + public CharSequenceBackedByChars(@NotNull CharBuffer charBuffer) { + super(charBuffer.array(), charBuffer.arrayOffset(), charBuffer.position()); + } + + public CharSequenceBackedByChars(@NotNull char[] chars, int start, int end) { + super(chars, start, end); + } + + @Override + public CharSequence subSequence(int start, int end) { + return start == 0 && end == length() ? this : new String(myChars, myStart + start, end - start); + } + } + private final static class JsonScope { /** * An array with no elements requires no separators or newlines before diff --git a/platform/platform-impl/src/org/jetbrains/io/MessageDecoder.java b/platform/platform-impl/src/org/jetbrains/io/MessageDecoder.java index 6746a4c1d2f1..3f672c3916d6 100644 --- a/platform/platform-impl/src/org/jetbrains/io/MessageDecoder.java +++ b/platform/platform-impl/src/org/jetbrains/io/MessageDecoder.java @@ -44,7 +44,7 @@ public abstract class MessageDecoder extends Decoder { chunkedContent = null; consumedContentByteCount = 0; } - return new ChannelBufferToString.MyCharArrayCharSequence(ChannelBufferToString.readIntoCharBuffer(input, required, charBuffer)); + return new JsonReaderEx.CharSequenceBackedByChars(ChannelBufferToString.readIntoCharBuffer(input, required, charBuffer)); } } diff --git a/platform/script-debugger/backend/src/org/jetbrains/debugger/DebugEventAdapter.java b/platform/script-debugger/backend/src/org/jetbrains/debugger/DebugEventAdapter.java index 3ee0bb525532..0724cfe6c989 100644 --- a/platform/script-debugger/backend/src/org/jetbrains/debugger/DebugEventAdapter.java +++ b/platform/script-debugger/backend/src/org/jetbrains/debugger/DebugEventAdapter.java @@ -18,7 +18,7 @@ public abstract class DebugEventAdapter implements DebugEventListener { } @Override - public void scriptAdded(@NotNull Script script, @Nullable String sourceMapData) { + public void scriptAdded(@NotNull Script script, @Nullable CharSequence sourceMapData) { } @Override diff --git a/platform/script-debugger/backend/src/org/jetbrains/debugger/DebugEventListener.java b/platform/script-debugger/backend/src/org/jetbrains/debugger/DebugEventListener.java index 7a523229adc7..d3f6ac2a649f 100755 --- a/platform/script-debugger/backend/src/org/jetbrains/debugger/DebugEventListener.java +++ b/platform/script-debugger/backend/src/org/jetbrains/debugger/DebugEventListener.java @@ -29,7 +29,7 @@ public interface DebugEventListener extends EventListener { /** * Reports that a new script has been loaded. */ - void scriptAdded(@NotNull Script script, @Nullable String sourceMapData); + void scriptAdded(@NotNull Script script, @Nullable CharSequence sourceMapData); void sourceMapFound(@NotNull Script script, @Nullable Url sourceMapUrl, @NotNull String sourceMapData); diff --git a/platform/script-debugger/backend/src/org/jetbrains/debugger/ScriptManager.java b/platform/script-debugger/backend/src/org/jetbrains/debugger/ScriptManager.java index 1132b284e16d..4b3a20e0f6ec 100644 --- a/platform/script-debugger/backend/src/org/jetbrains/debugger/ScriptManager.java +++ b/platform/script-debugger/backend/src/org/jetbrains/debugger/ScriptManager.java @@ -2,6 +2,7 @@ package org.jetbrains.debugger; import com.intellij.util.CommonProcessors; import com.intellij.util.Processor; +import com.intellij.util.Url; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.concurrency.Promise; @@ -37,6 +38,9 @@ public interface ScriptManager { @Nullable Script findScriptByUrl(@NotNull String rawUrl); + @Nullable + Script findScriptByUrl(@NotNull Url url); + @Nullable Script findScriptById(@NotNull String id); diff --git a/platform/script-debugger/backend/src/org/jetbrains/debugger/ScriptManagerBaseEx.java b/platform/script-debugger/backend/src/org/jetbrains/debugger/ScriptManagerBaseEx.java index 05cca7fd4317..b8286f77b15c 100644 --- a/platform/script-debugger/backend/src/org/jetbrains/debugger/ScriptManagerBaseEx.java +++ b/platform/script-debugger/backend/src/org/jetbrains/debugger/ScriptManagerBaseEx.java @@ -35,7 +35,12 @@ public abstract class ScriptManagerBaseEx