commit 46bc1a9aa5c722acc9e91ee22e4df9b895bab129
parent afbb28668b721ae50dc2f12861f5de6d89ed2783
Author: Dan Amlund <dan@danamlund.dk>
Date: Sat, 11 Nov 2017 21:17:54 +0100
1.4 extend from same dialog as ctrl-shift-t, put elements that match better higher in the list. Better matching means neigboring letters from pattern, and same casing as in pattern.
Diffstat:
7 files changed, 239 insertions(+), 121 deletions(-)
diff --git a/dk.danamlund.quicklaunch/.classpath b/dk.danamlund.quicklaunch/.classpath
@@ -3,5 +3,6 @@
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="unittest"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/dk.danamlund.quicklaunch/META-INF/MANIFEST.MF b/dk.danamlund.quicklaunch/META-INF/MANIFEST.MF
@@ -2,9 +2,10 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Quick Launch Configuration
Bundle-SymbolicName: dk.danamlund.quicklaunch;singleton:=true
-Bundle-Version: 1.3
+Bundle-Version: 1.4
Require-Bundle: org.eclipse.ui,
org.eclipse.debug.core;bundle-version="3.10.100",
org.eclipse.equinox.common;bundle-version="3.8.0",
- org.eclipse.debug.ui
+ org.eclipse.debug.ui,
+ org.junit
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
diff --git a/dk.danamlund.quicklaunch/build.properties b/dk.danamlund.quicklaunch/build.properties
@@ -1,4 +1,4 @@
-source.. = src/
+source.. = src/, unittest/
output.. = bin/
bin.includes = plugin.xml,\
META-INF/,\
diff --git a/dk.danamlund.quicklaunch/src/dk/danamlund/quicklaunch/QuickLaunchConfigurationDialog.java b/dk.danamlund.quicklaunch/src/dk/danamlund/quicklaunch/QuickLaunchConfigurationDialog.java
@@ -1,39 +1,47 @@
package dk.danamlund.quicklaunch;
-import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.IDebugModelPresentation;
+import org.eclipse.jface.dialogs.DialogSettings;
+import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.viewers.LabelProvider;
-import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Event;
-import org.eclipse.swt.widgets.Listener;
-import org.eclipse.swt.widgets.Text;
-import org.eclipse.ui.dialogs.ElementListSelectionDialog;
-import org.eclipse.ui.dialogs.FilteredList;
-import org.eclipse.ui.dialogs.FilteredList.FilterMatcher;
-
-public class QuickLaunchConfigurationDialog extends ElementListSelectionDialog {
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.dialogs.FilteredItemsSelectionDialog;
+
+public class QuickLaunchConfigurationDialog extends FilteredItemsSelectionDialog {
private final String runMode;
- private Object previousSelection = null;
+ private final FuzzyComparator fuzzyComparator;
+ private final StringFuzzyMatcher stringFuzzyMatcher;
private List<ILaunchConfiguration> launchConfigurations;
public QuickLaunchConfigurationDialog(String runMode) {
- super(null, new QuickLaunchConfigurationLabelProvider());
+ this(runMode, new StringFuzzyMatcher());
+ }
+
+ public QuickLaunchConfigurationDialog(String runMode, StringFuzzyMatcher stringFuzzyMatcher) {
+ super(null, false);
this.runMode = runMode;
+ this.fuzzyComparator = new FuzzyComparator(stringFuzzyMatcher);
+ this.stringFuzzyMatcher = stringFuzzyMatcher;
- setBlockOnOpen(false);
+ setListLabelProvider(new IconLabelProvider());
setTitle(capitalize(runMode) + " Launch Configuration");
setMessage("Enter fuzzy pattern:");
+
+ setSelectionHistory(new ResourceSelectionHistory());
}
private static String capitalize(String s) {
@@ -43,71 +51,53 @@ public class QuickLaunchConfigurationDialog extends ElementListSelectionDialog {
return s.substring(0, 1).toUpperCase() + s.substring(1);
}
- public void setLaunchConfigurations(List<ILaunchConfiguration> launchConfigurations) {
- this.launchConfigurations = launchConfigurations;
- setElements(this.launchConfigurations.toArray());
+ @Override
+ protected Control createExtendedContentArea(Composite parent) {
+ return null;
}
@Override
- public int open() {
- int output = super.open();
-
- // Running initially selected element caused ok button to be disabled
- // on next run until you search for something.
- // This fixes that, without me having to understand why it happens.
- Button okButton = getOkButton();
- if (okButton != null) {
- okButton.setEnabled(true);
- }
-
- if (previousSelection != null) {
- setSelection(new Object[] { previousSelection });
- }
+ protected IDialogSettings getDialogSettings() {
+ return new DialogSettings("QuickLaunchConfigurationDialog");
+ }
- return output;
+ @Override
+ protected IStatus validateItem(Object item) {
+ return Status.OK_STATUS;
}
@Override
- protected FilteredList createFilteredList(Composite parent) {
- FilteredList list = super.createFilteredList(parent);
- // Set dummy filters and comparators, do everything in doFilter
- list.setFilterMatcher(new TrueFilterMatcher());
- list.setComparator(new AllEqualComparator());
- return list;
+ protected ItemsFilter createFilter() {
+ return new FuzzyMatchItemsFilter();
}
@Override
- protected Text createFilterText(Composite parent) {
- Text text = super.createFilterText(parent);
+ protected Comparator<?> getItemsComparator() {
+ return fuzzyComparator;
+ }
- // Dont trigger FilterList filter
- for (Listener listener : text.getListeners(SWT.Modify)) {
- text.removeListener(SWT.Modify, listener);
+ @Override
+ protected void fillContentProvider(AbstractContentProvider contentProvider, ItemsFilter itemsFilter,
+ IProgressMonitor progressMonitor) throws CoreException {
+ FuzzyMatchItemsFilter fuzzyFilter = (FuzzyMatchItemsFilter) itemsFilter;
+ fuzzyComparator.setPattern(fuzzyFilter.getRealPattern());
+ progressMonitor.beginTask("Searching", launchConfigurations.size());
+ for (ILaunchConfiguration launchConfiguration : launchConfigurations) {
+ contentProvider.add(launchConfiguration, itemsFilter);
+ progressMonitor.worked(1);
}
-
- text.addListener(SWT.Modify, new Listener() {
- @Override
- public void handleEvent(Event e) {
- doFilter(text.getText());
- }
- });
-
- return text;
+ progressMonitor.done();
}
- private void doFilter(String pattern) {
- pattern = pattern.toLowerCase();
-
- List<ILaunchConfiguration> filtered = new ArrayList<>();
- for (ILaunchConfiguration launchConfig : launchConfigurations) {
- if (fuzzyMatch(launchConfig.toString(), pattern)) {
- filtered.add(launchConfig);
- }
- }
- filtered.sort(new FuzzyScoreComparator(pattern));
+ @Override
+ public int open() {
+ setInitialPattern("");
+ return super.open();
+ }
- // Naively update list with filtered elements.
- fFilteredList.setElements(filtered.toArray());
+ @Override
+ public String getElementName(Object item) {
+ return item.toString();
}
@Override
@@ -121,88 +111,82 @@ public class QuickLaunchConfigurationDialog extends ElementListSelectionDialog {
} catch (CoreException e) {
throw new IllegalStateException(e);
}
- previousSelection = launchConfiguration;
}
}
- private static class QuickLaunchConfigurationLabelProvider extends LabelProvider {
- final IDebugModelPresentation debugModelPresentation = DebugUITools.newDebugModelPresentation();
+ public void setLaunchConfigurations(List<ILaunchConfiguration> launchConfigurations) {
+ this.launchConfigurations = launchConfigurations;
+ }
+ private class FuzzyMatchItemsFilter extends ItemsFilter {
@Override
- public Image getImage(Object element) {
- return debugModelPresentation.getImage(element);
+ public boolean matchItem(Object item) {
+ return stringFuzzyMatcher.fuzzyMatch(item.toString(), getRealPattern());
}
- }
- /**
- * fuzzy 'foo' is same as regular '*f*o*o*'.
- */
- private static boolean fuzzyMatch(String element, String pattern) {
- if (pattern.isEmpty()) {
+ @Override
+ public boolean isConsistentItem(Object item) {
return true;
}
- String s = element.toLowerCase().trim();
- int sIndex = 0;
- for (int i = 0; i < pattern.length(); i++) {
- if (sIndex >= s.length()) {
- return false;
- }
- sIndex = s.indexOf(pattern.charAt(i), sIndex);
- if (sIndex < 0) {
- return false;
- }
- sIndex++;
+
+ @Override
+ public boolean isSubFilter(ItemsFilter filter) {
+ // false to trigger fillContentProvider every time
+ return false;
}
- return true;
- }
- private static int getFuzzyScore(String name, String filter) {
- int score = 0;
- int nameI = 0;
- for (int filterI = 0; filterI < filter.length(); filterI++) {
- int newNameI = name.indexOf(filter.charAt(filterI), nameI);
- if (newNameI == nameI) {
- score++;
- }
- nameI = newNameI + 1;
+ @Override
+ public String getPattern() {
+ // Make pattern always be non-empty so we also show results for the
+ // empty pattern
+ return super.getPattern() + "_";
+ }
+
+ public String getRealPattern() {
+ return super.getPattern();
}
- return score;
}
- private static class TrueFilterMatcher implements FilterMatcher {
+ private static class ResourceSelectionHistory extends SelectionHistory {
@Override
- public boolean match(Object element) {
- return true;
+ protected Object restoreItemFromMemento(IMemento element) {
+ return null;
}
@Override
- public void setFilter(String pattern, boolean ignoreCase, boolean ignoreWildCards) {
+ protected void storeItemToMemento(Object item, IMemento element) {
}
- }
- private static class AllEqualComparator implements Comparator<Object> {
@Override
- public int compare(Object o1, Object o2) {
- return 0;
+ public synchronized void accessed(Object object) {
+ // never save history
}
}
- private static class FuzzyScoreComparator implements Comparator<Object> {
- private final String pattern;
+ private static class FuzzyComparator implements Comparator<Object> {
+ private final StringFuzzyMatcher stringFuzzyMatcher;
+ private String pattern = "";
+
+ public FuzzyComparator(StringFuzzyMatcher stringFuzzyMatcher) {
+ this.stringFuzzyMatcher = stringFuzzyMatcher;
+ }
+
+ @Override
+ public int compare(Object a, Object b) {
+ return stringFuzzyMatcher.getFuzzyScoreComparator(pattern).compare(a, b);
+ }
- public FuzzyScoreComparator(String pattern) {
+ public void setPattern(String pattern) {
this.pattern = pattern;
}
+ }
+
+ private static class IconLabelProvider extends LabelProvider {
+ final IDebugModelPresentation debugModelPresentation = DebugUITools.newDebugModelPresentation();
@Override
- public int compare(Object aObject, Object bObject) {
- String a = aObject.toString().toLowerCase();
- String b = bObject.toString().toLowerCase();
- int comparison = Integer.compare(getFuzzyScore(a, pattern), getFuzzyScore(b, pattern));
- if (comparison != 0) {
- return comparison;
- }
- return a.compareTo(b);
+ public Image getImage(Object element) {
+ return debugModelPresentation.getImage(element);
}
}
}
diff --git a/dk.danamlund.quicklaunch/src/dk/danamlund/quicklaunch/StringFuzzyMatcher.java b/dk.danamlund.quicklaunch/src/dk/danamlund/quicklaunch/StringFuzzyMatcher.java
@@ -0,0 +1,78 @@
+package dk.danamlund.quicklaunch;
+
+import java.util.Comparator;
+
+public class StringFuzzyMatcher {
+
+ /**
+ * fuzzy 'foo' is same as regular '*f*o*o*'.
+ */
+ public boolean fuzzyMatch(String element, String pattern) {
+ if (pattern.isEmpty()) {
+ return true;
+ }
+ pattern = pattern.toLowerCase();
+ String s = element.toLowerCase().trim();
+ int sIndex = 0;
+ for (int i = 0; i < pattern.length(); i++) {
+ if (sIndex >= s.length()) {
+ return false;
+ }
+ sIndex = s.indexOf(pattern.charAt(i), sIndex);
+ if (sIndex < 0) {
+ return false;
+ }
+ sIndex++;
+ }
+ return true;
+ }
+
+ public int getFuzzyScore(String name, String filter) {
+ String nameLower = name.toLowerCase();
+ String filterLower = filter.toLowerCase();
+
+ int score = 0;
+ int nameI = 0;
+ for (int filterI = 0; filterI < filter.length(); filterI++) {
+ int newNameI = nameLower.indexOf(filterLower.charAt(filterI), nameI);
+ if (newNameI == nameI) {
+ score += 2;
+ if (name.charAt(nameI) == filter.charAt(filterI)) {
+ score++;
+ }
+ }
+ nameI = newNameI + 1;
+ }
+ // Give score if final char of filter is also final char of name
+ if (nameI == name.length()) {
+ score += 2;
+ }
+ return score;
+ }
+
+ public Comparator<Object> getFuzzyScoreComparator(String pattern) {
+ return new FuzzyScoreComparator(this, pattern);
+ }
+
+ private static class FuzzyScoreComparator implements Comparator<Object> {
+ private final StringFuzzyMatcher stringFuzzyMatcher;
+ private final String pattern;
+
+ public FuzzyScoreComparator(StringFuzzyMatcher stringFuzzyMatcher, String pattern) {
+ this.stringFuzzyMatcher = stringFuzzyMatcher;
+ this.pattern = pattern;
+ }
+
+ @Override
+ public int compare(Object aObject, Object bObject) {
+ String a = aObject.toString();
+ String b = bObject.toString();
+ int comparison = Integer.compare(stringFuzzyMatcher.getFuzzyScore(b, pattern),
+ stringFuzzyMatcher.getFuzzyScore(a, pattern));
+ if (comparison != 0) {
+ return comparison;
+ }
+ return a.compareTo(b);
+ }
+ }
+}
diff --git a/dk.danamlund.quicklaunch/unittest/dk/danamlund/quicklaunch/FuzzyMatchToolsTest.java b/dk.danamlund.quicklaunch/unittest/dk/danamlund/quicklaunch/FuzzyMatchToolsTest.java
@@ -0,0 +1,54 @@
+package dk.danamlund.quicklaunch;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class FuzzyMatchToolsTest {
+
+ @Test
+ public void testFuzzyMatch() {
+ assertTrue(fuzzyMatch("foo", "foo"));
+ assertTrue(fuzzyMatch("?f?o?o?", "foo"));
+ assertFalse(fuzzyMatch("oof", "foo"));
+ assertFalse(fuzzyMatch("bar", "foo"));
+
+ assertTrue(fuzzyMatch("FOO", "foo"));
+ assertTrue(fuzzyMatch("foo", "FOO"));
+ }
+
+ @Test
+ public void fuzzyScore() {
+ checkScoreHigherThan("foo", "?f?o?o?", "foo");
+ }
+
+ @Test
+ public void fuzzyScoreBetterToMatchFirstChar() {
+ checkScoreHigherThan("foo", "?foo", "foo");
+ checkScoreHigherThan("foo", "foobar", "foo");
+ }
+
+ @Test
+ public void fuzzyScoreBetterToMatchLastChar() {
+ checkScoreHigherThan("foo", "foo?", "foo");
+ }
+
+ @Test
+ public void fuzzyScoreBetterToMatchCase() {
+ checkScoreHigherThan("Foo", "foo", "Foo");
+ }
+
+ private void checkScoreHigherThan(String a, String b, String pattern) {
+ assertTrue(a + "(" + getFuzzyScore(a, pattern) + ") > " + b + "(" + getFuzzyScore(b, pattern) + ")",
+ getFuzzyScore(a, pattern) > getFuzzyScore(b, pattern));
+ }
+
+ private int getFuzzyScore(String element, String pattern) {
+ return new StringFuzzyMatcher().getFuzzyScore(element, pattern);
+ }
+
+ private boolean fuzzyMatch(String element, String pattern) {
+ return new StringFuzzyMatcher().fuzzyMatch(element, pattern);
+ }
+}
diff --git a/readme.txt b/readme.txt
@@ -1,6 +1,6 @@
Eclipse quick launch
-Run launch configurations like Ctrl-Shift-T
+Run launch configurations like Ctrl-Shift-D
Launch with Ctrl-Shift-Y
or (debug) with Ctrl-Shift-D