file structure dialog speed search match highlighting
authorAlexey Pegov <alexey.pegov@jetbrains.com>
Wed, 15 Sep 2010 13:18:51 +0000 (17:18 +0400)
committerAlexey Pegov <alexey.pegov@jetbrains.com>
Wed, 15 Sep 2010 13:18:51 +0000 (17:18 +0400)
platform/lang-impl/src/com/intellij/ide/commander/ColoredCommanderRenderer.java
platform/lang-impl/src/com/intellij/ide/commander/CommanderPanel.java
platform/lang-impl/src/com/intellij/ide/util/FileStructureDialog.java
platform/platform-impl/src/com/intellij/ide/ui/search/SearchUtil.java
platform/platform-impl/src/com/intellij/ui/SpeedSearchBase.java
plugins/commander/src/com/intellij/ide/commander/Commander.java

index fb06fbf4019277d7f4c72af3ddefe8cd50840ea0..691fd9d9f7cc71a8619d365be8a9dff0f37f61da 100644 (file)
 
 package com.intellij.ide.commander;
 
+import com.intellij.ide.ui.search.SearchUtil;
 import com.intellij.ide.util.treeView.AbstractTreeNode;
 import com.intellij.ide.util.treeView.NodeDescriptor;
-import com.intellij.openapi.editor.markup.TextAttributes;
 import com.intellij.openapi.editor.colors.EditorColorsManager;
 import com.intellij.openapi.editor.colors.TextAttributesKey;
+import com.intellij.openapi.editor.markup.TextAttributes;
+import com.intellij.openapi.util.Pair;
 import com.intellij.ui.ColoredListCellRenderer;
 import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.ui.SpeedSearchBase;
 import com.intellij.util.ui.UIUtil;
 import org.jetbrains.annotations.NotNull;
 
 import javax.swing.*;
 import java.awt.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
 
 final class ColoredCommanderRenderer extends ColoredListCellRenderer {
   private final CommanderPanel myCommanderPanel;
@@ -75,7 +81,35 @@ final class ColoredCommanderRenderer extends ColoredListCellRenderer {
 
     if(attributes == null) attributes = new SimpleTextAttributes(Font.PLAIN, color);
     final String text = value.toString();
-    append(text != null? text : "", attributes);
+
+    if (myCommanderPanel.isEnableSearchHighlighting() && SpeedSearchBase.hasActiveSpeedSearch(list)) {
+      final SpeedSearchBase.SpeedSearchComparator comparator = myCommanderPanel.getListSpeedSearch().getComparator();
+      final String recentSearchText = comparator.getRecentSearchText();
+      if (recentSearchText != null && recentSearchText.length() > 0 && comparator.doCompare(recentSearchText, text)) {
+        final Matcher matcher = comparator.getRecentSearchMatcher();
+        final List<Pair<String, Integer>> searchTerms = new ArrayList<Pair<String, Integer>>();
+        for (int i = 0; i < matcher.groupCount(); i++) {
+          final int start = matcher.start(i + 1);
+          if (searchTerms.size() > 0) {
+            final Pair<String, Integer> recent = searchTerms.get(searchTerms.size() - 1);
+            if (start == recent.second + recent.first.length()) {
+              searchTerms.set(searchTerms.size() - 1, Pair.create(recent.first + matcher.group(i+1), recent.second));
+              continue;
+            }
+          }
+
+          searchTerms.add(Pair.create(matcher.group(i+1), start));
+        }
+
+        SearchUtil.appendFragmentsStrict(text, searchTerms, Font.PLAIN, attributes.getFgColor(),
+                                         selected ? UIUtil.getTreeSelectionBackground() : UIUtil.getTreeTextBackground(), this);
+      } else {
+        append(text != null ? text : "", attributes);
+      }
+    }
+    else {
+      append(text != null ? text : "", attributes);
+    }
 
     if (locationString != null && locationString.length() > 0) {
       append(" (" + locationString + ")", SimpleTextAttributes.GRAY_ATTRIBUTES);
index a4d154b95facf0bf572e5c534f722189d7314e23..d4094e9be0f32e98b86c2956c2e9ca96cea09ed2 100644 (file)
@@ -91,10 +91,12 @@ public class CommanderPanel extends JPanel {
   private boolean myActive = true;
   private final List<CommanderHistoryListener> myHistoryListeners = ContainerUtil.createEmptyCOWList();
   private boolean myMoveFocus = false;
+  private boolean myEnableSearchHighlighting;
 
-  public CommanderPanel(final Project project, final boolean enablePopupMenu) {
+  public CommanderPanel(final Project project, final boolean enablePopupMenu, final boolean enableSearchHighlighting) {
     super(new BorderLayout());
     myProject = project;
+    myEnableSearchHighlighting = enableSearchHighlighting;
     myModel = new MyModel();
     myList = new JBList(myModel);
     myList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
@@ -169,6 +171,14 @@ public class CommanderPanel extends JPanel {
 
   }
 
+  public boolean isEnableSearchHighlighting() {
+    return myEnableSearchHighlighting;
+  }
+
+  public ListSpeedSearch getListSpeedSearch() {
+    return myListSpeedSearch;
+  }
+
   public void addHistoryListener(CommanderHistoryListener listener) {
     myHistoryListeners.add(listener);
   }
index e79942cdc42826d07a4470f33b7178882882ca19..f3455a0c744c261e883bf30dfff450222db773ff 100644 (file)
@@ -298,7 +298,7 @@ public class FileStructureDialog extends DialogWrapper {
     }
 
     public MyCommanderPanel(Project _project) {
-      super(_project, false);
+      super(_project, false, true);
       myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
       myListSpeedSearch.addChangeListener(new PropertyChangeListener() {
         public void propertyChange(PropertyChangeEvent evt) {
@@ -422,11 +422,14 @@ public class FileStructureDialog extends DialogWrapper {
     return new SpeedSearchBase.SpeedSearchComparator() {
       public void translateCharacter(final StringBuilder buf, final char ch) {
         if (ch == '*') {
+          if (buf.length() > 0 && "^*)(".indexOf(buf.charAt(buf.length() - 1)) == -1) buf.append(')');
           buf.append(".*"); // overrides '*' handling to skip (,) in parameter lists
         }
         else {
           if (ch == ':') {
+            if (buf.length() > 0 && "^*)(".indexOf(buf.charAt(buf.length() - 1)) == -1) buf.append(')');
             buf.append(".*"); //    get:int should match any getter returning int
+            buf.append('(');
           }
           super.translateCharacter(buf, ch);
         }
index 42a51d10b3a266a9bdcc32c78dee3dde5788e6aa..53a0d9dd6f0b8c0cf5a06d3aa381479e6e68f38c 100644 (file)
@@ -26,11 +26,13 @@ import com.intellij.openapi.options.ex.ProjectConfigurablesGroup;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.popup.JBPopup;
 import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.ui.*;
 import com.intellij.ui.components.JBList;
 import com.intellij.util.Alarm;
 import com.intellij.util.Consumer;
+import com.intellij.util.Processor;
 import com.intellij.util.containers.ContainerUtil;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
@@ -68,7 +70,7 @@ public class SearchUtil {
   }
 
   private static void processConfigurables(final Configurable[] configurables,
-                                          final HashMap<SearchableConfigurable, TreeSet<OptionDescription>> options) {
+                                           final HashMap<SearchableConfigurable, TreeSet<OptionDescription>> options) {
     for (Configurable configurable : configurables) {
       if (configurable instanceof SearchableConfigurable) {
         TreeSet<OptionDescription> configurableOptions = new TreeSet<OptionDescription>();
@@ -334,10 +336,12 @@ public class SearchUtil {
 
   private static String quoteStrictOccurences(final String textToMarkup, final String filter) {
     String cur = "";
+    final String s = textToMarkup.toLowerCase();
     for (String part : filter.split(" ")) {
-      if (textToMarkup.toLowerCase().indexOf(part) != -1) {
+      if (s.indexOf(part) != -1) {
         cur += "\"" + part + "\" ";
-      } else {
+      }
+      else {
         cur += part + " ";
       }
     }
@@ -353,7 +357,8 @@ public class SearchUtil {
       final String toMark = textToMarkup.substring(idx, idx + option.length());
       if (insideHtmlTagPattern.matcher(prefix).matches()) {
         result += prefix + toMark;
-      } else {
+      }
+      else {
         result += prefix + "<font color='#ffffff' bgColor='#1d5da7'>" + toMark + "</font>";
       }
       beg = idx + option.length();
@@ -362,12 +367,40 @@ public class SearchUtil {
     return result;
   }
 
+  public static void appendFragmentsStrict(@NonNls final String text, @NotNull final List<Pair<String, Integer>> toHighlight,
+                                           final int style, final Color foreground,
+                                           final Color background, final SimpleColoredComponent c) {
+    if (text == null) return;
+    final SimpleTextAttributes plainAttributes = new SimpleTextAttributes(style, foreground);
+
+    final int[] lastOffset = {0};
+    ContainerUtil.process(toHighlight, new Processor<Pair<String, Integer>>() {
+      @Override
+      public boolean process(Pair<String, Integer> pair) {
+        if (pair.second > lastOffset[0]) {
+          c.append(text.substring(lastOffset[0], pair.second), new SimpleTextAttributes(style, foreground));
+        }
+
+        c.append(text.substring(pair.second, pair.second + pair.first.length()), new SimpleTextAttributes(background,
+                                                                                                          foreground, null,
+                                                                                                          style |
+                                                                                                          SimpleTextAttributes.STYLE_SEARCH_MATCH));
+        lastOffset[0] = pair.second + pair.first.length();
+        return true;
+      }
+    });
+
+    if (lastOffset[0] < text.length()) {
+      c.append(text.substring(lastOffset[0]), plainAttributes);
+    }
+  }
+
   public static void appendFragments(String filter,
                                      @NonNls String text,
                                      final int style,
                                      final Color foreground,
                                      final Color background,
-                                     final ColoredTreeCellRenderer textRenderer) {
+                                     final SimpleColoredComponent textRenderer) {
     if (text == null) return;
     if (filter == null || filter.length() == 0) {
       textRenderer.append(text, new SimpleTextAttributes(style, foreground));
@@ -412,14 +445,19 @@ public class SearchUtil {
         idx = text.indexOf(word) + word.length();
         textRenderer.append(text.substring(idx - word.length(), idx), new SimpleTextAttributes(background,
                                                                                                foreground, null,
-                                                                                               style | SimpleTextAttributes.STYLE_SEARCH_MATCH));
+                                                                                               style |
+                                                                                               SimpleTextAttributes.STYLE_SEARCH_MATCH));
       }
       final String after = text.substring(idx, text.length());
       if (after.length() > 0) textRenderer.append(after, new SimpleTextAttributes(background, foreground, null, style));
     }
   }
 
-  private static void appendSelectedWords(final String text, final List<String> selectedWords, final int pos, int end, final String filter) {
+  private static void appendSelectedWords(final String text,
+                                          final List<String> selectedWords,
+                                          final int pos,
+                                          int end,
+                                          final String filter) {
     if (pos < end) {
       final Set<String> filters = SearchableOptionsRegistrar.getInstance().getProcessedWords(filter);
       final String[] words = text.substring(pos, end).split("[\\W&&[^_-]]");
@@ -499,8 +537,9 @@ public class SearchUtil {
             if (cancelPopups(activePopup) && e.getKeyCode() == KeyEvent.VK_ESCAPE) {
               return;
             }
-            if (e.getKeyChar() != KeyEvent.CHAR_UNDEFINED){
-              searchField.process(new KeyEvent(searchField, KeyEvent.KEY_TYPED, e.getWhen(), e.getModifiers(), KeyEvent.VK_UNDEFINED, e.getKeyChar()));
+            if (e.getKeyChar() != KeyEvent.CHAR_UNDEFINED) {
+              searchField.process(
+                new KeyEvent(searchField, KeyEvent.KEY_TYPED, e.getWhen(), e.getModifiers(), KeyEvent.VK_UNDEFINED, e.getKeyChar()));
             }
           }
         }
index 592c146ca1e426c44788dd7ea74cda650eae7f01..52cca247a4f2468c35d3649e9294be109c22f944 100644 (file)
@@ -196,6 +196,16 @@ public abstract class SpeedSearchBase<Comp extends JComponent> {
       for (int i = 0; i < len; ++i) {
         translateCharacter(buf, pattern.charAt(i));
       }
+
+      if (buf.length() > 0 && "*^".indexOf(buf.charAt(buf.length() - 1)) == -1) buf.append(')');
+    }
+
+    public String getRecentSearchText() {
+      return myRecentSearchText;
+    }
+
+    public Matcher getRecentSearchMatcher() {
+      return myRecentSearchMatcher;
     }
 
     public void translateCharacter(final StringBuilder buf, final char ch) {
@@ -206,10 +216,17 @@ public abstract class SpeedSearchBase<Comp extends JComponent> {
         // do not bother with other metachars
         buf.append('\\');
       }
+
       if (Character.isUpperCase(ch)) {
+        if (buf.length() > 0 && "*^".indexOf(buf.charAt(buf.length() - 1)) == -1) buf.append(')');
         // for camel humps
         buf.append("[A-Za-z_]*");
+        buf.append('(');
+      } else {
+        if (buf.length() > 0 && "*^".indexOf(buf.charAt(buf.length() - 1)) != -1) buf.append('(');
       }
+
+      if (buf.length() == 0 || buf.length() > 0 && "^".indexOf(buf.charAt(buf.length() - 1)) != -1) buf.append('(');
       buf.append(ch);
     }
   }
index 787e99288a48fe70a4ed9cbf3c919bada3803d8f..9baf183d9d5b696f4e7e425b357196939529e357 100644 (file)
@@ -310,7 +310,7 @@ public class Commander extends JPanel implements PersistentStateComponent<Elemen
   }
 
   private CommanderPanel createPanel() {
-    final CommanderPanel panel = new CommanderPanel(myProject, true);
+    final CommanderPanel panel = new CommanderPanel(myProject, true, false);
 
     panel.getList().addKeyListener(new KeyAdapter() {
       public void keyPressed(final KeyEvent e) {