generic API for console folding
authorpeter <peter.gromov@jetbrains.com>
Thu, 4 Mar 2010 17:50:44 +0000 (17:50 +0000)
committerpeter <peter.gromov@jetbrains.com>
Thu, 4 Mar 2010 17:50:44 +0000 (17:50 +0000)
java/execution/openapi/src/com/intellij/execution/filters/AuxiliaryCallsFolding.java [new file with mode: 0644]
java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilter.java
java/execution/openapi/src/com/intellij/execution/filters/ReflectionStackFrameFilter.java
java/execution/openapi/src/com/intellij/execution/filters/StackFrameFilter.java
platform/lang-api/src/com/intellij/execution/filters/Filter.java
platform/lang-impl/src/com/intellij/execution/ConsoleFolding.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java
platform/platform-resources/src/META-INF/LangExtensionPoints.xml
plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/filters/GroovyDebuggerClassFilterProvider.java
resources/src/META-INF/IdeaPlugin.xml

diff --git a/java/execution/openapi/src/com/intellij/execution/filters/AuxiliaryCallsFolding.java b/java/execution/openapi/src/com/intellij/execution/filters/AuxiliaryCallsFolding.java
new file mode 100644 (file)
index 0000000..5240ad7
--- /dev/null
@@ -0,0 +1,33 @@
+package com.intellij.execution.filters;
+
+import com.intellij.execution.ConsoleFolding;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.util.Trinity;
+
+import java.util.List;
+
+/**
+ * @author peter
+ */
+public class AuxiliaryCallsFolding extends ConsoleFolding {
+  @Override
+  public boolean shouldFoldLine(String line) {
+    final Trinity<String, String, TextRange> pair = ExceptionFilter.parseExceptionLine(line);
+    return pair != null && shouldFold(pair.first);
+  }
+
+  private static boolean shouldFold(String className) {
+    for (StackFrameFilter provider : StackFrameFilter.EP_NAME.getExtensions()) {
+      if (provider.isAuxiliaryFrame(className, "")) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+
+  @Override
+  public String getPlaceholderText(List<String> lines) {
+    return " <" + lines.size() + " internal calls>";
+  }
+}
index 701cb8de59f252e02d95a42d196c6962f1ca231c..6b05b21f3fa60eef290edfc3323e6424dc66abe9 100644 (file)
@@ -21,6 +21,8 @@ import com.intellij.openapi.editor.markup.TextAttributes;
 import com.intellij.openapi.project.DumbAware;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.util.Trinity;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.JavaPsiFacade;
 import com.intellij.psi.PsiClass;
@@ -30,6 +32,7 @@ import com.intellij.psi.search.GlobalSearchScope;
 import com.intellij.util.ui.UIUtil;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.awt.*;
 
@@ -51,7 +54,8 @@ public class ExceptionFilter implements Filter, DumbAware {
     mySearchScope = scope;
   }
 
-  public Result applyFilter(final String line, final int textEndOffset) {
+  @Nullable
+  static Trinity<String, String, TextRange> parseExceptionLine(final String line) {
     int atIndex;
     if (line.startsWith(AT_PREFIX)){
       atIndex = 0;
@@ -69,11 +73,6 @@ public class ExceptionFilter implements Filter, DumbAware {
     final int lastDotIndex = line.lastIndexOf('.', lparenthIndex);
     if (lastDotIndex < 0 || lastDotIndex < atIndex) return null;
     String className = line.substring(atIndex + AT.length() + 1, lastDotIndex).trim();
-    final boolean isInternal = shouldFold(className);
-    final int dollarIndex = className.indexOf('$');
-    if (dollarIndex >= 0){
-      className = className.substring(0, dollarIndex);
-    }
 
     //String methodName = text.substring(lastDotIndex + 1, lparenthIndex).trim();
 
@@ -81,6 +80,22 @@ public class ExceptionFilter implements Filter, DumbAware {
     if (rparenthIndex < 0) return null;
 
     final String fileAndLine = line.substring(lparenthIndex + 1, rparenthIndex).trim();
+    return Trinity.create(className, fileAndLine, new TextRange(lparenthIndex, rparenthIndex));
+  }
+
+  public Result applyFilter(final String line, final int textEndOffset) {
+    final Trinity<String, String, TextRange> info = parseExceptionLine(line);
+    if (info == null) {
+      return null;
+    }
+
+    String className = info.first;
+    final int dollarIndex = className.indexOf('$');
+    if (dollarIndex >= 0){
+      className = className.substring(0, dollarIndex);
+    }
+
+    final String fileAndLine = info.second;
 
     final int colonIndex = fileAndLine.lastIndexOf(':');
     if (colonIndex < 0) return null;
@@ -103,29 +118,21 @@ public class ExceptionFilter implements Filter, DumbAware {
       */
 
       final int textStartOffset = textEndOffset - line.length();
-      final int highlightStartOffset = textStartOffset + lparenthIndex + 1;
-      final int highlightEndOffset = textStartOffset + rparenthIndex;
+      final int highlightStartOffset = textStartOffset + info.third.getStartOffset() + 1;
+      final int highlightEndOffset = textStartOffset + info.third.getEndOffset();
       VirtualFile virtualFile = file.getVirtualFile();
-      final OpenFileHyperlinkInfo info = new OpenFileHyperlinkInfo(myProject, virtualFile, lineNumber - 1);
+      final OpenFileHyperlinkInfo linkInfo = new OpenFileHyperlinkInfo(myProject, virtualFile, lineNumber - 1);
       TextAttributes attributes = HYPERLINK_ATTRIBUTES.clone();
       if (!ProjectRootManager.getInstance(myProject).getFileIndex().isInContent(virtualFile)) {
         Color color = UIUtil.getTextInactiveTextColor();
         attributes.setForegroundColor(color);
         attributes.setEffectColor(color);
       }
-      return new Result(highlightStartOffset, highlightEndOffset, info, attributes, isInternal);
+      return new Result(highlightStartOffset, highlightEndOffset, linkInfo, attributes);
     }
     catch(NumberFormatException e){
       return null;
     }
   }
 
-  private static boolean shouldFold(String className) {
-    for (StackFrameFilter provider : StackFrameFilter.EP_NAME.getExtensions()) {
-      if (provider.isInternalFrame(className, "")) {
-        return true;
-      }
-    }
-    return false;
-  }
 }
\ No newline at end of file
index 14714a25b3806dfd6c6c5ba2964c2fe511cf485f..ed60d87c0b4d0066c9cd6dddac3c2f91a82d5a8a 100644 (file)
@@ -4,7 +4,7 @@ package com.intellij.execution.filters;
  * @author peter
  */
 public class ReflectionStackFrameFilter extends StackFrameFilter {
-  public boolean isInternalFrame(String className, String methodName) {
+  public boolean isAuxiliaryFrame(String className, String methodName) {
     return className.startsWith("sun.reflect.");
   }
 }
index a07d058f673fdddb2863d57f00b7d6ec8f2c78ad..25f6cdb1b07cc942c53c47abdb342c1cf135c6b6 100644 (file)
@@ -8,6 +8,6 @@ import com.intellij.openapi.extensions.ExtensionPointName;
 public abstract class StackFrameFilter {
   public static final ExtensionPointName<StackFrameFilter> EP_NAME = ExtensionPointName.create("com.intellij.stackFrameFilter");
 
-  public abstract boolean isInternalFrame(String className, String methodName);
+  public abstract boolean isAuxiliaryFrame(String className, String methodName);
 
 }
index 2346497b07d254b3c46ccc031b925061d7d11b90..264792ec87233b99df3f219ec121819a858a013c 100644 (file)
@@ -28,22 +28,16 @@ public interface Filter {
     public final int highlightEndOffset;
     public final TextAttributes highlightAttributes;
     public final HyperlinkInfo hyperlinkInfo;
-    public final boolean isInternal;
 
     public Result(final int highlightStartOffset, final int highlightEndOffset, final HyperlinkInfo hyperlinkInfo) {
       this(highlightStartOffset, highlightEndOffset, hyperlinkInfo, null);
     }
 
     public Result(final int highlightStartOffset, final int highlightEndOffset, final HyperlinkInfo hyperlinkInfo, final TextAttributes highlightAttributes) {
-      this(highlightStartOffset, highlightEndOffset, hyperlinkInfo, highlightAttributes, false);
-    }
-
-    public Result(final int highlightStartOffset, final int highlightEndOffset, final HyperlinkInfo hyperlinkInfo, final TextAttributes highlightAttributes, boolean isInternal) {
       this.highlightStartOffset = highlightStartOffset;
       this.highlightEndOffset = highlightEndOffset;
-      this.highlightAttributes = highlightAttributes;
       this.hyperlinkInfo = hyperlinkInfo;
-      this.isInternal = isInternal;
+      this.highlightAttributes = highlightAttributes;
     }
   }
 
diff --git a/platform/lang-impl/src/com/intellij/execution/ConsoleFolding.java b/platform/lang-impl/src/com/intellij/execution/ConsoleFolding.java
new file mode 100644 (file)
index 0000000..32f7814
--- /dev/null
@@ -0,0 +1,18 @@
+package com.intellij.execution;
+
+import com.intellij.openapi.extensions.ExtensionPointName;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * @author peter
+ */
+public abstract class ConsoleFolding {
+  public static final ExtensionPointName<ConsoleFolding> EP_NAME = ExtensionPointName.create("com.intellij.console.folding");
+
+  public abstract boolean shouldFoldLine(String line);
+
+  @Nullable
+  public abstract String getPlaceholderText(List<String> lines);
+}
index af7bbefa5b70fdd7bac449cec5fe25838defd54b..d5e20a06b5dd672c7ad475c8b8025408fad6d961 100644 (file)
@@ -17,6 +17,7 @@
 package com.intellij.execution.impl;
 
 import com.intellij.codeInsight.navigation.IncrementalSearchHandler;
+import com.intellij.execution.ConsoleFolding;
 import com.intellij.execution.ExecutionBundle;
 import com.intellij.execution.filters.*;
 import com.intellij.execution.process.ProcessHandler;
@@ -73,7 +74,6 @@ import com.intellij.util.EditorPopupHandler;
 import com.intellij.util.LocalTimeCounter;
 import com.intellij.util.containers.HashMap;
 import com.intellij.util.text.CharArrayUtil;
-import gnu.trove.THashSet;
 import gnu.trove.TIntObjectHashMap;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -172,7 +172,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
 
   private ArrayList<TokenInfo> myTokens = new ArrayList<TokenInfo>();
   private final Hyperlinks myHyperlinks = new Hyperlinks();
-  private final TIntObjectHashMap<Boolean> myFolding = new TIntObjectHashMap<Boolean>();
+  private final TIntObjectHashMap<ConsoleFolding> myFolding = new TIntObjectHashMap<ConsoleFolding>();
 
   private String myHelpId;
 
@@ -807,16 +807,14 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
 
     final int startLine = Math.max(0, line1);
 
-    final Set<FoldRegion> toRemove = clearOutdatedFolding(document, endLine, startLine);
     final List<FoldRegion> toAdd = new ArrayList<FoldRegion>();
 
     for(int line = startLine; line <= endLine; line++) {
-      final int startOffset = document.getLineStartOffset(line);
       int endOffset = document.getLineEndOffset(line);
       if (endOffset < document.getTextLength()) {
         endOffset++; // add '\n'
       }
-      final String text = chars.subSequence(startOffset, endOffset).toString();
+      final String text = getLineText(document, line, true);
       Filter.Result result = myCustomFilter.applyFilter(text, endOffset);
       if (result == null) {
         result = myPredefinedMessageFilter.applyFilter(text, endOffset);
@@ -826,21 +824,18 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
         final int highlightEndOffset = result.highlightEndOffset;
         final HyperlinkInfo hyperlinkInfo = result.hyperlinkInfo;
         addHyperlink(highlightStartOffset, highlightEndOffset, result.highlightAttributes, hyperlinkInfo, hyperlinkAttributes);
-        addFolding(document, chars, line, result.isInternal, toAdd);
       }
+      addFolding(document, chars, line, toAdd);
     }
-    if (!toAdd.isEmpty() || !toRemove.isEmpty()) {
-      doUpdateFolding(toRemove, toAdd);
+    if (!toAdd.isEmpty()) {
+      doUpdateFolding(toAdd);
     }
   }
 
-  private void doUpdateFolding(final Set<FoldRegion> toRemove, final List<FoldRegion> toAdd) {
+  private void doUpdateFolding(final List<FoldRegion> toAdd) {
     final FoldingModel model = myEditor.getFoldingModel();
     model.runBatchFoldingOperationDoNotCollapseCaret(new Runnable() {
       public void run() {
-        for (FoldRegion region : toRemove) {
-          model.removeFoldRegion(region);
-        }
         for (FoldRegion region : toAdd) {
           region.setExpanded(false);
           model.addFoldRegion(region);
@@ -849,39 +844,49 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
     });
   }
 
-  private Set<FoldRegion> clearOutdatedFolding(Document document, int endLine, int startLine) {
-    final THashSet<FoldRegion> toRemove = new THashSet<FoldRegion>();
-    final int dirtyRegionStart = document.getLineStartOffset(startLine);
-    final int diretyRegionEnd = document.getLineEndOffset(endLine);
-    for (FoldRegion region : myEditor.getFoldingModel().getAllFoldRegions()) {
-      final int regStart = region.getStartOffset();
-      final int regEnd = region.getEndOffset();
-      if (regStart >= dirtyRegionStart && regStart <= dirtyRegionStart ||
-          regEnd >= dirtyRegionStart && regEnd <= diretyRegionEnd) {
-        toRemove.add(region);
-      }
-    }
-    return toRemove;
-  }
+  private void addFolding(Document document, CharSequence chars, int line, List<FoldRegion> toAdd) {
+    ConsoleFolding current = foldingForLine(getLineText(document, line, false));
+    myFolding.put(line, current);
 
-  private void addFolding(Document document, CharSequence chars, int line, boolean isInternal, List<FoldRegion> toAdd) {
-    myFolding.put(line, isInternal);
-    final Boolean prevFolding = myFolding.get(line - 1);
-    if (!isInternal && Boolean.TRUE.equals(prevFolding)) {
+    final ConsoleFolding prevFolding = myFolding.get(line - 1);
+    if (current == null && prevFolding != null) {
       final int lEnd = line - 1;
       int lStart = lEnd;
-      while (Boolean.TRUE.equals(myFolding.get(lStart - 1))) lStart--;
+      while (prevFolding.equals(myFolding.get(lStart - 1))) lStart--;
       if (lStart == lEnd) {
         return;
       }
 
+      List<String> toFold = new ArrayList<String>(lEnd - lStart + 1);
+      for (int i = lStart; i <= lEnd; i++) {
+        toFold.add(getLineText(document, i, false));
+      }
+
       int oStart = document.getLineStartOffset(lStart);
-      oStart = CharArrayUtil.shiftForward(chars, oStart, " \t");
-//      if (oStart > 0) oStart--;
+//      oStart = CharArrayUtil.shiftForward(chars, oStart, " \t");
+      if (oStart > 0) oStart--;
       int oEnd = CharArrayUtil.shiftBackward(chars, document.getLineEndOffset(lEnd) - 1, " \t") + 1;
 
-      toAdd.add(new FoldRegionImpl(myEditor, oStart, oEnd, " " + (lEnd - lStart + 1) + " internal calls", null));
+      toAdd.add(new FoldRegionImpl(myEditor, oStart, oEnd, prevFolding.getPlaceholderText(toFold), null));
+    }
+  }
+
+  private static String getLineText(Document document, int lineNumber, boolean includeEol) {
+    int endOffset = document.getLineEndOffset(lineNumber);
+    if (includeEol && endOffset < document.getTextLength()) {
+      endOffset++;
+    }
+    return document.getCharsSequence().subSequence(document.getLineStartOffset(lineNumber), endOffset).toString();
+  }
+
+  private static ConsoleFolding foldingForLine(String lineText) {
+    ConsoleFolding current = null;
+    for (ConsoleFolding folding : ConsoleFolding.EP_NAME.getExtensions()) {
+      if (folding.shouldFoldLine(lineText)) {
+        current = folding;
+      }
     }
+    return current;
   }
 
   private void addHyperlink(final int highlightStartOffset,
index bbe0d6f0b4fdeb584d69e333dac1a4f7f5442d2a..51dc6c039dbf0aae755d54efc6b5162f0d275554 100644 (file)
 
   <extensionPoint name="consoleFilterProvider" interface="com.intellij.execution.filters.ConsoleFilterProvider"/>
 
-  <extensionPoint name="stackFrameFilter" interface="com.intellij.execution.filters.StackFrameFilter"/>
+  <extensionPoint name="console.folding" interface="com.intellij.execution.ConsoleFolding"/>
 
   <extensionPoint name="configurationProducer"
                   interface="com.intellij.execution.junit.RuntimeConfigurationProducer"/>
index 6e992b1f5e0c44932bbc780fb9f2e0577a88ca93..3e0a45dd38de3ad44aa32ce02aaefdba8d2150ee 100644 (file)
@@ -41,7 +41,12 @@ public class GroovyDebuggerClassFilterProvider extends StackFrameFilter implemen
     return list;
   }
 
-  public boolean isInternalFrame(String className, String methodName) {
+  public boolean isAuxiliaryFrame(String className, String methodName) {
+    if (className.equals("org.codehaus.groovy.runtime.DefaultGroovyMethods") ||
+        className.equals("org.codehaus.groovy.runtime.DefaultGroovyMethodsSupport")) {
+      return false;
+    }
+
     for (ClassFilter filter : FILTERS) {
       final String pattern = filter.getPattern();
       if (className.startsWith(pattern.substring(0, pattern.length() - 1))) {
index f14242a4f49d81fa61b178726dc35645b2a9824d..9d64b8b58933bb60926197baf884ceba05d742a9 100644 (file)
     <extensionPoint name="clsStubBuilderFactory" interface="com.intellij.psi.impl.compiled.ClsStubBuilderFactory"/>
     <extensionPoint name="javaMainMethodProvider" interface="com.intellij.codeInsight.runner.JavaMainMethodProvider"/>
 
-    <!--debugger-->
     <extensionPoint name="debuggerClassFilterProvider" interface="com.intellij.ui.classFilter.DebuggerClassFilterProvider"/>
 
+    <extensionPoint name="stackFrameFilter" interface="com.intellij.execution.filters.StackFrameFilter"/>
+
     <extensionPoint name="paletteItemProvider" area="IDEA_PROJECT" interface="com.intellij.ide.palette.PaletteItemProvider"/>
 
     <extensionPoint name="javadocTagInfo" area="IDEA_PROJECT" interface="com.intellij.psi.javadoc.JavadocTagInfo"/>
     <gotoFileContributor implementation="com.intellij.ide.util.gotoByName.ProjectBaseDirNavigationContributor"/>
 
     <consoleFilterProvider implementation="com.intellij.execution.filters.DefaultConsoleFiltersProvider"/>
+    <console.folding implementation="com.intellij.execution.filters.AuxiliaryCallsFolding"/>
 
     <stackFrameFilter implementation="com.intellij.execution.filters.ReflectionStackFrameFilter"/>