Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKris De Volder2019-07-02 17:55:55 +0000
committerMickael Istria2019-07-29 07:18:54 +0000
commite8b733007b85e76ddab4836dad7175c5012cc000 (patch)
tree1ab085cb20c4b762ffb94347c3839e7916a1003c
parente3145df862180f7b4ffe6376008b92dca3632947 (diff)
downloadeclipse.platform.text-e8b733007b85e76ddab4836dad7175c5012cc000.tar.gz
eclipse.platform.text-e8b733007b85e76ddab4836dad7175c5012cc000.tar.xz
eclipse.platform.text-e8b733007b85e76ddab4836dad7175c5012cc000.zip
Bug 548518: STS Quicksearch ContributionY20190729-0900
Make copy of STS quicksearch code from https://github.com/spring-projects/eclipse-integration-commons/ Changes from original code: - plugin ids changed to org.eclipse.text.quicksearch and org.eclipse.text.quicksearch.tests - command and action set ids renamed to org.eclipse.text.quicksearch.* - about.html changed based on example from other platform bundles - packages renamed to org.eclipse.text.* - Preference page reimplemented using jface FieldEditors. - EPL license headers v1.0 => v2.0 - Add some missing license headers - Externalize strings from preferences page UI - Various small code style improvements from feedback in gerrit - Keybinding changed to CTRL+ALT+SHIFT+L - add '.internal' to package names - use 'x-internal=true' on all packages - export all packages - bundle and package version constraints - trailing whitespace cleanups - configure save actions to remove trailing whitespace in the future - remove jgit dependency - adopt ant 'TokenizedPattern' as replacement for jgit in ResourceMatcher - add some basic regression tests for ResourceMatcher - add 'Automatic-Module-Name' headers to plugin manifests Change-Id: Id2c1e0fbd75e7635656e3801cf31af09e71858d5 Signed-off-by: Kris De Volder <kdevolder@pivotal.io>
-rw-r--r--org.eclipse.text.quicksearch.tests/.classpath11
-rw-r--r--org.eclipse.text.quicksearch.tests/.project28
-rw-r--r--org.eclipse.text.quicksearch.tests/.settings/org.eclipse.jdt.core.prefs7
-rw-r--r--org.eclipse.text.quicksearch.tests/.settings/org.eclipse.jdt.ui.prefs61
-rw-r--r--org.eclipse.text.quicksearch.tests/META-INF/MANIFEST.MF14
-rw-r--r--org.eclipse.text.quicksearch.tests/about.html36
-rw-r--r--org.eclipse.text.quicksearch.tests/build.properties18
-rw-r--r--org.eclipse.text.quicksearch.tests/plugin.properties15
-rw-r--r--org.eclipse.text.quicksearch.tests/pom.xml34
-rw-r--r--org.eclipse.text.quicksearch.tests/src/org/eclipse/text/quicksearch/tests/MockResource.java448
-rw-r--r--org.eclipse.text.quicksearch.tests/src/org/eclipse/text/quicksearch/tests/PrioriTreeTest.java165
-rw-r--r--org.eclipse.text.quicksearch.tests/src/org/eclipse/text/quicksearch/tests/ResourceMatcherTest.java55
-rw-r--r--org.eclipse.text.quicksearch/.classpath7
-rw-r--r--org.eclipse.text.quicksearch/.project39
-rw-r--r--org.eclipse.text.quicksearch/.settings/org.eclipse.core.resources.prefs2
-rw-r--r--org.eclipse.text.quicksearch/.settings/org.eclipse.jdt.core.prefs7
-rw-r--r--org.eclipse.text.quicksearch/.settings/org.eclipse.jdt.ui.prefs61
-rw-r--r--org.eclipse.text.quicksearch/META-INF/MANIFEST.MF30
-rw-r--r--org.eclipse.text.quicksearch/about.html36
-rw-r--r--org.eclipse.text.quicksearch/build.properties21
-rw-r--r--org.eclipse.text.quicksearch/plugin.properties16
-rw-r--r--org.eclipse.text.quicksearch/plugin.xml114
-rw-r--r--org.eclipse.text.quicksearch/pom.xml29
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/LineItem.java91
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/QuickTextQuery.java229
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/QuickTextSearchRequestor.java53
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/QuickTextSearcher.java320
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/ResourceWalker.java201
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/pathmatch/ResourceMatcher.java21
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/pathmatch/ResourceMatchers.java99
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/preferences/QuickSearchPreferences.java112
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/priority/DefaultPriorityFunction.java154
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/priority/PrioriTree.java230
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/priority/PriorityFunction.java57
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/Messages.java35
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchAction.java38
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchActivator.java103
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchContext.java177
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchDialog.java1469
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchHandler.java81
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchPreferencesPage.java74
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/TableResizeHelper.java89
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/messages.properties9
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/util/DocumentFetcher.java176
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/util/LightSchedulingRule.java50
-rw-r--r--org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/util/LineReader.java146
-rw-r--r--pom.xml3
47 files changed, 5271 insertions, 0 deletions
diff --git a/org.eclipse.text.quicksearch.tests/.classpath b/org.eclipse.text.quicksearch.tests/.classpath
new file mode 100644
index 00000000000..3e5654f17eb
--- /dev/null
+++ b/org.eclipse.text.quicksearch.tests/.classpath
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <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">
+ <attributes>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.text.quicksearch.tests/.project b/org.eclipse.text.quicksearch.tests/.project
new file mode 100644
index 00000000000..b370cef0340
--- /dev/null
+++ b/org.eclipse.text.quicksearch.tests/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.text.quicksearch.tests</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.text.quicksearch.tests/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.text.quicksearch.tests/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 00000000000..0c68a61dca8
--- /dev/null
+++ b/org.eclipse.text.quicksearch.tests/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/org.eclipse.text.quicksearch.tests/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.text.quicksearch.tests/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 00000000000..36d45928741
--- /dev/null
+++ b/org.eclipse.text.quicksearch.tests/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=true
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_functional_interfaces=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.insert_inferred_type_arguments=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_redundant_modifiers=false
+sp_cleanup.remove_redundant_semicolons=false
+sp_cleanup.remove_redundant_type_arguments=false
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_anonymous_class_creation=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_lambda=true
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/org.eclipse.text.quicksearch.tests/META-INF/MANIFEST.MF b/org.eclipse.text.quicksearch.tests/META-INF/MANIFEST.MF
new file mode 100644
index 00000000000..f9397634612
--- /dev/null
+++ b/org.eclipse.text.quicksearch.tests/META-INF/MANIFEST.MF
@@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.text.quicksearch.tests
+Bundle-Version: 1.0.0.qualifier
+Require-Bundle: org.eclipse.core.runtime,
+ org.eclipse.text.quicksearch,
+ org.eclipse.core.resources,
+ org.junit;bundle-version="4.8.0"
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Bundle-Vendor: %providerName
+Export-Package: org.eclipse.text.quicksearch.tests
+Automatic-Module-Name: org.eclipse.text.quicksearch.tests
diff --git a/org.eclipse.text.quicksearch.tests/about.html b/org.eclipse.text.quicksearch.tests/about.html
new file mode 100644
index 00000000000..c3b1d8fd59c
--- /dev/null
+++ b/org.eclipse.text.quicksearch.tests/about.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<title>About</title>
+</head>
+<body lang="EN-US">
+ <h2>About This Content</h2>
+
+ <p>July 2, 2019</p>
+ <h3>License</h3>
+
+ <p>
+ The Eclipse Foundation makes available all content in this plug-in
+ (&quot;Content&quot;). Unless otherwise indicated below, the Content
+ is provided to you under the terms and conditions of the Eclipse
+ Public License Version 2.0 (&quot;EPL&quot;). A copy of the EPL is
+ available at <a href="http://www.eclipse.org/legal/epl-2.0">http://www.eclipse.org/legal/epl-2.0</a>.
+ For purposes of the EPL, &quot;Program&quot; will mean the Content.
+ </p>
+
+ <p>
+ If you did not receive this Content directly from the Eclipse
+ Foundation, the Content is being redistributed by another party
+ (&quot;Redistributor&quot;) and different terms and conditions may
+ apply to your use of any object code in the Content. Check the
+ Redistributor's license that was provided with the Content. If no such
+ license exists, contact the Redistributor. Unless otherwise indicated
+ below, the terms and conditions of the EPL still apply to any source
+ code in the Content and such source code may be obtained at <a
+ href="http://www.eclipse.org/">http://www.eclipse.org</a>.
+ </p>
+
+</body>
+</html> \ No newline at end of file
diff --git a/org.eclipse.text.quicksearch.tests/build.properties b/org.eclipse.text.quicksearch.tests/build.properties
new file mode 100644
index 00000000000..8762079a174
--- /dev/null
+++ b/org.eclipse.text.quicksearch.tests/build.properties
@@ -0,0 +1,18 @@
+###############################################################################
+# Copyright (c) 2013-2019 Pivotal Inc and others.
+#
+# This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License 2.0
+# which accompanies this distribution, and is available at
+# https://www.eclipse.org/legal/epl-2.0/
+#
+# SPDX-License-Identifier: EPL-2.0
+#
+# Contributors:
+# Pivotal Inc - initial API and implementation
+###############################################################################
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ about.html,\
+ .
diff --git a/org.eclipse.text.quicksearch.tests/plugin.properties b/org.eclipse.text.quicksearch.tests/plugin.properties
new file mode 100644
index 00000000000..b399c2cf298
--- /dev/null
+++ b/org.eclipse.text.quicksearch.tests/plugin.properties
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright (c) 2019 Pivotal Inc and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v2.0
+# which accompanies this distribution, and is available at
+# https://www.eclipse.org/legal/epl-2.0/
+#
+# SPDX-License-Identifier: EPL-2.0
+#
+# Contributors:
+# Pivotal Inc - initial API and implementation
+###############################################################################
+
+pluginName= Tests for org.eclipse.text.quicksearch Bundle
+providerName= Eclipse.org
diff --git a/org.eclipse.text.quicksearch.tests/pom.xml b/org.eclipse.text.quicksearch.tests/pom.xml
new file mode 100644
index 00000000000..ef0c5fc224b
--- /dev/null
+++ b/org.eclipse.text.quicksearch.tests/pom.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2012, 2014. 2019 Eclipse Foundation and others.
+ All rights reserved. This program and the accompanying materials
+ are made available under the terms of the Eclipse Distribution License v2.0
+ which accompanies this distribution, and is available at
+ http://www.eclipse.org/org/documents/edl-v20.php
+
+ SPDX-License-Identifier: EPL-2.0
+
+ Contributors:
+ Igor Fedorenko - initial implementation
+ Pivotal Inc - copy adapted for quicksearch
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>tests-pom</artifactId>
+ <groupId>eclipse.platform.text</groupId>
+ <version>4.13.0-SNAPSHOT</version>
+ <relativePath>../tests-pom/</relativePath>
+ </parent>
+ <groupId>org.eclipse.text</groupId>
+ <artifactId>org.eclipse.text.quicksearch.tests</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>eclipse-test-plugin</packaging>
+ <properties>
+ <testSuite>${project.artifactId}</testSuite>
+ <testClass>org.eclipse.text.quicksearch.tests.*Test</testClass>
+ <skipAPIAnalysis>true</skipAPIAnalysis> <!-- https://bugs.eclipse.org/bugs/show_bug.cgi?id=548518#c41 -->
+ </properties>
+</project>
diff --git a/org.eclipse.text.quicksearch.tests/src/org/eclipse/text/quicksearch/tests/MockResource.java b/org.eclipse.text.quicksearch.tests/src/org/eclipse/text/quicksearch/tests/MockResource.java
new file mode 100644
index 00000000000..4c66bbbb113
--- /dev/null
+++ b/org.eclipse.text.quicksearch.tests/src/org/eclipse/text/quicksearch/tests/MockResource.java
@@ -0,0 +1,448 @@
+/*******************************************************************************
+ * Copyright (c) 2013-2019 Pivotal Software, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * Contributors:
+ * Pivotal Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.tests;
+
+import java.net.URI;
+import java.util.Map;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IPathVariableManager;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceProxy;
+import org.eclipse.core.resources.IResourceProxyVisitor;
+import org.eclipse.core.resources.IResourceVisitor;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourceAttributes;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+public class MockResource implements IResource {
+
+ IPath fullPath;
+
+ public MockResource(String pathStr) {
+ this.fullPath = new Path(pathStr);
+ }
+
+ @Override
+ public String toString() {
+ return fullPath.toString();
+ }
+
+ @Override
+ public Object getAdapter(Class adapter) {
+ return null;
+ }
+
+ @Override
+ public boolean contains(ISchedulingRule rule) {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public boolean isConflicting(ISchedulingRule rule) {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void accept(IResourceProxyVisitor visitor, int memberFlags) throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void accept(IResourceProxyVisitor visitor, int depth, int memberFlags) throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void accept(IResourceVisitor visitor) throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void accept(IResourceVisitor visitor, int depth,
+ boolean includePhantoms) throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void accept(IResourceVisitor visitor, int depth, int memberFlags)
+ throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void clearHistory(IProgressMonitor monitor) throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void copy(IPath destination, boolean force, IProgressMonitor monitor)
+ throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void copy(IPath destination, int updateFlags,
+ IProgressMonitor monitor) throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void copy(IProjectDescription description, boolean force,
+ IProgressMonitor monitor) throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void copy(IProjectDescription description, int updateFlags,
+ IProgressMonitor monitor) throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public IMarker createMarker(String type) throws CoreException {
+ // TODO Auto-generated method stub
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public IResourceProxy createProxy() {
+ // TODO Auto-generated method stub
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void delete(boolean force, IProgressMonitor monitor)
+ throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void delete(int updateFlags, IProgressMonitor monitor)
+ throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void deleteMarkers(String type, boolean includeSubtypes, int depth)
+ throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public boolean exists() {
+ return true;
+ }
+
+ @Override
+ public IMarker findMarker(long id) throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public IMarker[] findMarkers(String type, boolean includeSubtypes, int depth)
+ throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public int findMaxProblemSeverity(String type, boolean includeSubtypes,
+ int depth) throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public String getFileExtension() {
+ return fullPath.getFileExtension();
+ }
+
+ @Override
+ public IPath getFullPath() {
+ return fullPath;
+ }
+
+ @Override
+ public long getLocalTimeStamp() {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public IPath getLocation() {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public URI getLocationURI() {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public IMarker getMarker(long id) {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public long getModificationStamp() {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public String getName() {
+ String name = fullPath.lastSegment();
+ if (name!=null) {
+ return name;
+ }
+ return "";
+ }
+
+ @Override
+ public IPathVariableManager getPathVariableManager() {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public IContainer getParent() {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public Map<QualifiedName, String> getPersistentProperties()
+ throws CoreException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getPersistentProperty(QualifiedName key) throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public IProject getProject() {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public IPath getProjectRelativePath() {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public IPath getRawLocation() {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public URI getRawLocationURI() {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public ResourceAttributes getResourceAttributes() {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public Map<QualifiedName, Object> getSessionProperties()
+ throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public Object getSessionProperty(QualifiedName key) throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public int getType() {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public IWorkspace getWorkspace() {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public boolean isAccessible() {
+ return true;
+ }
+
+ @Override
+ public boolean isDerived() {
+ return false;
+ }
+
+ @Override
+ public boolean isDerived(int options) {
+ return false;
+ }
+
+ @Override
+ public boolean isHidden() {
+ return false;
+ }
+
+ @Override
+ public boolean isHidden(int options) {
+ return false;
+ }
+
+ @Override
+ public boolean isLinked() {
+ return false;
+ }
+
+ @Override
+ public boolean isVirtual() {
+ return false;
+ }
+
+ @Override
+ public boolean isLinked(int options) {
+ return false;
+ }
+
+ @Override
+ public boolean isLocal(int depth) {
+ return false;
+ }
+
+ @Override
+ public boolean isPhantom() {
+ return false;
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return false;
+ }
+
+ @Override
+ public boolean isSynchronized(int depth) {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public boolean isTeamPrivateMember() {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public boolean isTeamPrivateMember(int options) {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void move(IPath destination, boolean force, IProgressMonitor monitor)
+ throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void move(IPath destination, int updateFlags,
+ IProgressMonitor monitor) throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void move(IProjectDescription description, boolean force,
+ boolean keepHistory, IProgressMonitor monitor) throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void move(IProjectDescription description, int updateFlags,
+ IProgressMonitor monitor) throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void refreshLocal(int depth, IProgressMonitor monitor)
+ throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void revertModificationStamp(long value) throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void setDerived(boolean isDerived) throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void setDerived(boolean isDerived, IProgressMonitor monitor)
+ throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void setHidden(boolean isHidden) throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void setLocal(boolean flag, int depth, IProgressMonitor monitor)
+ throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public long setLocalTimeStamp(long value) throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void setPersistentProperty(QualifiedName key, String value)
+ throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void setReadOnly(boolean readOnly) {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void setResourceAttributes(ResourceAttributes attributes)
+ throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void setSessionProperty(QualifiedName key, Object value)
+ throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void setTeamPrivateMember(boolean isTeamPrivate)
+ throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+ @Override
+ public void touch(IProgressMonitor monitor) throws CoreException {
+ throw new Error("Not implemented");
+ }
+
+}
diff --git a/org.eclipse.text.quicksearch.tests/src/org/eclipse/text/quicksearch/tests/PrioriTreeTest.java b/org.eclipse.text.quicksearch.tests/src/org/eclipse/text/quicksearch/tests/PrioriTreeTest.java
new file mode 100644
index 00000000000..ac972c420af
--- /dev/null
+++ b/org.eclipse.text.quicksearch.tests/src/org/eclipse/text/quicksearch/tests/PrioriTreeTest.java
@@ -0,0 +1,165 @@
+/*******************************************************************************
+ * Copyright (c) 2013-2019 Pivotal Software, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * Contributors:
+ * Pivotal Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.tests;
+
+import static org.eclipse.text.quicksearch.internal.core.priority.PriorityFunction.PRIORITY_DEFAULT;
+import static org.eclipse.text.quicksearch.internal.core.priority.PriorityFunction.PRIORITY_IGNORE;
+
+import org.eclipse.core.runtime.Path;
+import org.eclipse.text.quicksearch.internal.core.priority.PrioriTree;
+
+import junit.framework.TestCase;
+
+@SuppressWarnings("restriction")
+public class PrioriTreeTest extends TestCase {
+
+ PrioriTree tree;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ tree = PrioriTree.create();
+ }
+
+ public void testWithEmptyTree() {
+ //In the empty tree most paths are assigned 'DEFAULT' priority.
+ checkPriority(PRIORITY_DEFAULT, "/");
+ checkPriority(PRIORITY_DEFAULT, "/foo/bar/zor");
+
+ //Paths with ignored extensions should be 'ignored'.
+ checkPriority(PRIORITY_IGNORE, "/foo/muck.zip");
+ checkPriority(PRIORITY_IGNORE, "/muck.jar");
+ checkPriority(PRIORITY_IGNORE, "/images/muck.jpg");
+
+ //Names starting with ignored prefix should be ignored
+ checkPriority(PRIORITY_IGNORE, "/project/.git");
+
+ //Some specific names are also to be ignored
+ checkPriority(PRIORITY_IGNORE, "/project/target");
+ checkPriority(PRIORITY_IGNORE, "/project/build");
+
+ }
+
+ public void testSinglePathSet() {
+ setPriority("/foo/bar/zor", 100.0);
+
+ //Path itself should have the set priority
+ checkPriority(100.0, "/foo/bar/zor");
+
+ //Also the parent paths should have been set automatically
+ checkPriority(100.0, "/foo/bar");
+ checkPriority(100.0, "/foo");
+ checkPriority(100.0, "/");
+
+ //Things not on the paths should still be 'default'
+ checkPriority(PRIORITY_DEFAULT, "/other/bar");
+ checkPriority(PRIORITY_DEFAULT, "/other");
+
+ //The things nested underneath the set path also get assigned implicitly ...
+ checkPriority(100.0, "/foo/bar/zor/nested");
+ checkPriority(100.0, "/foo/bar/zor/nested/deeper");
+ // ... unless they are 'ignored'. Ignored paths are never converted to non-ignored.
+
+ checkPriority(PRIORITY_IGNORE, "/foo/bar/zor/nested/big.zip");
+ }
+
+ public void testSetOverlappingPaths() {
+ setPriority("/shared/foo", 50.0);
+ setPriority("/shared/bar", 100.0);
+
+ tree.dump();
+
+ checkPriority(50.0, "/shared/foo");
+ checkPriority(100.0, "/shared/bar");
+
+ //Shared section of path should get highest priority of both
+ checkPriority(100.0, "/");
+ checkPriority(100.0, "/shared");
+
+ //Disjoint paths remain default
+ checkPriority(PRIORITY_DEFAULT, "/other");
+ }
+
+ /**
+ * Similar to testSetOverlappingPaths but order of
+ * priority set operations is reversed. The result should
+ * be the same.
+ */
+ public void testSetOverlappingPaths2() {
+ setPriority("/shared/bar", 100.0);
+ setPriority("/shared/foo", 50.0);
+
+ checkPriority(50.0, "/shared/foo");
+ checkPriority(100.0, "/shared/bar");
+
+ //Shared section of path should get highest priority of both
+ checkPriority(100.0, "/");
+ checkPriority(100.0, "/shared");
+
+ //Disjoint paths remain default
+ checkPriority(PRIORITY_DEFAULT, "/other");
+ }
+
+ /**
+ * Need support for setting priority of an entire subtree.
+ */
+ public void testSetTreePriority() {
+ setPriority("/promoted", 100.0);
+
+ //Stuff not in the raised subtree should be unchanged
+ checkPriority(PRIORITY_DEFAULT, "/unrelated");
+
+ //Stuff in the raised subtree should be affected.
+ checkPriority(100.0, "/promoted");
+ checkPriority(100.0, "/promoted/sub");
+ checkPriority(100.0, "/promoted/sub/sub");
+
+ //But... ignored stuff should never be made searchable even in a raised subtree.
+ checkPriority(PRIORITY_IGNORE, "/promoted/big.zip");
+ }
+
+ /**
+ * Check that setting priotity of a tree raises children priority also if those
+ * children already had a priority assigned before.
+ */
+ public void testSetTreePriority2() {
+ setPriority("/promoted/sub/sub", 50.0);
+ checkPriority(50.0, "/promoted");
+ checkPriority(50.0, "/promoted/sub");
+ checkPriority(50.0, "/promoted/sub/sub");
+ checkPriority(PRIORITY_DEFAULT, "/promoted/other");
+
+ setPriority("/promoted", 100.0);
+
+ //Stuff not in the raised subtree should be unchanged
+ checkPriority(PRIORITY_DEFAULT, "/unrelated");
+
+ //Stuff in the raised subtree should be affected.
+ checkPriority(100.0, "/promoted");
+ checkPriority(100.0, "/promoted/sub");
+ checkPriority(100.0, "/promoted/sub/sub");
+ checkPriority(100.0, "/promoted/other");
+
+ //But... ignored stuff should never be made searchable even in a raised subtree.
+ checkPriority(PRIORITY_IGNORE, "/promoted/sub/big.zip");
+ checkPriority(PRIORITY_IGNORE, "/promoted/other/big.zip");
+ }
+
+ private void setPriority(String pathStr, double pri) {
+ tree.setPriority(new Path(pathStr), pri);
+ }
+
+ private void checkPriority(double expected, String pathStr) {
+ assertEquals(pathStr,
+ expected, tree.priority(new MockResource(pathStr)));
+ }
+
+}
diff --git a/org.eclipse.text.quicksearch.tests/src/org/eclipse/text/quicksearch/tests/ResourceMatcherTest.java b/org.eclipse.text.quicksearch.tests/src/org/eclipse/text/quicksearch/tests/ResourceMatcherTest.java
new file mode 100644
index 00000000000..4ecce5d0736
--- /dev/null
+++ b/org.eclipse.text.quicksearch.tests/src/org/eclipse/text/quicksearch/tests/ResourceMatcherTest.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Pivotal, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.eclipse.core.runtime.Path;
+import org.eclipse.text.quicksearch.internal.core.pathmatch.ResourceMatcher;
+import org.eclipse.text.quicksearch.internal.core.pathmatch.ResourceMatchers;
+import org.junit.Test;
+
+@SuppressWarnings("restriction")
+public class ResourceMatcherTest {
+
+ @Test
+ public void simpleRelativePattern() throws Exception {
+ assertMatch(true, "*.java", "/myproject/something/nested/foo.java");
+ assertMatch(false, "*.java", "/myproject/foo.class");
+ }
+
+ @Test
+ public void commaSeparatedPaths() throws Exception {
+ assertMatch(true, "*.java,*.properties", "/myproject/something/nested/foo.java");
+ assertMatch(true, "*.java,*.properties", "/myproject/something/nested/application.properties");
+ }
+
+ @Test
+ public void complexRelativePattern() throws Exception {
+ assertMatch(true, "src/**/*.java", "/myproject/src/my/package/Foo.java");
+ assertMatch(false, "src/**/*.java", "/myproject/resources/my/package/Foo.java");
+ }
+
+ @Test
+ public void absolutePath() throws Exception {
+ assertMatch(true, "/myproject/**/*.java", "/myproject/src/my/package/Foo.java");
+ assertMatch(false, "/myproject/**/*.java", "/otherproject/src/my/package/Foo.java");
+ }
+
+ private void assertMatch(boolean expectedMatch, String patterns, String path) {
+ assertTrue(new Path(path).isAbsolute());
+ ResourceMatcher matcher = ResourceMatchers.commaSeparatedPaths(patterns);
+ assertEquals(expectedMatch, matcher.matches(new MockResource(path)));
+ }
+}
diff --git a/org.eclipse.text.quicksearch/.classpath b/org.eclipse.text.quicksearch/.classpath
new file mode 100644
index 00000000000..6726f63d347
--- /dev/null
+++ b/org.eclipse.text.quicksearch/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <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 excluding="org/springsource/ide/eclipse/commons/quicksearch/ui/FileSearchViewer.java|org/springsource/ide/eclipse/commons/quicksearch/ui/QuickSearchResult.java" kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.text.quicksearch/.project b/org.eclipse.text.quicksearch/.project
new file mode 100644
index 00000000000..f29a963e865
--- /dev/null
+++ b/org.eclipse.text.quicksearch/.project
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.text.quicksearch</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+ <filteredResources>
+ <filter>
+ <id>1366673713400</id>
+ <name></name>
+ <type>10</type>
+ <matcher>
+ <id>org.eclipse.ui.ide.multiFilter</id>
+ <arguments>1.0-projectRelativePath-matches-false-false-target</arguments>
+ </matcher>
+ </filter>
+ </filteredResources>
+</projectDescription>
diff --git a/org.eclipse.text.quicksearch/.settings/org.eclipse.core.resources.prefs b/org.eclipse.text.quicksearch/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 00000000000..da81118144d
--- /dev/null
+++ b/org.eclipse.text.quicksearch/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding//src/org/eclipse/text/quicksearch/internal/ui/messages.properties=ISO-8859-1
diff --git a/org.eclipse.text.quicksearch/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.text.quicksearch/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 00000000000..0c68a61dca8
--- /dev/null
+++ b/org.eclipse.text.quicksearch/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/org.eclipse.text.quicksearch/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.text.quicksearch/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 00000000000..c576259787c
--- /dev/null
+++ b/org.eclipse.text.quicksearch/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_functional_interfaces=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.insert_inferred_type_arguments=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_redundant_modifiers=false
+sp_cleanup.remove_redundant_semicolons=false
+sp_cleanup.remove_redundant_type_arguments=false
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_anonymous_class_creation=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_lambda=true
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/org.eclipse.text.quicksearch/META-INF/MANIFEST.MF b/org.eclipse.text.quicksearch/META-INF/MANIFEST.MF
new file mode 100644
index 00000000000..dc662211d24
--- /dev/null
+++ b/org.eclipse.text.quicksearch/META-INF/MANIFEST.MF
@@ -0,0 +1,30 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.text.quicksearch;singleton:=true
+Bundle-Version: 1.0.0.qualifier
+Bundle-Activator: org.eclipse.text.quicksearch.internal.ui.QuickSearchActivator
+Require-Bundle: org.eclipse.ui;bundle-version="[3.113.0,4.0.0)",
+ org.eclipse.core.resources;bundle-version="[3.13.0,4.0.0)",
+ org.eclipse.ui.ide;bundle-version="[3.16.0,4.0.0)",
+ org.eclipse.core.expressions;bundle-version="[3.6.0,4.0.0)",
+ org.eclipse.search;bundle-version="[3.11.0,4.0.0)",
+ org.eclipse.ui.editors;bundle-version="[3.11.0,4.0.0)",
+ org.eclipse.jface;bundle-version="[3.17.0,4.0.0)",
+ org.eclipse.jface.text;bundle-version="[3.15.0,4.0.0)",
+ org.apache.ant;bundle-version="[1.10.0,2.0.0)"
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Export-Package: org.eclipse.text.quicksearch.internal.core;x-internal:=true,
+ org.eclipse.text.quicksearch.internal.core.pathmatch;x-internal:=true,
+ org.eclipse.text.quicksearch.internal.core.preferences;x-internal:=true,
+ org.eclipse.text.quicksearch.internal.core.priority;x-internal:=true,
+ org.eclipse.text.quicksearch.internal.ui;x-internal:=true,
+ org.eclipse.text.quicksearch.internal.util;x-internal:=true
+Import-Package: org.eclipse.core.runtime;version="3.5.0",
+ org.eclipse.core.runtime.jobs,
+ org.eclipse.osgi.util;version="1.1.0",
+ org.osgi.framework;version="1.9.0"
+Automatic-Module-Name: org.eclipse.text.quicksearch
diff --git a/org.eclipse.text.quicksearch/about.html b/org.eclipse.text.quicksearch/about.html
new file mode 100644
index 00000000000..c3b1d8fd59c
--- /dev/null
+++ b/org.eclipse.text.quicksearch/about.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<title>About</title>
+</head>
+<body lang="EN-US">
+ <h2>About This Content</h2>
+
+ <p>July 2, 2019</p>
+ <h3>License</h3>
+
+ <p>
+ The Eclipse Foundation makes available all content in this plug-in
+ (&quot;Content&quot;). Unless otherwise indicated below, the Content
+ is provided to you under the terms and conditions of the Eclipse
+ Public License Version 2.0 (&quot;EPL&quot;). A copy of the EPL is
+ available at <a href="http://www.eclipse.org/legal/epl-2.0">http://www.eclipse.org/legal/epl-2.0</a>.
+ For purposes of the EPL, &quot;Program&quot; will mean the Content.
+ </p>
+
+ <p>
+ If you did not receive this Content directly from the Eclipse
+ Foundation, the Content is being redistributed by another party
+ (&quot;Redistributor&quot;) and different terms and conditions may
+ apply to your use of any object code in the Content. Check the
+ Redistributor's license that was provided with the Content. If no such
+ license exists, contact the Redistributor. Unless otherwise indicated
+ below, the terms and conditions of the EPL still apply to any source
+ code in the Content and such source code may be obtained at <a
+ href="http://www.eclipse.org/">http://www.eclipse.org</a>.
+ </p>
+
+</body>
+</html> \ No newline at end of file
diff --git a/org.eclipse.text.quicksearch/build.properties b/org.eclipse.text.quicksearch/build.properties
new file mode 100644
index 00000000000..31b9d2c0254
--- /dev/null
+++ b/org.eclipse.text.quicksearch/build.properties
@@ -0,0 +1,21 @@
+###############################################################################
+# Copyright (c) 2013-2019 Pivotal Inc and others.
+#
+# This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License 2.0
+# which accompanies this distribution, and is available at
+# https://www.eclipse.org/legal/epl-2.0/
+#
+# SPDX-License-Identifier: EPL-2.0
+#
+# Contributors:
+# Pivotal Inc - initial API and implementation
+###############################################################################
+source.. = src/
+output.. = bin/
+bin.includes = plugin.xml,\
+ META-INF/,\
+ .,\
+ icons/,\
+ about.html,\
+ plugin.properties
diff --git a/org.eclipse.text.quicksearch/plugin.properties b/org.eclipse.text.quicksearch/plugin.properties
new file mode 100644
index 00000000000..a2292cbc515
--- /dev/null
+++ b/org.eclipse.text.quicksearch/plugin.properties
@@ -0,0 +1,16 @@
+###############################################################################
+# Copyright (c) 2019 Pivotal Inc and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v2.0
+# which accompanies this distribution, and is available at
+# https://www.eclipse.org/legal/epl-2.0/
+#
+# SPDX-License-Identifier: EPL-2.0
+#
+# Contributors:
+# Pivotal Inc - initial API and implementation
+###############################################################################
+
+pluginName= Quick Search
+providerName= Eclipse.org
+searchMenu.label= Se&arch
diff --git a/org.eclipse.text.quicksearch/plugin.xml b/org.eclipse.text.quicksearch/plugin.xml
new file mode 100644
index 00000000000..6570905c1f7
--- /dev/null
+++ b/org.eclipse.text.quicksearch/plugin.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2019 Pivotal Inc. and others.
+ All rights reserved. This program and the accompanying materials
+ are made available under the terms of the Eclipse Distribution License v2.0
+ which accompanies this distribution, and is available at
+ http://www.eclipse.org/org/documents/edl-v20.php
+
+ SPDX-License-Identifier: EPL-2.0
+
+ Contributors:
+ Pivotal Inc - Initial API and implementation
+-->
+<?eclipse version="3.4"?>
+<plugin>
+
+ <extension
+ point="org.eclipse.ui.commands">
+ <category
+ name="Quick Search Category"
+ id="org.eclipse.text.quicksearch.commands.category">
+ </category>
+ <command
+ name="Quick Search Command"
+ categoryId="org.eclipse.text.quicksearch.commands.category"
+ id="org.eclipse.text.quicksearch.commands.quicksearchCommand">
+ </command>
+ </extension>
+
+ <!-- it seems that using the new and recommended way of contributing entries to existing menus
+ doesn't work for the search menu.
+ So we have to use the old mechanism using actionSets
+ See here https://stackoverflow.com/questions/7113380/how-to-extend-the-source-menu-in-eclipse-or-what-is-its-locationuri
+ -->
+ <extension
+ point="org.eclipse.ui.actionSets">
+
+ <actionSet
+ label="Quick Search ActionSet"
+ visible="true"
+ id="org.eclipse.text.quicksearch.actionSet">
+
+
+ <!-- =================================================================== -->
+ <!-- Search Menu -->
+ <!-- =================================================================== -->
+
+ <!-- weirdly, it seems we have to copy this code here because otherwise we
+ sometimes get an error that the menu does not exist. Presumably this
+ is because somehow our extensions are getting initialized before the
+ extensions in other plugins that define the menu -->
+ <menu
+ id="org.eclipse.search.menu"
+ label="%searchMenu.label"
+ path="navigate">
+ <groupMarker name="internalDialogGroup"/> <!-- not to be used by clients -->
+ <groupMarker name="dialogGroup"/> <!-- to be used by clients -->
+ <separator name="fileSearchContextMenuActionsGroup"/> <!-- to be used by clients -->
+ <separator name="contextMenuActionsGroup"/> <!-- to be used by clients -->
+ <separator name="occurencesActionsGroup"/> <!-- to be used by clients -->
+ <separator name="extraSearchGroup"/> <!-- to be used by clients -->
+ </menu>
+
+ <action
+ id="org.eclipse.text.quicksearch.commands.quicksearchAction"
+ class="org.eclipse.text.quicksearch.internal.ui.QuickSearchAction"
+ definitionId="org.eclipse.text.quicksearch.commands.quicksearchCommand"
+ label="Quick Search"
+ menubarPath="org.eclipse.search.menu/extraSearchGroup"
+ tooltip="Search for Text pattern in the workspace">
+ </action>
+ </actionSet>
+ </extension>
+
+<!-- This doesn't work unfortunately...
+ <extension point="org.eclipse.ui.menus">
+ <menuContribution locationURI="menu:navigate?after=additions">
+ <command commandId="org.eclipse.text.quicksearch.commands.quicksearchCommand"
+ label="Quick Search"
+ mnemonic="Q">
+ </command>
+ </menuContribution>
+ </extension> -->
+
+ <!-- Allthough we have an action defined, it seems the action isn't always working. So for good measure
+ also define a handler -->
+ <extension
+ point="org.eclipse.ui.handlers">
+ <handler
+ commandId="org.eclipse.text.quicksearch.commands.quicksearchCommand"
+ class="org.eclipse.text.quicksearch.internal.ui.QuickSearchHandler">
+ </handler>
+ </extension>
+
+<!-- Define keybinding -->
+ <extension
+ point="org.eclipse.ui.bindings">
+ <key
+ commandId="org.eclipse.text.quicksearch.commands.quicksearchCommand"
+ contextId="org.eclipse.ui.contexts.window"
+ sequence="M1+M2+M3+L"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration">
+ </key>
+ </extension>
+
+<extension
+ point = "org.eclipse.ui.preferencePages">
+ <page id="org.eclipse.text.quicksearch.PreferencesPage"
+ class="org.eclipse.text.quicksearch.internal.ui.QuickSearchPreferencesPage"
+ name="Quick Search">
+ </page>
+</extension>
+
+</plugin>
diff --git a/org.eclipse.text.quicksearch/pom.xml b/org.eclipse.text.quicksearch/pom.xml
new file mode 100644
index 00000000000..6720f960ab2
--- /dev/null
+++ b/org.eclipse.text.quicksearch/pom.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (c) 2012, 2014. 2019 Eclipse Foundation and others. All rights
+ reserved. This program and the accompanying materials are made available
+ under the terms of the Eclipse Distribution License v2.0 which accompanies
+ this distribution, and is available at http://www.eclipse.org/org/documents/edl-v20.php
+
+ SPDX-License-Identifier: EPL-2.0
+
+ Contributors: Igor Fedorenko - initial implementation
+ Pivotal Inc - copy adapted for quicksearch bundle
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>eclipse.platform.text</artifactId>
+ <groupId>eclipse.platform.text</groupId>
+ <version>4.13.0-SNAPSHOT</version>
+ </parent>
+ <groupId>org.eclipse.core</groupId>
+ <artifactId>org.eclipse.text.quicksearch</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>eclipse-plugin</packaging>
+
+ <properties>
+ <skipAPIAnalysis>true</skipAPIAnalysis> <!-- https://bugs.eclipse.org/bugs/show_bug.cgi?id=548518#c41 -->
+ </properties>
+</project>
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/LineItem.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/LineItem.java
new file mode 100644
index 00000000000..6867e96d96d
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/LineItem.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Pivotal Software, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.core;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.search.internal.ui.text.FileMatch;
+
+@SuppressWarnings("restriction")
+public class LineItem {
+
+ IFile f;
+ String line;
+ int lineNumber;
+ int lineOffset;
+
+ public LineItem(IFile f, String line, int lineNumber, int lineOffset) {
+ this.f = f;
+ this.line = line;
+ this.lineNumber = lineNumber;
+ this.lineOffset = lineOffset;
+ }
+
+ public LineItem(FileMatch match) {
+ this.f = match.getFile();
+ this.line = match.getLineElement().getContents();
+ this.lineNumber = match.getLineElement().getLine();
+ this.lineOffset = match.getLineElement().getOffset();
+ }
+
+ @Override
+ public String toString() {
+ return lineNumber + ": " + line + " (" +f.getProjectRelativePath() + ")";
+ }
+
+ public String getText() {
+ return line;
+ }
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ public IFile getFile() {
+ return this.f;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((f == null) ? 0 : f.hashCode());
+ result = prime * result + lineNumber;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ LineItem other = (LineItem) obj;
+ if (f == null) {
+ if (other.f != null)
+ return false;
+ } else if (!f.equals(other.f))
+ return false;
+ if (lineNumber != other.lineNumber)
+ return false;
+ return true;
+ }
+
+ public int getOffset() {
+ return lineOffset;
+ }
+
+
+
+}
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/QuickTextQuery.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/QuickTextQuery.java
new file mode 100644
index 00000000000..e6ce2edb826
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/QuickTextQuery.java
@@ -0,0 +1,229 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Pivotal Software, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.core;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jface.text.IRegion;
+
+/**
+ * Represents something you can search for with a 'quick search' text searcher.
+ *
+ * @author Kris De Volder
+ */
+@SuppressWarnings("restriction")
+public class QuickTextQuery {
+
+ //TODO: delete and use jface Region class instead.
+ public class TextRange implements IRegion {
+ public final int start;
+ public final int len;
+ public TextRange(int start, int len) {
+ this.start = start;
+ this.len = len;
+ }
+ public int getLength() {
+ return len;
+ }
+ public int getOffset() {
+ return start;
+ }
+ }
+
+ private boolean caseSensitive;
+ private String orgPattern; //Original pattern case preserved even if search is case insensitive.
+ private Pattern pattern;
+
+ /**
+ * A query that matches anything.
+ */
+ public QuickTextQuery() {
+ this("", true);
+ }
+
+ public QuickTextQuery(String substring, boolean caseSensitive) {
+ this.orgPattern = substring;
+ this.caseSensitive = caseSensitive;
+ createMatcher(substring, caseSensitive);
+ }
+
+ /**
+ * Compile a pattern string into an RegExp and create a Matcher for that
+ * regexp. This is so we can 'compile' the pattern once and then keep reusing
+ * the matcher or compiled pattern.
+ */
+ private void createMatcher(String patString, boolean caseSensitive) {
+ StringBuilder segment = new StringBuilder(); //Accumulates text that needs to be 'quoted'
+ StringBuilder regexp = new StringBuilder(); //Accumulates 'compiled' pattern
+ int pos = 0, len = patString.length();
+ while (pos<len) {
+ char c = patString.charAt(pos++);
+ switch (c) {
+ case '?':
+ appendSegment(segment, regexp);
+ regexp.append(".");
+ break;
+ case '*':
+ appendSegment(segment, regexp);
+ regexp.append(".*");
+ break;
+ case '\\':
+ if (pos<len) {
+ char nextChar = patString.charAt(pos);
+ if (nextChar=='*' || nextChar=='?' || nextChar=='\\') {
+ segment.append(nextChar);
+ pos++;
+ break;
+ }
+ }
+ default:
+ //Char is 'nothing special'. Add it to segment that will be wrapped in 'quotes'
+ segment.append(c);
+ break;
+ }
+ }
+ //Don't forget to process that last segment.
+ appendSegment(segment, regexp);
+
+ this.pattern = Pattern.compile(regexp.toString(), caseSensitive?0:Pattern.CASE_INSENSITIVE);
+// this.matcher = pattern.matcher("");
+ }
+
+ private void appendSegment(StringBuilder segment, StringBuilder regexp) {
+ if (segment.length()>0) {
+ regexp.append(Pattern.quote(segment.toString()));
+ segment.setLength(0); //clear: ready for next segment
+ }
+ //else {
+ // nothing to append
+ //}
+ }
+
+ public boolean equalsFilter(QuickTextQuery o) {
+ //TODO: actually for case insensitive matches we could relax this and treat patterns that
+ // differ only in case as the same.
+ return this.caseSensitive == o.caseSensitive && this.orgPattern.equals(o.orgPattern);
+ }
+
+ /**
+ * Returns true if the other query is a specialisation of this query. I.e. any results matching the other
+ * query must also match this query. If this method returns true then we can optimise the search for other
+ * re-using already found results for this query.
+ * <p>
+ * If it is hard or impossible to decide whether other query is a specialisation of this query then this
+ * method is allowed to 'punt' and just return false. However, the consequence of this is that the query
+ * will be re-run instead of incrementally updated.
+ */
+ public boolean isSubFilter(QuickTextQuery other) {
+ if (this.isTrivial()) {
+ return false;
+ }
+ if (this.caseSensitive==other.caseSensitive) {
+ boolean caseSensitive = this.caseSensitive;
+ String otherPat = normalize(other.orgPattern, caseSensitive);
+ String thisPat = normalize(this.orgPattern, caseSensitive);
+ return otherPat.contains(thisPat);
+ }
+ return false;
+ }
+
+ /**
+ * Transforms a pattern string so we can use a simple 'substring' test to determine
+ * whether one pattern is sub-pattern of the other.
+ */
+ private String normalize(String pat, boolean caseSensitive) {
+ if (pat.endsWith("\\")) {
+ pat = pat + "\\";
+ }
+ if (!caseSensitive) {
+ pat = pat.toLowerCase();
+ }
+ return pat;
+ }
+
+ /**
+ * @return whether the LineItem text contains the search pattern.
+ */
+ public boolean matchItem(LineItem item) {
+ return matchItem(item.getText());
+ }
+
+ /**
+ * Same as matchItem except only takes the text of the item. This can
+ * be useful for efficient processing. In particular to avoid creating
+ * LineItem instances for non-matching lines.
+ */
+ public boolean matchItem(String item) {
+ //Alternate implementation. This is thread safe without synchronized,
+ // but it creates some garbage.
+ Matcher matcher = pattern.matcher(item); //Creating garbage here
+ return matcher.find();
+ }
+
+ /**
+ * A trivial query is one that either
+ * - matches anything
+ * - matches nothing
+ * In other words, if a query is 'trivial' then it returns either nothing or all the text in the scope
+ * of the search.
+ */
+ public boolean isTrivial() {
+ return "".equals(this.orgPattern);
+ }
+
+ @Override
+ public String toString() {
+ return "QTQuery("+orgPattern+", "+(caseSensitive?"caseSens":"caseInSens")+")";
+ }
+
+ public List<TextRange> findAll(String text) {
+ //alternate implementation without 'synchronized' but creates more garbage
+ if (isTrivial()) {
+ return Arrays.asList();
+ } else {
+ List<TextRange> ranges = new ArrayList<QuickTextQuery.TextRange>();
+ Matcher matcher = pattern.matcher(text);
+ while (matcher.find()) {
+ int start = matcher.start();
+ int end = matcher.end();
+ ranges.add(new TextRange(start, end-start));
+ }
+ return ranges;
+ }
+ }
+
+ public TextRange findFirst(String str) {
+ //TODO: more efficient implementation, just search the first one
+ // no need to find all matches then toss away everything except the
+ // first one.
+ List<TextRange> all = findAll(str);
+ if (all!=null && !all.isEmpty()) {
+ return all.get(0);
+ }
+ return null;
+ }
+
+ public String getPatternString() {
+ return orgPattern;
+ }
+
+ public boolean isCaseSensitive() {
+ return caseSensitive;
+ }
+
+
+}
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/QuickTextSearchRequestor.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/QuickTextSearchRequestor.java
new file mode 100644
index 00000000000..b46cd3dcb9f
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/QuickTextSearchRequestor.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Pivotal Software, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.core;
+
+/**
+ * Plays a similar role than SearchReqeustor in eclipse Searches. I.e. a search requestor
+ * is some entity accepting the results of a search. Typically the requestor displays the
+ * result to the user.
+ * <p>
+ * This API differs a little from the Eclipse SearchRequestor in that searches are 'live'.
+ * I.e the results are updating while the user is typing the query.
+ * As the query is changing, this may cause results that were added earlier being changed or
+ * revoked.
+ *
+ * @author Kris De Volder
+ */
+public class QuickTextSearchRequestor {
+
+ /**
+ * Called when a line of text containing the search text is found.
+ */
+ public void add(LineItem match) {}
+
+ /**
+ * Called when a previously added line of text needs to be redisplayed (this happens if
+ * the query has changed but still matches the line. I.e. the line is still a match, but
+ * the highlighting of the search term is different.
+ */
+ public void update(LineItem match) {}
+
+ /**
+ * Called when a line of text previously added is no longer a match for the current query.
+ * I.e. the line should no longer be displayed.
+ */
+ public void revoke(LineItem line) {}
+
+ /**
+ * Called when all previous results have become revoked at once.
+ * This happens when a query is changed in such a way that it can't be updated
+ * incrementally but needs to be completely restarted.
+ */
+ public void clear() {}
+}
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/QuickTextSearcher.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/QuickTextSearcher.java
new file mode 100644
index 00000000000..d8a23e1cc9f
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/QuickTextSearcher.java
@@ -0,0 +1,320 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Pivotal Software, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.core;
+
+import java.io.InputStreamReader;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.text.quicksearch.internal.core.pathmatch.ResourceMatcher;
+import org.eclipse.text.quicksearch.internal.core.pathmatch.ResourceMatchers;
+import org.eclipse.text.quicksearch.internal.core.priority.PriorityFunction;
+import org.eclipse.text.quicksearch.internal.util.LightSchedulingRule;
+import org.eclipse.text.quicksearch.internal.util.LineReader;
+
+public class QuickTextSearcher {
+ private final QuickTextSearchRequestor requestor;
+ private QuickTextQuery query;
+
+ /**
+ * Keeps track of currently found matches. Items are added as they are found and may also
+ * be removed when the query changed and they become invalid.
+ */
+ private Set<LineItem> matches = new HashSet<LineItem>(2000);
+
+ /**
+ * Scheduling rule used by Jobs that work on the matches collection.
+ */
+ private ISchedulingRule matchesRule = new LightSchedulingRule("QuickSearchMatchesRule");
+
+ private SearchInFilesWalker walker = null;
+ private IncrementalUpdateJob incrementalUpdate;
+
+ /**
+ * This field gets set to request a query change. The new query isn't stuffed directly
+ * into the query field because the query is responded to by the updater job which needs
+ * access to both the original query and the newQuery to decide on an efficient strategy.
+ */
+ private QuickTextQuery newQuery;
+
+ /**
+ * If number of accumulated results reaches maxResults the search will be suspended.
+ * <p>
+ * Note that more results may still arrive beyond the limit since the searcher does not (yet) have the
+ * capability to suspend/resume a search in the middle of a file.
+ */
+ private int maxResults = 200;
+
+ /**
+ * If a line of text is encountered longer than this, the searcher will stop searching
+ * that file (this rule avoids searching machine generated text files, like minified javascript).
+ */
+ private int MAX_LINE_LEN;
+
+ /**
+ * While searching in a file, this field will be set. This can be used to show the name
+ * of the 'current file' in the progress area of the quicksearch dialog.
+ */
+ private IFile currentFile = null;
+
+ /**
+ * Flag to disable incremental filtering logic based on incremental
+ * query updates. This forces a full refresh of the search results.
+ */
+ private boolean forceRefresh = false;
+ private ResourceMatcher pathMatcher = ResourceMatchers.ANY;
+
+ /**
+ * Retrieves the current result limit.
+ */
+ public int getMaxResults() {
+ return maxResults;
+ }
+
+ public void setMaxResults(int maxResults) {
+ this.maxResults = maxResults;
+ }
+
+ public QuickTextSearcher(QuickTextQuery query, PriorityFunction priorities, int maxLineLen, QuickTextSearchRequestor requestor) {
+ this.MAX_LINE_LEN = maxLineLen;
+ this.requestor = requestor;
+ this.query = query;
+ this.walker = createWalker(new PriorityFunction() {
+ @Override
+ public double priority(IResource r) {
+ double basePriority = priorities.priority(r);
+ if (basePriority==PRIORITY_IGNORE) {
+ return basePriority;
+ }
+ if (r.getType()==IResource.FILE && !pathMatcher.matches(r)) {
+ return PRIORITY_IGNORE;
+ }
+ return basePriority;
+ }
+ });
+ }
+
+ private SearchInFilesWalker createWalker(PriorityFunction priorities) {
+ final SearchInFilesWalker job = new SearchInFilesWalker();
+ job.setPriorityFun(priorities);
+ job.setRule(matchesRule);
+ job.schedule();
+ return job;
+ }
+
+ private final class SearchInFilesWalker extends ResourceWalker {
+
+
+ @Override
+ protected void visit(IFile f, IProgressMonitor mon) {
+ if (checkCanceled(mon)) {
+ return;
+ }
+
+
+ LineReader lr = null;
+ currentFile = f;
+ try {
+ lr = new LineReader(new InputStreamReader(f.getContents(true), f.getCharset()), MAX_LINE_LEN);
+ String line = null;
+ int lineIndex = 1;
+ while ((line = lr.readLine()) != null) {
+ int offset = lr.getLastLineOffset();
+ if (checkCanceled(mon)) {
+ return;
+ }
+
+ boolean found = query.matchItem(line);
+ if (found) {
+ LineItem lineItem = new LineItem(f, line, lineIndex, offset);
+ add(lineItem);
+ }
+
+ lineIndex++;
+ }
+ } catch (Exception e) {
+ } finally {
+ currentFile = null;
+ if (lr != null) {
+ lr.close();
+ }
+ }
+ }
+
+ @Override
+ public void resume() {
+ //Only resume if we don't already exceed the maxResult limit.
+ if (matches.size()<maxResults) {
+ super.resume();
+ }
+ }
+
+ private boolean checkCanceled(IProgressMonitor mon) {
+ return mon.isCanceled();
+ }
+
+ public void requestMoreResults() {
+ int currentSize = matches.size();
+ maxResults = Math.max(maxResults, currentSize + currentSize/10);
+ resume();
+ }
+
+ }
+
+ /**
+ * This job updates already found matches when the query is changed.
+ * Both the walker job and this job share the same scheduling rule so
+ * only one of them can be executing at the same time.
+ * <p>
+ * This is to avoid problems with concurrent modification of the
+ * matches collection.
+ */
+ private class IncrementalUpdateJob extends Job {
+ public IncrementalUpdateJob() {
+ super("Update matches");
+ this.setRule(matchesRule);
+ //This job isn't started automatically. It should be schedule every time
+ // there's a 'newQuery' set by the user/client.
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ QuickTextQuery nq = newQuery; //Copy into local variable to avoid
+ // problems if another thread changes newQuery while we
+ // are still mucking with it.
+ if (!forceRefresh && query.isSubFilter(nq)) {
+ query = nq;
+ performIncrementalUpdate(monitor);
+ } else {
+ query = nq;
+ forceRefresh = false;
+ performRestart(monitor);
+ }
+ return monitor.isCanceled()?Status.CANCEL_STATUS:Status.OK_STATUS;
+ }
+
+ private void performIncrementalUpdate(IProgressMonitor mon) {
+ Iterator<LineItem> items = matches.iterator();
+ while (items.hasNext() && !mon.isCanceled()) {
+
+ LineItem item = items.next();
+ if (query.matchItem(item)) {
+ //Match still valid but may need updating highlighted text in the UI:
+ requestor.update(item);
+ } else {
+ items.remove();
+ requestor.revoke(item);
+ }
+ }
+ if (!mon.isCanceled()) {
+ //Resume searching remaining files, if any.
+ walker.resume();
+ }
+ }
+
+ private void performRestart(IProgressMonitor mon) {
+ //walker may be null if dialog got closed already before we managed to
+ // 'performRestart'.
+ if (walker!=null) {
+ //since we are inside Job here that uses same scheduling rule as walker, we
+ //know walker is not currently executing. so walker cancel should be instantenous
+ matches.clear();
+ requestor.clear();
+ walker.cancel();
+ if (!query.isTrivial()) {
+ walker.init(); //Reinitialize the walker work queue to its starting state
+ walker.resume(); //Allow walker to resume when we release the scheduling rule.
+ }
+ }
+ }
+
+ }
+
+ private void add(LineItem line) {
+ if (matches.add(line)) {
+ requestor.add(line);
+ if (matches.size() >= maxResults) {
+ walker.suspend();
+ }
+ }
+ }
+
+ public void setQuery(QuickTextQuery newQuery, boolean force) {
+ if (newQuery.equalsFilter(query) && !force) {
+ return;
+ }
+ this.newQuery = newQuery;
+ this.forceRefresh = true;
+ scheduleIncrementalUpdate();
+ }
+
+ public void setPathMatcher(ResourceMatcher pathMatcher) {
+ if (this.pathMatcher.equals(pathMatcher)) {
+ return;
+ }
+ this.pathMatcher = pathMatcher;
+ setQuery(query, true);
+ }
+
+ public QuickTextQuery getQuery() {
+ //We return the newQuery as soon as it was set, even if it has not yet been effectively applied
+ // to previously found query results. Most logical since when you call 'setQuery' you would
+ // expect 'getQuery' to return the query you just set.
+ return newQuery!=null ? newQuery : query;
+ }
+
+ private synchronized void scheduleIncrementalUpdate() {
+ walker.suspend(); //The walker must be suspended so the update job can run, they share scheduling rule
+ // so only one job can run at any time.
+
+ //Any outstanding incremental update should be canceled since the query has changed again.
+ if (incrementalUpdate!=null) {
+ incrementalUpdate.cancel();
+ }
+ incrementalUpdate = new IncrementalUpdateJob();
+ incrementalUpdate.schedule();
+ }
+
+ public boolean isDone() {
+ //Walker can be null if job was canceled because dialog closed. But stuff like
+ //the job that shows 'Searching ...' doesn't instantly stop and may still
+ //be asking the incremental update job whether its done.
+ return walker!=null && walker.isDone();
+ }
+
+ public void requestMoreResults() {
+ if (walker!=null && !walker.isDone()) {
+ walker.requestMoreResults();
+ }
+ }
+
+ public void cancel() {
+ if (walker!=null) {
+ walker.cancel();
+ walker = null;
+ }
+ }
+
+ public IFile getCurrentFile() {
+ return currentFile;
+ }
+
+}
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/ResourceWalker.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/ResourceWalker.java
new file mode 100644
index 00000000000..e3535a01189
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/ResourceWalker.java
@@ -0,0 +1,201 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Pivotal Software, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.core;
+
+import java.util.PriorityQueue;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.text.quicksearch.internal.core.priority.DefaultPriorityFunction;
+import org.eclipse.text.quicksearch.internal.core.priority.PriorityFunction;
+import org.eclipse.text.quicksearch.internal.ui.QuickSearchActivator;
+
+/**
+ * A Helper class that allows traversing all the resources in the workspace, assigning priorities
+ * to the resources to decide the ordering and completely ignore some resources.
+ * <p>
+ * The walker can also be paused and resumed.
+ *
+ * @author Kris De Volder
+ */
+public abstract class ResourceWalker extends Job {
+
+ private class QItem implements Comparable<QItem> {
+ public final double priority;
+ public final IResource resource;
+
+ public QItem(double p, IResource r) {
+ this.priority = p;
+ this.resource = r;
+ }
+
+ public int compareTo(QItem other) {
+ return Double.compare(other.priority, this.priority);
+ }
+ }
+
+ public ResourceWalker() {
+ super("QuickSearch");
+ init();
+ }
+
+ protected void init() {
+ queue = new PriorityQueue<ResourceWalker.QItem>();
+ queue.add(new QItem(0, ResourcesPlugin.getWorkspace().getRoot()));
+ }
+
+ /**
+ * Queue of work to do. When all work is done this will be set to null. So it
+ * can also be used to determine 'done' status.
+ */
+ private PriorityQueue<QItem> queue = null;
+
+ /**
+ * Setting this to true will cause the ResourceWalker to stop walking. If the walker is running
+ * as a scheduled job, then this Job will terminate. However it is possible to 'resume' the
+ * later since pending list of workitems will be retained.
+ */
+ private boolean suspend = false;
+
+ private PriorityFunction prioritFun = new DefaultPriorityFunction();
+
+ public boolean isDone() {
+ return queue==null;
+ }
+
+ /**
+ * Request that the walker stops walking at the next reasonable opportunity.
+ */
+ public void suspend() {
+ this.suspend = true;
+ }
+
+ /**
+ * Request the walker to be restarted... i.e. begin walking the resource tree from
+ * the initial state.
+ */
+
+ /**
+ * Request that the walker be resumed. This clears the 'supsend' state if it is set
+ * and ensures that the Job is scheduled.
+ */
+ public void resume() {
+ if (isDone()) {
+ //Well... there's no work so don't bother with doing anything.
+ return;
+ }
+ this.suspend = false;
+ this.schedule();
+ }
+
+ public IStatus run(IProgressMonitor monitor) {
+ //TODO: progress reporting?
+ while (!suspend && queue!=null) {
+ if (monitor.isCanceled()) {
+ queue = null;
+ } else {
+ IResource r = getWork();
+ if (r!=null) {
+ if (r instanceof IFile) {
+ IFile f = (IFile) r;
+ visit(f, monitor);
+ } else if (r instanceof IContainer) {
+ IContainer f = (IContainer) r;
+ if (f.isAccessible()) {
+ try {
+ for (IResource child : f.members()) {
+ enqueue(child);
+ }
+ } catch (CoreException e) {
+ QuickSearchActivator.log(e);
+ }
+ }
+ }
+ } else {
+ queue = null;
+ }
+ }
+ }
+ if (monitor.isCanceled()) {
+ return Status.CANCEL_STATUS;
+ } else {
+ return Status.OK_STATUS;
+ }
+ }
+
+ /**
+ * Add a resource to the work queue taking account the priority of the resource.
+ */
+ private void enqueue(IResource child) {
+ PriorityQueue<QItem> q = queue;
+ if (q!=null) {
+ double p = priority(child);
+ if (p==PriorityFunction.PRIORITY_IGNORE) {
+ return;
+ }
+ q.add(new QItem(p, child));
+ }
+ }
+
+ protected abstract void visit(IFile r, IProgressMonitor m);
+
+ /**
+ * Assigns a priority to a given resource. This priority will affect the order in which
+ * resources get visited. Resources to be visited are tracked in a priority queue and
+ * at any time the resource with highest priority number is visited first.
+ * <p>
+ * Note that if a resource is a folder then lowering its priority implicitly reduces
+ * the priority of anything nested inside that folder because to visit the children
+ * one has to first visit the parent to reach them.
+ * <p>
+ * If the priority returned is PRIORITY_IGNORE then the resource will be ignored
+ * completely and not visited at all.
+ *
+ * @param r
+ * @return
+ */
+ final double priority(IResource r) {
+ return prioritFun.priority(r);
+ }
+
+ /**
+ * Set the priority function to use to determine walking order. For the function to
+ * take effect, it should be set before walking has started as the function is
+ * used when elements are added to the work queue during the walk.
+ * <p>
+ * Elements already in the work queue are not re-prioritized if a function is set
+ * in 'mid-run'.
+ */
+ public void setPriorityFun(PriorityFunction f) {
+ Assert.isNotNull(f, "PriorityFunction should never be null");
+ this.prioritFun = f;
+ }
+
+ private IResource getWork() {
+ PriorityQueue<QItem> q = queue;
+ if (q!=null && !q.isEmpty()) {
+ return q.remove().resource;
+ }
+ return null;
+ }
+
+
+}
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/pathmatch/ResourceMatcher.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/pathmatch/ResourceMatcher.java
new file mode 100644
index 00000000000..d45f11f21e2
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/pathmatch/ResourceMatcher.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Pivotal, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.core.pathmatch;
+
+import org.eclipse.core.resources.IResource;
+
+public interface ResourceMatcher {
+
+ boolean matches(IResource resource);
+
+}
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/pathmatch/ResourceMatchers.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/pathmatch/ResourceMatchers.java
new file mode 100644
index 00000000000..ae1479f377a
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/pathmatch/ResourceMatchers.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Pivotal, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.core.pathmatch;
+
+import org.apache.tools.ant.types.selectors.TokenizedPath;
+import org.apache.tools.ant.types.selectors.TokenizedPattern;
+import org.eclipse.core.resources.IResource;
+
+@SuppressWarnings("restriction")
+public class ResourceMatchers {
+
+ public static ResourceMatcher ANY = new ResourceMatcher() {
+ @Override
+ public String toString() {
+ return "ResourceMatcher(ANY)";
+ }
+ @Override
+ public boolean matches(IResource resource) {
+ return true;
+ }
+ };
+
+ public static ResourceMatcher commaSeparatedPaths(String text) {
+ text = text.trim();
+ if (text.isEmpty()) {
+ return ANY;
+ }
+ String[] paths = text.split(",");
+ if (paths.length==1) {
+ return path(paths[0]);
+ } else {
+ ResourceMatcher[] matchers = new ResourceMatcher[paths.length];
+ for (int i = 0; i < matchers.length; i++) {
+ matchers[i] = path(paths[i]);
+ }
+ return either(matchers);
+ }
+ }
+
+ private static ResourceMatcher either(ResourceMatcher... matchers) {
+ return new ResourceMatcher() {
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder("ResourceMatcher(");
+ for (int i = 0; i < matchers.length; i++) {
+ if (i>0) {
+ buf.append(", ");
+ }
+ buf.append(matchers[i]);
+ }
+ buf.append(")");
+ return buf.toString();
+ }
+
+ @Override
+ public boolean matches(IResource resource) {
+ for (ResourceMatcher m : matchers) {
+ if (m.matches(resource)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+ }
+
+ private static ResourceMatcher path(String _pat) {
+ if (!_pat.startsWith("/") && !_pat.startsWith("**/")) {
+ _pat = "**/"+_pat;
+ }
+ final String pat = _pat;
+ TokenizedPattern matcher = new TokenizedPattern(pat);
+ return new ResourceMatcher() {
+
+ @Override
+ public String toString() {
+ return pat;
+ }
+
+ @Override
+ public boolean matches(IResource resource) {
+ TokenizedPath path = new TokenizedPath(resource.getFullPath().toString());
+ return matcher.matchPath(path, true);
+ }
+ };
+ }
+
+}
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/preferences/QuickSearchPreferences.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/preferences/QuickSearchPreferences.java
new file mode 100644
index 00000000000..aff2ec9230a
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/preferences/QuickSearchPreferences.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Pivotal Software, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.core.preferences;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.text.quicksearch.internal.core.priority.DefaultPriorityFunction;
+import org.eclipse.text.quicksearch.internal.ui.QuickSearchActivator;
+import org.eclipse.text.quicksearch.internal.util.LineReader;
+
+/**
+ * Helper class to access the QuickSearch Preferences.
+ *
+ * @author Kris De Volder
+ */
+public class QuickSearchPreferences {
+
+ //Keys used to fetch 'raw' preferences values from the preferences store.
+ public static final String IGNORED_EXTENSIONS = "ignored.extensions";
+ public static final String IGNORED_NAMES = "ignored.names";
+ public static final String IGNORED_PREFIXES = "ignored.prefixes";
+ public static final String MAX_LINE_LEN = "LineReader.MAX_LINE_LEN";
+ private static boolean initializedDefaults;
+
+ private IPreferenceStore store;
+
+ public QuickSearchPreferences(IPreferenceStore preferenceStore) {
+ this.store = preferenceStore;
+ initializeDefaults();
+ }
+
+ public String[] getIgnoredExtensions() {
+ return getAndParseStringList(IGNORED_EXTENSIONS);
+ }
+
+ public String[] getIgnoredPrefixes() {
+ return getAndParseStringList(IGNORED_PREFIXES);
+ }
+
+ public String[] getIgnoredNames() {
+ return getAndParseStringList(IGNORED_NAMES);
+ }
+
+ public int getMaxLineLen() {
+ return store.getInt(MAX_LINE_LEN);
+ }
+
+ private String[] getAndParseStringList(String key) {
+ String raw = store.getString(key);
+ if (raw!=null) {
+ return parseStringList(raw);
+ }
+ return null;
+ }
+
+ /**
+ * Takes a raw string list as entered in the prefs page input field and parses it.
+ * <p>
+ * Commas and newline are treated as 'separators' between elements. Further, any trailing
+ * and leading whitespace is stripped from individual elements and empty strings are silently
+ * dropped.
+ */
+ private String[] parseStringList(String raw) {
+ String[] elements = raw.split("[,\n]");
+ List<String> list = new ArrayList<String>(elements.length);
+ for (String e : elements) {
+ e = e.trim();
+ if (!"".equals(e)) {
+ list.add(e);
+ }
+ }
+ return list.toArray(new String[list.size()]);
+ }
+
+ public static void initializeDefaults() {
+ if (!initializedDefaults) {
+ initializedDefaults = true;
+ IPreferenceStore store = QuickSearchActivator.getDefault().getPreferenceStore();
+ store.setDefault(QuickSearchPreferences.MAX_LINE_LEN, LineReader.DEFAULT_MAX_LINE_LENGTH);
+
+ DefaultPriorityFunction dpf = new DefaultPriorityFunction();
+ store.setDefault(QuickSearchPreferences.IGNORED_EXTENSIONS, encode(dpf.ignoredExtensions));
+ store.setDefault(QuickSearchPreferences.IGNORED_NAMES, encode(dpf.ignoredNames));
+ store.setDefault(QuickSearchPreferences.IGNORED_PREFIXES, encode(dpf.ignoredPrefixes));
+ }
+ }
+
+ private static String encode(String[] strings) {
+ StringBuilder encoded = new StringBuilder();
+ for (int i = 0; i < strings.length; i++) {
+ if (i>0) {
+ encoded.append(", ");
+ }
+ encoded.append(strings[i]);
+ }
+ return encoded.toString();
+ }
+
+
+}
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/priority/DefaultPriorityFunction.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/priority/DefaultPriorityFunction.java
new file mode 100644
index 00000000000..4d6c232646e
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/priority/DefaultPriorityFunction.java
@@ -0,0 +1,154 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Pivotal Software, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.core.priority;
+
+import java.net.URI;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.text.quicksearch.internal.core.preferences.QuickSearchPreferences;
+
+/**
+ * Default implementation of PriorityFunction. It doesn't de-emphasize anything but
+ * if has some list of extensions and names that are used to ignore some types of
+ * files and directories.
+ * <p>
+ * This class can be used as is and customised by changing the lists, or it can be
+ * subclassed and inherit some of the default behaviour by calling super.priority()
+ */
+public class DefaultPriorityFunction extends PriorityFunction {
+
+ /**
+ * If true, any resources marked as 'derived' in the Eclipse workspace will
+ * be ignored.
+ */
+ public boolean ignoreDerived = true;
+
+ /**
+ * The default priority function causes any resources that end with these strings to
+ * be ignored.
+ */
+ public String[] ignoredExtensions = {
+ ".class", ".gif", ".exe", ".png", ".jpg", ".zip", ".jar", ".svg", ".psd", "~",
+ ".CLASS", ".GIF", ".EXE", ".PNG", ".JPG", ".ZIP", ".JAR", ".SVG", ".PSD",
+ ".pdf", ".p12", ".odt", ".odp", ".doc", ".pptx", ".ppt", ".bin", ".docx", ".xls", ".xlsx",
+ ".PDF", ".P12", ".ODT", ".ODP", ".DOC", ".PPTX", ".PPT", ".BIN", ".DOCX", ".XLS", ".XLSX"
+ };
+
+ /**
+ * The default priority function causes any resource who's name (i.e last path segment)
+ * starts with any of these Strings to be ignored.
+ */
+ public String[] ignoredPrefixes = {
+ "."
+ };
+
+ /**
+ * The default priority function causes any resources who's name equals any of these
+ * Strings to be ignored.
+ */
+ public String[] ignoredNames = {
+ "bin", "target", "build"
+ };
+
+ public Set<IResource> ignoredResources = null;
+
+ @Override
+ public double priority(IResource r) {
+ if (r!=null && r.isAccessible()) {
+ if (ignoreDerived && r.isDerived()) {
+ return PRIORITY_IGNORE;
+ }
+ if (ignoredResources!=null && ignoredResources.contains(r)) {
+ return PRIORITY_IGNORE;
+ }
+ String name = r.getName();
+ for (String ext : ignoredExtensions) {
+ if (name.endsWith(ext)) {
+ return PRIORITY_IGNORE;
+ }
+ }
+ for (String pre : ignoredPrefixes) {
+ if (name.startsWith(pre)) {
+ return PRIORITY_IGNORE;
+ }
+ }
+ for (String n : ignoredNames) {
+ if (name.equals(n)) {
+ return PRIORITY_IGNORE;
+ }
+ }
+ return PRIORITY_DEFAULT;
+ }
+ return PRIORITY_IGNORE;
+ }
+
+ /**
+ * Initialise some configurable settings from an instance of QuickSearchPreferences
+ */
+ public void configure(QuickSearchPreferences preferences) {
+ String[] pref = preferences.getIgnoredExtensions();
+ if (pref!=null) {
+ this.ignoredExtensions = pref;
+ }
+ pref = preferences.getIgnoredNames();
+ if (pref!=null) {
+ this.ignoredNames = pref;
+ }
+ pref = preferences.getIgnoredPrefixes();
+ if (pref!=null) {
+ this.ignoredPrefixes = pref;
+ }
+ computeIgnoredFolders();
+ }
+
+ /**
+ * We want to avoid searchin the same files / folders twice in cases where users have 'overlapping projects'.
+ * I.e a project contains folders that are actually correspond to other projects also imported in the workspace.
+ * <p>
+ * See https://issuetracker.springsource.com/browse/STS-3783
+ * <p>
+ * This method computes a set of folders to ignore.
+ */
+ private void computeIgnoredFolders() {
+ //TODO: Hopefully this won't take too long to compute. Otherwise we may need to look at ways of caching it.
+ // it probably doesn't change that often.
+ IProject[] allprojects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
+ for (IProject p : allprojects) {
+ if (p.isAccessible()) {
+ URI location = p.getLocationURI();
+ if (location!=null) {
+ IContainer[] containers = ResourcesPlugin.getWorkspace().getRoot().findContainersForLocationURI(location);
+ if (containers!=null) {
+ for (IContainer folder : containers) {
+ if (!folder.equals(p)) {
+ ignore(folder);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void ignore(IContainer folder) {
+ if (ignoredResources==null) {
+ ignoredResources = new HashSet<IResource>();
+ }
+ ignoredResources.add(folder);
+ }
+}
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/priority/PrioriTree.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/priority/PrioriTree.java
new file mode 100644
index 00000000000..812cb795a1d
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/priority/PrioriTree.java
@@ -0,0 +1,230 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Pivotal Software, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.core.priority;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * A PrioriTree is an implementation of PriorityFunction that is based on assigning specific priorities
+ * to a finite set of paths. The paths are kept in a tree-like structure internally so that
+ * assigning a priority to a given path also implicitly forces all the children leading to that
+ * path to have a priority that is at least as high as that of the path itself.
+ *
+ * TODO: The priority function produced by this tree would probably be better if it also raised/affected
+ * the priority of nodes in a subtree of a node, but not by as much as the node itself.
+ *
+ * @author Kris De Volder
+ */
+public class PrioriTree extends DefaultPriorityFunction {
+
+ private static final boolean DEBUG = false; //(""+Platform.getLocation()).contains("kdvolder");
+
+ /**
+ * Creates an empty PrioriTree. This tree assigns provided default priority to any path.
+ */
+ public static PrioriTree create() {
+ return new PrioriTree(0, PRIORITY_DEFAULT);
+ }
+
+ private PrioriTree(int level, double defaultPriority) {
+ this.level = level;
+ this.priority = defaultPriority;
+ this.childPriority = defaultPriority;
+ }
+
+ private static void debug(String string) {
+ if (DEBUG) {
+ System.out.println(string);
+ }
+ }
+
+ /**
+ * Level of this node in the tree. This corresponds to the length of path ending in this node. I.e. 0 for the root node,
+ * 1 for children of root, 2 for children of level 1 nodes etc.
+ * <p>
+ * Level is used to easily check whether a node lookup returned an exact matching node or an ancestor.
+ */
+ private int level;
+
+ /**
+ * Priority assigned to any path lookup that ends here.
+ */
+ private double priority = PRIORITY_IGNORE;
+ //Must start out as low as possible because putting stuff into the tree will raise the priorities monotonically.
+ //Note that normally we will not create a PrioriTree node unless there is some reason to
+ // assign a priority. So this value is expected to be always overwritten shortly after
+ // a node is created.
+
+ /**
+ * Default priority assigned to all children of this node
+ */
+ private double childPriority = PRIORITY_IGNORE;
+
+ /**
+ * Children indexed by first segment in the path. This is only initialised if there's at least one
+ * child.
+ */
+ private Map<String, PrioriTree> children = null;
+
+ /**
+ * Set the priority for a given path. Also forces an update of all 'ancestor' nodes in the
+ * tree leading to this path to ensure that a parent node always has a priority at least as high as any
+ * of its children.
+ * <p>
+ * Also assigns the same priority to any descendants of the target path (but not the descendants of
+ * implicitly set parent nodes.
+ * <p>
+ * THe picture below illustrates: # marks a target node * marks nodes that implicitly
+ * also get set. And '-' marks nodes that are unaffected.
+ * <p>
+ * <pre>
+ * *
+ * / \
+ * - *
+ * / \
+ * - #
+ * / \
+ * * *
+ * /\ /\
+ * * * * *
+ * </pre>
+ * <p>
+ * Note: this operation never reduces the priority of any path already in the tree.
+ * Thus if the same path gets assigned a priority more than once, only the highest priority will
+ * be retained in the tree node for that path.
+ */
+ public void setPriority(IPath path, double priority) {
+ this.priority = Math.max(this.priority, priority); //Use Math.max, never reduce priorities!
+ if (path.segmentCount()>0) {
+ // path leads to a child node
+ PrioriTree child = ensureChild(path.segment(0));
+ child.setPriority(path.removeFirstSegments(1), priority);
+ } else {
+ // path ends here
+ setChildPriority(priority);
+ }
+ }
+
+ private void setChildPriority(double priority) {
+ double newChildPriority = Math.max(priority, childPriority);
+ if (newChildPriority!=childPriority) {
+ //Must update default child priority as well check if already created children priorities need
+ // to be raised.
+ this.childPriority = newChildPriority;
+ if (children!=null) {
+ for (PrioriTree child : children.values()) {
+ //TODO: remove children that became redundant because the increase of childPriority in parent
+ // makes them have no observable effect.
+ //Currently this cleanup is not happening. This is inefficient but not incorrect.
+ child.priority = Math.max(child.priority, newChildPriority);
+ child.setChildPriority(newChildPriority);
+ }
+ }
+ }
+ }
+
+ /**
+ * Ensure that this node has a child for a given segment string. If no node exists yet, create it.
+ * @param segment
+ * @return {@link PrioriTree} the existing or newly created child, never null.
+ */
+ private PrioriTree ensureChild(String segment) {
+ if (children==null) {
+ children = new HashMap<String, PrioriTree>();
+ }
+ PrioriTree child = children.get(segment);
+ if (child==null) {
+ child = new PrioriTree(level+1, childPriority);
+ children.put(segment, child);
+ }
+ return child;
+ }
+
+ @Override
+ public double priority(IResource r) {
+ double result = super.priority(r);
+ if (result==PRIORITY_IGNORE) {
+ //Ignored paths shouldn't be changed ... ever.
+ return PRIORITY_IGNORE;
+ }
+ IPath path = r.getFullPath();
+ PrioriTree node = this.lookup(path);
+ if (node.level == path.segmentCount()) {
+ //exact node found
+ result = node.priority;
+ } else {
+ //ancestor node found
+ result = node.childPriority;
+ }
+ debug("Priority for "+r.getFullPath() + " = " + result);
+ return result;
+ }
+
+
+ /**
+ * Locate tree node corresponding to a given path.
+ * @param fullPath
+ * @return The node or null if no corresponding node exists in the tree.
+ */
+ private PrioriTree lookup(IPath path) {
+ PrioriTree found = null;
+ if (path.segmentCount()>0) {
+ PrioriTree child = getChild(path.segment(0));
+ if (child!=null) {
+ found = child.lookup(path.removeFirstSegments(1));
+ }
+ }
+ return found==null?this:found;
+ }
+
+ /**
+ * Fetch the child for the corresponding segment String.
+ * @param segment
+ * @return The child or null if there is no such child.
+ */
+ private PrioriTree getChild(String segment) {
+ if (children!=null) {
+ return children.get(segment);
+ }
+ return null;
+ }
+
+ /**
+ * For debugging purposes. Dumps tree data onto System.out
+ */
+ public void dump() {
+ dump("/", 0);
+ }
+
+ private void dump(String name, int indent) {
+ indent(indent);
+ System.out.println(name + " : " +priority);
+ if (children!=null) {
+ for (Entry<String, PrioriTree> c : children.entrySet()) {
+ c.getValue().dump(c.getKey(), indent+1);
+ }
+ }
+ }
+
+ private void indent(int i) {
+ for (int j = 0; j < i; j++) {
+ System.out.print(" ");
+ }
+ }
+
+}
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/priority/PriorityFunction.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/priority/PriorityFunction.java
new file mode 100644
index 00000000000..8dc5b9001aa
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/core/priority/PriorityFunction.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Pivotal Software, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.core.priority;
+
+import org.eclipse.core.resources.IResource;
+
+/**
+ * An instance implementing this interface can optionally be provided to influence
+ * the searching order. If it is not provided then the default will be used.
+ */
+public abstract class PriorityFunction {
+
+ /**
+ * The highest priority. Any elements in the queue with this priority will be visited before
+ * any others in the queue. Be warned that assigning this priority to a deeply nested
+ * element in the tree alone doesn't guarantee it will be visited early on because in
+ * order to reach the element the parents have to be visited first. If the parent
+ * has a low priority...
+ */
+ public static final double PRIORITY_HIGHEST = Double.POSITIVE_INFINITY;
+
+ /**
+ * Priority indicating something that is moderately more interesting than the default.
+ * So it should be processed before default stuff but not before "VISIT_FIRST" priority.
+ */
+ public static final double PRIORITY_INTERESTING = 100;
+
+ /**
+ * A default priority value. Meant to be used for elements that are neither particularly
+ * interesting or particularly non-interesting. Use larger numbers to emphasize elements
+ * and lower numbers to de-emphasise them. Note that in order to emphasise an element
+ * globally it also necessary to raise the priority of their parents because children
+ * can't be reached without passing through their parent.
+ */
+ public static final double PRIORITY_DEFAULT = 0;
+
+
+ /**
+ * A special priority that causes elements (and their children) to be completely ignored.
+ */
+ public static final double PRIORITY_IGNORE = Double.NEGATIVE_INFINITY;
+
+
+
+
+ public abstract double priority(IResource r);
+}
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/Messages.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/Messages.java
new file mode 100644
index 00000000000..6eeb76f30f4
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/Messages.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Pivotal Software, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.ui;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.text.quicksearch.internal.ui.messages"; //$NON-NLS-1$
+ public static String QuickSearchPreferencesPage_0;
+ public static String QuickSearchPreferencesPage_1;
+ public static String QuickSearchPreferencesPage_2;
+ public static String QuickSearchPreferencesPage_3;
+ public static String QuickSearchPreferencesPage_4;
+ public static String QuickSearchPreferencesPage_5;
+ public static String QuickSearchPreferencesPage_6;
+ public static String QuickSearchPreferencesPage_7;
+ public static String QuickSearchPreferencesPage_8;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchAction.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchAction.java
new file mode 100644
index 00000000000..48884c1e463
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchAction.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2013-2019 Pivotal Software, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.ui;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+
+public class QuickSearchAction implements IWorkbenchWindowActionDelegate {
+
+ private IWorkbenchWindow window;
+
+ public void run(IAction action) {
+ QuickSearchHandler.doQuickSearch(window);
+ }
+
+ public void selectionChanged(IAction action, ISelection selection) {
+ }
+
+ public void dispose() {
+ }
+
+ public void init(IWorkbenchWindow window) {
+ this.window = window;
+ }
+
+}
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchActivator.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchActivator.java
new file mode 100644
index 00000000000..58ff698a197
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchActivator.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2013-2019 Pivotal Software, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.ui;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.text.quicksearch.internal.core.preferences.QuickSearchPreferences;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class QuickSearchActivator extends AbstractUIPlugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "org.eclipse.text.quicksearch"; //$NON-NLS-1$
+
+ // The shared instance
+ private static QuickSearchActivator plugin;
+
+ private QuickSearchPreferences prefs = null; //Lazy initialized
+
+ /**
+ * The constructor
+ */
+ public QuickSearchActivator() {
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static QuickSearchActivator getDefault() {
+ return plugin;
+ }
+
+ /**
+ * Returns an image descriptor for the image file at the given
+ * plug-in relative path
+ *
+ * @param path the path
+ * @return the image descriptor
+ */
+ public static ImageDescriptor getImageDescriptor(String path) {
+ return imageDescriptorFromPlugin(PLUGIN_ID, path);
+ }
+
+ public static void log(Throwable exception) {
+ log(createErrorStatus(exception));
+ }
+
+ public static void log(IStatus status) {
+// if (logger == null) {
+ getDefault().getLog().log(status);
+// }
+// else {
+// logger.logEntry(status);
+// }
+ }
+
+ public static IStatus createErrorStatus(Throwable exception) {
+ return new Status(IStatus.ERROR, PLUGIN_ID, 0, exception.getMessage(), exception);
+ }
+
+ public QuickSearchPreferences getPreferences() {
+ if (prefs==null) {
+ prefs = new QuickSearchPreferences(QuickSearchActivator.getDefault().getPreferenceStore());
+ }
+ return prefs;
+ }
+
+}
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchContext.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchContext.java
new file mode 100644
index 00000000000..197342eeeb7
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchContext.java
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Pivotal Software, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.ui;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.text.quicksearch.internal.core.priority.DefaultPriorityFunction;
+import org.eclipse.text.quicksearch.internal.core.priority.PrioriTree;
+import org.eclipse.text.quicksearch.internal.core.priority.PriorityFunction;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorReference;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+
+/**
+ * An instance of this class groups together some logic to inform the
+ * quicksearch dialog about 'search context'. I.e. things such as the
+ * current selection and open editors when the dialog is opened.
+ * <p>
+ * This contextual information is used to inform the PriorityFunction
+ * for the search process.
+ *
+ * @author Kris De Volder
+ */
+public class QuickSearchContext {
+
+ /**
+ * We remember the last result of getOpenFiles in here. This is so that we can return this
+ * if we are having trouble to compute the open files. Sometimes we may not be able to
+ * access the active workbench page etc. In this case it is probably better to return
+ * a stale list of files than nothing at all.
+ */
+ private static Collection<IFile> lastOpenFiles = Arrays.asList(); //Empty list to start with.
+
+ private IWorkbenchWindow window;
+
+ public QuickSearchContext(IWorkbenchWindow window) {
+ this.window = window;
+ }
+
+ /**
+ * Create a walker priority function based on the current 'context'.
+ */
+ public PriorityFunction createPriorityFun() {
+ PrioriTree priorities = PrioriTree.create();
+ priorities.configure(QuickSearchActivator.getDefault().getPreferences());
+ try {
+// TODO: This is not working correctly right now, if the selected resources are containers / folders.
+// The PrioriTree only assigns a priority to the folder itself, but not to its children.
+// So open editors will automatically take priority over the children of selected projects.
+// To fix, PrioriTree will need a mechanism to assign priorities to children.
+// If doing so, care must be taken not to accidentally assign priorities to ignored
+// resources.
+
+ Collection<IResource> selectedResources = getSelectedResources();
+ for (IResource r : selectedResources) {
+ priorities.setPriority(r.getFullPath(), 3*PriorityFunction.PRIORITY_INTERESTING);
+ }
+
+ IFile currentFile = getActiveFile();
+ if (currentFile!=null) {
+ //Current file is more interesting than other open files
+ priorities.setPriority(currentFile.getFullPath(), 2*PriorityFunction.PRIORITY_INTERESTING);
+ }
+ Collection<IFile> openFiles = getOpenFiles();
+ for (IFile file : openFiles) {
+ priorities.setPriority(file.getFullPath(), PriorityFunction.PRIORITY_INTERESTING);
+ }
+ return priorities;
+ } catch (Throwable e) {
+ QuickSearchActivator.log(e);
+ }
+ return new DefaultPriorityFunction();
+ }
+
+ private Collection<IFile> getOpenFiles() {
+ try {
+ IWorkbenchPage page = window.getActivePage();
+ if (page!=null) {
+ Collection<IFile> files = new ArrayList<IFile>();
+ IEditorReference[] editors = page.getEditorReferences();
+ if (editors!=null) {
+ for (IEditorReference editor : editors) {
+ try {
+ IEditorInput input = editor.getEditorInput();
+ if (input!=null) {
+ IFile file = (IFile) input.getAdapter(IFile.class);
+ if (file != null) {
+ files.add(file);
+ }
+ }
+ } catch (PartInitException e) {
+ //Ignore silently. See: https://issuetracker.springsource.com/browse/STS-4156
+ //Rationale: Whatever may be the reason we can't obtain a 'input' for the editor.
+ //It likely means there's no text to search in that editor, so it is safe to ignore
+ //without loss of functionality to the quicksearch engine.
+
+ //QuickSearchActivator.log(e);
+ }
+ }
+ lastOpenFiles = files;
+ return files;
+ }
+ }
+ return lastOpenFiles;
+ } finally {
+ }
+ }
+
+ /**
+ * Gets the IFile that is currently open in the active editor.
+ * @return IFile or null if there is no current editor or the editor isn't associated to a file.
+ */
+ private IFile getActiveFile() {
+ IWorkbenchPage page = window.getActivePage();
+ if (page!=null) {
+ IEditorPart editor = page.getActiveEditor();
+ if (editor!=null) {
+ IEditorInput input = editor.getEditorInput();
+ if (input!=null) {
+ return (IFile) input.getAdapter(IFile.class);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get a Collection of selected resources from the active selection if that selection is
+ * a Structured selection (e.g. in navigator or project/package explorer)
+ */
+ private Collection<IResource> getSelectedResources() {
+ ISelection _s = window.getSelectionService().getSelection();
+ if (_s!=null && _s instanceof IStructuredSelection) {
+ IStructuredSelection s = (IStructuredSelection) _s;
+ if (s!=null && !s.isEmpty()) {
+ Object[] elements = s.toArray();
+ List<IResource> resources = new ArrayList<IResource>(elements.length);
+ for (Object e : elements) {
+ if (e instanceof IResource) {
+ resources.add((IResource) e);
+ } else if (e instanceof IAdaptable) {
+ IAdaptable ae = (IAdaptable) e;
+ IResource r = (IResource) ae.getAdapter(IResource.class);
+ if (r!=null) {
+ resources.add(r);
+ }
+ }
+ }
+ return resources;
+ }
+ }
+ return Collections.emptyList();
+ }
+
+}
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchDialog.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchDialog.java
new file mode 100644
index 00000000000..64a93292041
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchDialog.java
@@ -0,0 +1,1469 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011, 2013 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Willian Mitsuda <wmitsuda@gmail.com>
+ * - Fix for bug 196553 - [Dialogs] Support IColorProvider/IFontProvider in FilteredItemsSelectionDialog
+ * Peter Friese <peter.friese@gentleware.com>
+ * - Fix for bug 208602 - [Dialogs] Open Type dialog needs accessible labels
+ * Simon Muschel <smuschel@gmx.de> - bug 258493
+ * Kris De Volder Copied and modified from org.eclipse.ui.dialogs.FilteredResourcesSelectionDialog
+ * to create QuickSearchDialog
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.ui;
+
+import static org.eclipse.jface.resource.JFaceResources.TEXT_FONT;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.LegacyActionTools;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ILazyContentProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.StyledCellLabelProvider;
+import org.eclipse.jface.viewers.StyledString;
+import org.eclipse.jface.viewers.StyledString.Styler;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.search.internal.ui.text.EditorOpener;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.accessibility.ACC;
+import org.eclipse.swt.accessibility.AccessibleAdapter;
+import org.eclipse.swt.accessibility.AccessibleEvent;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+import org.eclipse.text.quicksearch.internal.core.LineItem;
+import org.eclipse.text.quicksearch.internal.core.QuickTextQuery;
+import org.eclipse.text.quicksearch.internal.core.QuickTextQuery.TextRange;
+import org.eclipse.text.quicksearch.internal.core.QuickTextSearchRequestor;
+import org.eclipse.text.quicksearch.internal.core.QuickTextSearcher;
+import org.eclipse.text.quicksearch.internal.core.pathmatch.ResourceMatchers;
+import org.eclipse.text.quicksearch.internal.util.DocumentFetcher;
+import org.eclipse.ui.ActiveShellExpression;
+import org.eclipse.ui.IWorkbenchCommandConstants;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.SelectionStatusDialog;
+import org.eclipse.ui.handlers.IHandlerActivation;
+import org.eclipse.ui.handlers.IHandlerService;
+import org.eclipse.ui.internal.IWorkbenchGraphicConstants;
+import org.eclipse.ui.internal.WorkbenchImages;
+import org.eclipse.ui.internal.WorkbenchMessages;
+import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
+import org.eclipse.ui.progress.UIJob;
+
+/**
+ * Shows a list of items to the user with a text entry field for a string
+ * pattern used to filter the list of items.
+ *
+ * @since 3.3
+ */
+@SuppressWarnings({ "rawtypes", "restriction", "unchecked" })
+public class QuickSearchDialog extends SelectionStatusDialog {
+
+ private static final int GO_BUTTON_ID = IDialogConstants.CLIENT_ID + 1;
+ private static final int REFRESH_BUTTON_ID = IDialogConstants.CLIENT_ID + 2;
+
+ public static final Styler HIGHLIGHT_STYLE = org.eclipse.search.internal.ui.text.DecoratingFileSearchLabelProvider.HIGHLIGHT_STYLE;
+
+ private UIJob refreshJob = new UIJob("Refresh") {
+ @Override
+ public IStatus runInUIThread(IProgressMonitor monitor) {
+ refreshWidgets();
+ return Status.OK_STATUS;
+ }
+ };
+
+ protected void openSelection() {
+ try {
+ LineItem item = (LineItem) this.getFirstResult();
+ if (item!=null) {
+ QuickTextQuery q = this.getQuery();
+ TextRange range = q.findFirst(item.getText());
+ EditorOpener opener = new EditorOpener();
+ IWorkbenchPage page = window.getActivePage();
+ if (page!=null) {
+ opener.openAndSelect(page, item.getFile(), range.getOffset()+item.getOffset(),
+ range.getLength(), true);
+ }
+ }
+ } catch (PartInitException e) {
+ QuickSearchActivator.log(e);
+ }
+ }
+
+ /**
+ * Job that shows a simple busy indicator while a search is active.
+ * The job must be scheduled when a search starts/resumes.
+ */
+ private UIJob progressJob = new UIJob("Refresh") {
+ int animate = 0; // number of dots to display.
+
+ protected String dots(int animate) {
+ char[] chars = new char[animate];
+ for (int i = 0; i < chars.length; i++) {
+ chars[i] = '.';
+ }
+ return new String(chars);
+ }
+
+ protected String currentFileInfo(IFile currentFile, int animate) {
+ if (currentFile!=null) {
+ String path = currentFile.getFullPath().toString();
+ if (path.length()<=30) {
+ return path;
+ }
+ return "..."+path.substring(path.length()-30);
+ }
+ return dots(animate);
+ }
+
+ @Override
+ public IStatus runInUIThread(IProgressMonitor mon) {
+ if (!mon.isCanceled() && progressLabel!=null && !progressLabel.isDisposed()) {
+ if (searcher==null || searcher.isDone()) {
+ progressLabel.setText("");
+ } else {
+ progressLabel.setText("Searching"+currentFileInfo(searcher.getCurrentFile(), animate));
+ animate = (animate+1)%4;
+ this.schedule(333);
+ }
+ }
+ return Status.OK_STATUS;
+ }
+ };
+
+ public final StyledCellLabelProvider LINE_NUMBER_LABEL_PROVIDER = new StyledCellLabelProvider() {
+ @Override
+ public void update(ViewerCell cell) {
+ LineItem item = (LineItem) cell.getElement();
+ if (item!=null) {
+ cell.setText(""+item.getLineNumber());
+ } else {
+ cell.setText("?");
+ }
+ cell.setImage(getBlankImage());
+ };
+ };
+
+ private static final Color GREY = Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GRAY);
+
+ private final StyledCellLabelProvider LINE_TEXT_LABEL_PROVIDER = new StyledCellLabelProvider() {
+
+ @Override
+ public void update(ViewerCell cell) {
+ LineItem item = (LineItem) cell.getElement();
+ if (item!=null) {
+ StyledString text = highlightMatches(item.getText());
+ cell.setText(text.getString());
+ cell.setStyleRanges(text.getStyleRanges());
+ } else {
+ cell.setText("");
+ cell.setStyleRanges(null);
+ }
+ cell.setImage(getBlankImage());
+ super.update(cell);
+ }
+ };
+
+ private Image blankImage;
+
+ private Image getBlankImage() {
+ if (blankImage==null) {
+ blankImage = new Image(Display.getDefault(), 1, 1);
+// GC gc = new GC(blankImage);
+// gc.fillRectangle(0, 0, 16, 16);
+// gc.dispose();
+ }
+ return blankImage;
+ }
+
+ private final StyledCellLabelProvider LINE_FILE_LABEL_PROVIDER = new StyledCellLabelProvider() {
+
+ @Override
+ public void update(ViewerCell cell) {
+ LineItem item = (LineItem) cell.getElement();
+ if (item!=null) {
+ IPath path = item.getFile().getFullPath();
+ String name = path.lastSegment();
+ String dir = path.removeLastSegments(1).toString();
+ cell.setText(name + " - "+dir);
+ StyleRange[] styleRanges = new StyleRange[] {
+ new StyleRange(name.length(), dir.length()+3, GREY, null)
+ };
+ cell.setStyleRanges(styleRanges);
+ } else {
+ cell.setText("");
+ cell.setStyleRanges(null);
+ }
+ cell.setImage(getBlankImage());
+ super.update(cell);
+ }
+
+// public String getToolTipText(Object element) {
+// LineItem item = (LineItem) element;
+// if (item!=null) {
+// return ""+item.getFile().getFullPath();
+// }
+// return "";
+// };
+
+// public String getText(Object _item) {
+// if (_item!=null) {
+// LineItem item = (LineItem) _item;
+// return item.getFile().getName().toString();
+// }
+// return "?";
+// };
+ };
+
+ private static final String DIALOG_SETTINGS = QuickSearchDialog.class.getName()+".DIALOG_SETTINGS";
+
+ private static final String DIALOG_BOUNDS_SETTINGS = "DialogBoundsSettings"; //$NON-NLS-1$
+
+ private static final String DIALOG_HEIGHT = "DIALOG_HEIGHT"; //$NON-NLS-1$
+ private static final String DIALOG_WIDTH = "DIALOG_WIDTH"; //$NON-NLS-1$
+ private static final String DIALOG_COLUMNS = "COLUMN_WIDTHS";
+ private static final String DIALOG_SASH_WEIGHTS = "SASH_WEIGHTS";
+
+ private static final String DIALOG_LAST_QUERY = "LAST_QUERY";
+ private static final String DIALOG_PATH_FILTER = "PATH_FILTER";
+ private static final String CASE_SENSITIVE = "CASE_SENSITIVE";
+ private static final boolean CASE_SENSITIVE_DEFAULT = true;
+
+ private static final String KEEP_OPEN = "KEEP_OPEN";
+ private static final boolean KEEP_OPEN_DEFAULT = false;
+
+ /**
+ * Represents an empty selection in the pattern input field (used only for
+ * initial pattern).
+ */
+ public static final int NONE = 0;
+
+ /**
+ * Pattern input field selection where caret is at the beginning (used only
+ * for initial pattern).
+ */
+ public static final int CARET_BEGINNING = 1;
+
+ /**
+ * Represents a full selection in the pattern input field (used only for
+ * initial pattern).
+ */
+ public static final int FULL_SELECTION = 2;
+
+ private Text pattern;
+
+ private TableViewer list;
+
+ private MenuManager menuManager;
+
+ private MenuManager contextMenuManager;
+
+ private boolean multi;
+
+ private ToolBar toolBar;
+
+ private ToolItem toolItem;
+
+ private Label progressLabel;
+
+ private ContentProvider contentProvider;
+
+ private String initialPatternText;
+
+ private int selectionMode;
+
+ private static final String EMPTY_STRING = ""; //$NON-NLS-1$
+
+ private final int MAX_LINE_LEN;
+
+ private IHandlerActivation showViewHandler;
+
+ private QuickTextSearcher searcher;
+
+ private StyledText details;
+
+ private DocumentFetcher documents;
+
+
+ private ToggleCaseSensitiveAction toggleCaseSensitiveAction;
+ private ToggleKeepOpenAction toggleKeepOpenAction;
+
+
+ private QuickSearchContext context;
+
+
+ private SashForm sashForm;
+
+ private Label headerLabel;
+
+ private IWorkbenchWindow window;
+ private Text searchIn;
+
+ /**
+ * Creates a new instance of the class.
+ *
+ * @param window.getShell()
+ * shell to parent the dialog on
+ * @param multi
+ * indicates whether dialog allows to select more than one
+ * position in its list of items
+ */
+ public QuickSearchDialog(IWorkbenchWindow window) {
+ super(window.getShell());
+ this.window = window;
+ setShellStyle(SWT.CLOSE | SWT.MODELESS | SWT.BORDER | SWT.TITLE | SWT.RESIZE);
+ setBlockOnOpen(false);
+ this.setTitle("Quick Text Search");
+ this.context = new QuickSearchContext(window);
+ this.multi = false;
+ contentProvider = new ContentProvider();
+ selectionMode = NONE;
+ MAX_LINE_LEN = QuickSearchActivator.getDefault().getPreferences().getMaxLineLen();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.window.Window#create()
+ */
+ public void create() {
+ super.create();
+ pattern.setFocus();
+ }
+
+ /**
+ * Restores dialog using persisted settings.
+ */
+ protected void restoreDialog(IDialogSettings settings) {
+ try {
+ if (initialPatternText==null) {
+ String lastSearch = settings.get(DIALOG_LAST_QUERY);
+ if (lastSearch==null) {
+ lastSearch = "";
+ }
+ pattern.setText(lastSearch);
+ pattern.setSelection(0, lastSearch.length());
+ }
+ if (settings.get(DIALOG_PATH_FILTER)!=null) {
+ String filter = settings.get(DIALOG_PATH_FILTER);
+ searchIn.setText(filter);
+ }
+
+ if (settings.getArray(DIALOG_COLUMNS)!=null) {
+ String[] columnWidths = settings.getArray(DIALOG_COLUMNS);
+ Table table = list.getTable();
+ int cols = table.getColumnCount();
+ for (int i = 0; i < cols; i++) {
+ TableColumn col = table.getColumn(i);
+ try {
+ if (col!=null) {
+ col.setWidth(Integer.valueOf(columnWidths[i]));
+ }
+ } catch (Throwable e) {
+ QuickSearchActivator.log(e);
+ }
+ }
+ }
+
+ if (settings.getArray(DIALOG_SASH_WEIGHTS)!=null) {
+ String[] _weights = settings.getArray(DIALOG_SASH_WEIGHTS);
+ int[] weights = new int[_weights.length];
+ for (int i = 0; i < weights.length; i++) {
+ weights[i] = Integer.valueOf(_weights[i]);
+ }
+ sashForm.setWeights(weights);
+ }
+ } catch (Throwable e) {
+ //None of this stuff is critical so shouldn't stop opening dialog if it fails!
+ QuickSearchActivator.log(e);
+ }
+ }
+
+ private class ToggleKeepOpenAction extends Action {
+ public ToggleKeepOpenAction(IDialogSettings settings) {
+ super(
+ "Keep Open",
+ IAction.AS_CHECK_BOX
+ );
+ if (settings.get(KEEP_OPEN)==null) {
+ setChecked(KEEP_OPEN_DEFAULT);
+ } else{
+ setChecked(settings.getBoolean(KEEP_OPEN));
+ }
+ }
+
+ public void run() {
+ //setChecked(!isChecked());
+ }
+
+ }
+
+
+ private class ToggleCaseSensitiveAction extends Action {
+
+ public ToggleCaseSensitiveAction(IDialogSettings settings) {
+ super(
+ "Case Sensitive",
+ IAction.AS_CHECK_BOX
+ );
+ if (settings.get(CASE_SENSITIVE)==null) {
+ setChecked(CASE_SENSITIVE_DEFAULT);
+ } else{
+ setChecked(settings.getBoolean(CASE_SENSITIVE));
+ }
+ }
+
+ public void run() {
+ //setChecked(!isChecked());
+ refreshHeaderLabel();
+ applyFilter(false);
+ }
+ }
+
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.window.Window#close()
+ */
+ public boolean close() {
+ this.progressJob.cancel();
+ this.progressJob = null;
+// this.refreshProgressMessageJob.cancel();
+ if (showViewHandler != null) {
+ IHandlerService service = (IHandlerService) PlatformUI
+ .getWorkbench().getService(IHandlerService.class);
+ service.deactivateHandler(showViewHandler);
+ showViewHandler.getHandler().dispose();
+ showViewHandler = null;
+ }
+ if (menuManager != null)
+ menuManager.dispose();
+ if (contextMenuManager != null)
+ contextMenuManager.dispose();
+ storeDialog(getDialogSettings());
+ if (searcher!=null) {
+ searcher.cancel();
+ }
+ return super.close();
+ }
+
+ /**
+ * Stores dialog settings.
+ *
+ * @param settings
+ * settings used to store dialog
+ */
+ protected void storeDialog(IDialogSettings settings) {
+ String currentSearch = pattern.getText();
+ settings.put(DIALOG_LAST_QUERY, currentSearch);
+ settings.put(DIALOG_PATH_FILTER, searchIn.getText());
+ if (toggleCaseSensitiveAction!=null) {
+ settings.put(CASE_SENSITIVE, toggleCaseSensitiveAction.isChecked());
+ }
+ if (toggleKeepOpenAction!=null) {
+ settings.put(KEEP_OPEN, toggleKeepOpenAction.isChecked());
+ }
+ Table table = list.getTable();
+ if (table.getColumnCount()>0) {
+ String[] columnWidths = new String[table.getColumnCount()];
+ for (int i = 0; i < columnWidths.length; i++) {
+ columnWidths[i] = ""+table.getColumn(i).getWidth();
+ }
+ settings.put(DIALOG_COLUMNS, columnWidths);
+ }
+ if (sashForm.getWeights()!=null) {
+ int[] w = sashForm.getWeights();
+ String[] ws = new String[w.length];
+ for (int i = 0; i < ws.length; i++) {
+ ws[i] = ""+w[i];
+ }
+ settings.put(DIALOG_SASH_WEIGHTS, ws);
+ }
+ }
+
+ /**
+ * Create a new header which is labelled by headerLabel.
+ *
+ * @param parent
+ * @return Label the label of the header
+ */
+ private Label createHeader(Composite parent) {
+ Composite header = new Composite(parent, SWT.NONE);
+
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 2;
+ layout.marginWidth = 0;
+ layout.marginHeight = 0;
+ header.setLayout(layout);
+
+ headerLabel = new Label(header, SWT.NONE);
+ headerLabel.addTraverseListener(new TraverseListener() {
+ public void keyTraversed(TraverseEvent e) {
+ if (e.detail == SWT.TRAVERSE_MNEMONIC && e.doit) {
+ e.detail = SWT.TRAVERSE_NONE;
+ pattern.setFocus();
+ }
+ }
+ });
+
+ GridData gd = new GridData(GridData.FILL_HORIZONTAL);
+ headerLabel.setLayoutData(gd);
+
+ createViewMenu(header);
+ header.setLayoutData(gd);
+
+ refreshHeaderLabel();
+ return headerLabel;
+ }
+
+ private void refreshHeaderLabel() {
+ String msg = toggleCaseSensitiveAction.isChecked() ? "Case SENSITIVE" : "Case INSENSITIVE";
+ msg += " Pattern (? = any character, * = any string)";
+ headerLabel.setText(msg);
+ }
+
+ /**
+ * Create the labels for the list and the progress. Return the list label.
+ *
+ * @param parent
+ * @return Label
+ */
+ private Label createLabels(Composite parent) {
+ Composite labels = new Composite(parent, SWT.NONE);
+
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 2;
+ layout.marginWidth = 0;
+ layout.marginHeight = 0;
+ labels.setLayout(layout);
+
+ Label listLabel = new Label(labels, SWT.NONE);
+ listLabel
+ .setText(WorkbenchMessages.FilteredItemsSelectionDialog_listLabel);
+
+ listLabel.addTraverseListener(new TraverseListener() {
+ public void keyTraversed(TraverseEvent e) {
+ if (e.detail == SWT.TRAVERSE_MNEMONIC && e.doit) {
+ e.detail = SWT.TRAVERSE_NONE;
+ list.getTable().setFocus();
+ }
+ }
+ });
+
+ GridData gd = new GridData(GridData.FILL_HORIZONTAL);
+ listLabel.setLayoutData(gd);
+
+ progressLabel = new Label(labels, SWT.RIGHT);
+ progressLabel.setLayoutData(gd);
+
+ labels.setLayoutData(gd);
+ return listLabel;
+ }
+
+ private void createViewMenu(Composite parent) {
+ toolBar = new ToolBar(parent, SWT.FLAT);
+ toolItem = new ToolItem(toolBar, SWT.PUSH, 0);
+
+ GridData data = new GridData();
+ data.horizontalAlignment = GridData.END;
+ toolBar.setLayoutData(data);
+
+ toolBar.addMouseListener(new MouseAdapter() {
+ public void mouseDown(MouseEvent e) {
+ showViewMenu();
+ }
+ });
+
+ toolItem.setImage(WorkbenchImages
+ .getImage(IWorkbenchGraphicConstants.IMG_LCL_VIEW_MENU));
+ toolItem
+ .setToolTipText(WorkbenchMessages.FilteredItemsSelectionDialog_menu);
+ toolItem.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ showViewMenu();
+ }
+ });
+
+ menuManager = new MenuManager();
+
+ fillViewMenu(menuManager);
+
+ IHandlerService service = (IHandlerService) PlatformUI.getWorkbench()
+ .getService(IHandlerService.class);
+ IHandler handler = new AbstractHandler() {
+ public Object execute(ExecutionEvent event) {
+ showViewMenu();
+ return null;
+ }
+ };
+ showViewHandler = service.activateHandler(
+ IWorkbenchCommandConstants.WINDOW_SHOW_VIEW_MENU, handler,
+ new ActiveShellExpression(getShell()));
+ }
+
+ /**
+ * Fills the menu of the dialog.
+ *
+ * @param menuManager
+ * the menu manager
+ */
+ protected void fillViewMenu(IMenuManager menuManager) {
+ IDialogSettings settings = getDialogSettings();
+ toggleCaseSensitiveAction = new ToggleCaseSensitiveAction(settings);
+ menuManager.add(toggleCaseSensitiveAction);
+ toggleKeepOpenAction = new ToggleKeepOpenAction(settings);
+ menuManager.add(toggleKeepOpenAction);
+ }
+
+ private void showViewMenu() {
+ Menu menu = menuManager.createContextMenu(getShell());
+ Rectangle bounds = toolItem.getBounds();
+ Point topLeft = new Point(bounds.x, bounds.y + bounds.height);
+ topLeft = toolBar.toDisplay(topLeft);
+ menu.setLocation(topLeft.x, topLeft.y);
+ menu.setVisible(true);
+ }
+
+ /**
+ * Hook that allows to add actions to the context menu.
+ * <p>
+ * Subclasses may extend in order to add other actions.</p>
+ *
+ * @param menuManager the context menu manager
+ * @since 3.5
+ */
+ protected void fillContextMenu(IMenuManager menuManager) {
+ }
+
+ private void createPopupMenu() {
+
+ contextMenuManager = new MenuManager();
+ contextMenuManager.setRemoveAllWhenShown(true);
+ contextMenuManager.addMenuListener(new IMenuListener() {
+ public void menuAboutToShow(IMenuManager manager) {
+ fillContextMenu(manager);
+ }
+ });
+
+ final Table table = list.getTable();
+ Menu menu= contextMenuManager.createContextMenu(table);
+ table.setMenu(menu);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ Composite dialogArea = (Composite) super.createDialogArea(parent);
+
+ dialogArea.addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ QuickSearchDialog.this.dispose();
+ }
+ });
+
+ Composite content = createNestedComposite(dialogArea, 1, false);
+ GridData gd = new GridData(GridData.FILL_BOTH);
+ content.setLayoutData(gd);
+
+ final Label headerLabel = createHeader(content);
+
+ Composite inputRow = createNestedComposite(content, 10, true);
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(inputRow);
+ pattern = new Text(inputRow, SWT.SINGLE | SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL);
+ pattern.getAccessible().addAccessibleListener(new AccessibleAdapter() {
+ public void getName(AccessibleEvent e) {
+ e.result = LegacyActionTools.removeMnemonics(headerLabel
+ .getText());
+ }
+ });
+ GridDataFactory.fillDefaults().span(6,1).grab(true, false).applyTo(pattern);
+
+ Composite searchInComposite = createNestedComposite(inputRow, 2, false);
+ GridDataFactory.fillDefaults().span(4,1).grab(true, false).applyTo(searchInComposite);
+ Label searchInLabel = new Label(searchInComposite, SWT.NONE);
+ searchInLabel.setText(" In: ");
+ searchIn = new Text(searchInComposite, SWT.SINGLE | SWT.BORDER | SWT.ICON_CANCEL);
+ searchIn.setToolTipText("Search in (comma-separated list of '.gitignore' style inclusion patterns)");
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(searchIn);
+
+ final Label listLabel = createLabels(content);
+
+ sashForm = new SashForm(content, SWT.VERTICAL);
+ GridDataFactory.fillDefaults().grab(true, true).applyTo(sashForm);
+
+ list = new TableViewer(sashForm, (multi ? SWT.MULTI : SWT.SINGLE) |
+ SWT.FULL_SELECTION | SWT.BORDER | SWT.V_SCROLL | SWT.VIRTUAL);
+// ColumnViewerToolTipSupport.enableFor(list, ToolTip.NO_RECREATE);
+
+ list.getTable().setHeaderVisible(true);
+ list.getTable().setLinesVisible(true);
+ list.getTable().getAccessible().addAccessibleListener(
+ new AccessibleAdapter() {
+ public void getName(AccessibleEvent e) {
+ if (e.childID == ACC.CHILDID_SELF) {
+ e.result = LegacyActionTools
+ .removeMnemonics(listLabel.getText());
+ }
+ }
+ });
+ list.setContentProvider(contentProvider);
+// new ScrollListener(list.getTable().getVerticalBar());
+// new SelectionChangedListener(list);
+
+ TableViewerColumn col = new TableViewerColumn(list, SWT.RIGHT);
+ col.setLabelProvider(LINE_NUMBER_LABEL_PROVIDER);
+ col.getColumn().setText("Line");
+ col.getColumn().setWidth(40);
+ col = new TableViewerColumn(list, SWT.LEFT);
+ col.getColumn().setText("Text");
+ col.setLabelProvider(LINE_TEXT_LABEL_PROVIDER);
+ col.getColumn().setWidth(400);
+ col = new TableViewerColumn(list, SWT.LEFT);
+ col.getColumn().setText("Path");
+ col.setLabelProvider(LINE_FILE_LABEL_PROVIDER);
+ col.getColumn().setWidth(150);
+
+ new TableResizeHelper(list).enableResizing();
+
+ //list.setLabelProvider(getItemsListLabelProvider());
+ list.setInput(new Object[0]);
+ list.setItemCount(contentProvider.getNumberOfElements());
+ gd = new GridData(GridData.FILL_BOTH);
+ applyDialogFont(list.getTable());
+ gd.heightHint= list.getTable().getItemHeight() * 15;
+ list.getTable().setLayoutData(gd);
+
+ createPopupMenu();
+
+ pattern.addModifyListener(e -> {
+ applyFilter(false);
+ });
+
+ searchIn.addModifyListener(e -> {
+ applyPathMatcher();
+ });
+
+ pattern.addKeyListener(new KeyAdapter() {
+ public void keyPressed(KeyEvent e) {
+ if (e.keyCode == SWT.ARROW_DOWN) {
+ if (list.getTable().getItemCount() > 0) {
+ list.getTable().setFocus();
+ list.getTable().select(0);
+ //programatic selection may not fire selection events so...
+ refreshDetails();
+ }
+ }
+ }
+ });
+
+ list.addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ StructuredSelection selection = (StructuredSelection) event
+ .getSelection();
+ handleSelected(selection);
+ }
+ });
+
+ list.addDoubleClickListener(new IDoubleClickListener() {
+ public void doubleClick(DoubleClickEvent event) {
+ handleDoubleClick();
+ }
+ });
+
+ list.getTable().addKeyListener(new KeyAdapter() {
+ public void keyPressed(KeyEvent e) {
+
+ if (e.keyCode == SWT.ARROW_UP && (e.stateMask & SWT.SHIFT) == 0
+ && (e.stateMask & SWT.CTRL) == 0) {
+ StructuredSelection selection = (StructuredSelection) list
+ .getSelection();
+
+ if (selection.size() == 1) {
+ Object element = selection.getFirstElement();
+ if (element.equals(list.getElementAt(0))) {
+ pattern.setFocus();
+ }
+ list.getTable().notifyListeners(SWT.Selection,
+ new Event());
+
+ }
+ }
+
+ if (e.keyCode == SWT.ARROW_DOWN
+ && (e.stateMask & SWT.SHIFT) != 0
+ && (e.stateMask & SWT.CTRL) != 0) {
+
+ list.getTable().notifyListeners(SWT.Selection, new Event());
+ }
+
+ }
+ });
+
+ createDetailsArea(sashForm);
+ sashForm.setWeights(new int[] {5,1});
+
+ applyDialogFont(content);
+
+ restoreDialog(getDialogSettings());
+
+ if (initialPatternText != null) {
+ pattern.setText(initialPatternText);
+ }
+
+ switch (selectionMode) {
+ case CARET_BEGINNING:
+ pattern.setSelection(0, 0);
+ break;
+ case FULL_SELECTION:
+ pattern.setSelection(0, initialPatternText.length());
+ break;
+ }
+
+ // apply filter even if pattern is empty (display history)
+ applyFilter(false);
+
+ return dialogArea;
+ }
+
+ private Composite createNestedComposite(Composite parent, int numRows, boolean equalRows) {
+ Composite nested = new Composite(parent, SWT.NONE);
+ {
+ GridLayout layout = new GridLayout(numRows, equalRows);
+ layout.marginWidth = 0;
+ layout.marginHeight = 0;
+ layout.marginLeft = 0;
+ layout.marginRight = 0;
+ layout.horizontalSpacing = 0;
+ nested.setLayout(layout);
+ }
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(nested);
+ return nested;
+ }
+
+ protected void dispose() {
+ if (blankImage!=null) {
+ blankImage.dispose();
+ blankImage = null;
+ }
+ }
+
+ private void createDetailsArea(Composite parent) {
+ details = new StyledText(parent, SWT.MULTI+SWT.READ_ONLY+SWT.BORDER+SWT.H_SCROLL);
+ details.setFont(JFaceResources.getFont(TEXT_FONT));
+
+ list.addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ refreshDetails();
+ }
+ });
+ details.addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ refreshDetails();
+ }
+ });
+ }
+
+
+ // Dumber version just using the a 'raw' StyledText widget.
+ private void refreshDetails() {
+ if (details!=null && list!=null && !list.getTable().isDisposed()) {
+ if (documents==null) {
+ documents = new DocumentFetcher();
+ }
+ IStructuredSelection sel = (IStructuredSelection) list.getSelection();
+ if (sel==null || sel.isEmpty()) {
+ details.setText("");
+ } else {
+ //Not empty selection
+ int numLines = computeLines();
+ if (numLines > 0) {
+ LineItem item = (LineItem) sel.getFirstElement();
+ IDocument document = documents.getDocument(item.getFile());
+ if (document!=null) {
+ try {
+ int line = item.getLineNumber()-1; //in document lines are 0 based. In search 1 based.
+ int start = document.getLineOffset(Math.max(line-(numLines-1)/2, 0));
+ int end = document.getLength();
+ try {
+ end = document.getLineOffset(start+numLines);
+ } catch (BadLocationException e) {
+ //Presumably line number is past the end of document.
+ //ignore.
+ }
+
+ StyledString styledString = highlightMatches(document.get(start, end-start));
+ details.setText(styledString.getString());
+ details.setStyleRanges(styledString.getStyleRanges());
+
+ return;
+ } catch (BadLocationException e) {
+ }
+ }
+ }
+ }
+ //empty selection or some error:
+ details.setText("");
+ }
+ }
+
+ /**
+ * Computes how much lines of text can be displayed in the details section based on
+ * its current height and font metrics.
+ */
+ private int computeLines() {
+ if (details!=null && !details.isDisposed()) {
+ GC gc = new GC(details);
+ try {
+ FontMetrics fm = gc.getFontMetrics();
+ int itemH = fm.getHeight();
+ int areaH = details.getClientArea().height;
+ return (areaH+itemH-1) / itemH;
+ } finally {
+ gc.dispose();
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Helper function to highlight all the matches for the current query in a given piece
+ * of text.
+ *
+ * @return StyledString instance.
+ */
+ private StyledString highlightMatches(String visibleText) {
+ StyledString styledText = new StyledString(visibleText);
+ List<TextRange> matches = getQuery().findAll(visibleText);
+ for (TextRange m : matches) {
+ styledText.setStyle(m.getOffset(), m.getLength(), HIGHLIGHT_STYLE);
+ }
+ return styledText;
+ }
+
+// Version using sourceviewer
+// private void refreshDetails() {
+// if (details!=null && list!=null && !list.getTable().isDisposed()) {
+// if (documents==null) {
+// documents = new DocumentFetcher();
+// }
+// IStructuredSelection sel = (IStructuredSelection) list.getSelection();
+// if (sel!=null && !sel.isEmpty()) {
+// //Not empty selection
+// LineItem item = (LineItem) sel.getFirstElement();
+// IDocument document = documents.getDocument(item.getFile());
+// try {
+// int line = item.getLineNumber()-1; //in document lines are 0 based. In search 1 based.
+// int start = document.getLineOffset(Math.max(line-2, 0));
+// int end = document.getLength();
+// try {
+// end = document.getLineOffset(line+3);
+// } catch (BadLocationException e) {
+// //Presumably line number is past the end of document.
+// //ignore.
+// }
+// details.setDocument(document, start, end-start);
+//
+// String visibleText = document.get(start, end-start);
+// List<TextRange> matches = getQuery().findAll(visibleText);
+// Region visibleRegion = new Region(start, end-start);
+// TextPresentation presentation = new TextPresentation(visibleRegion, 20);
+// presentation.setDefaultStyleRange(new StyleRange(0, document.getLength(), null, null));
+// for (TextRange m : matches) {
+// presentation.addStyleRange(new StyleRange(m.start+start, m.len, null, YELLOW));
+// }
+// details.changeTextPresentation(presentation, true);
+//
+// return;
+// } catch (BadLocationException e) {
+// }
+// }
+// details.setDocument(null);
+// }
+// }
+
+ /**
+ * Handle selection in the items list by updating labels of selected and
+ * unselected items and refresh the details field using the selection.
+ *
+ * @param selection
+ * the new selection
+ */
+ protected void handleSelected(StructuredSelection selection) {
+ IStatus status = new Status(IStatus.OK, PlatformUI.PLUGIN_ID,
+ IStatus.OK, EMPTY_STRING, null);
+
+ updateStatus(status);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.window.Dialog#getDialogBoundsSettings()
+ */
+ protected IDialogSettings getDialogBoundsSettings() {
+ IDialogSettings settings = getDialogSettings();
+ IDialogSettings section = settings.getSection(DIALOG_BOUNDS_SETTINGS);
+ if (section == null) {
+ section = settings.addNewSection(DIALOG_BOUNDS_SETTINGS);
+ section.put(DIALOG_HEIGHT, 500);
+ section.put(DIALOG_WIDTH, 600);
+ }
+ return section;
+ }
+
+ /**
+ * Returns the dialog settings. Returned object can't be null.
+ *
+ * @return return dialog settings for this dialog
+ */
+ protected IDialogSettings getDialogSettings() {
+ IDialogSettings settings = IDEWorkbenchPlugin.getDefault()
+ .getDialogSettings().getSection(DIALOG_SETTINGS);
+
+ if (settings == null) {
+ settings = IDEWorkbenchPlugin.getDefault().getDialogSettings()
+ .addNewSection(DIALOG_SETTINGS);
+ }
+
+ return settings;
+ }
+
+ /**
+ * Has to be called in UI thread.
+ */
+ public void refreshWidgets() {
+ if (list != null && !list.getTable().isDisposed()) {
+ int itemCount = contentProvider.getNumberOfElements();
+ list.setItemCount(itemCount);
+ list.refresh(true, false);
+ Button goButton = getButton(GO_BUTTON_ID);
+ if (goButton!=null && !goButton.isDisposed()) {
+ //Even if no element is selected. The dialog should be have as if the first
+ //element in the list is selected. So the button is enabled if any
+ //element is available in the list.
+ goButton.setEnabled(itemCount>0);
+ }
+
+ }
+ }
+
+ /**
+ * Schedule refresh job.
+ */
+ public void scheduleRefresh() {
+ refreshJob.schedule();
+// refreshCacheJob.cancelAll();
+// refreshCacheJob.schedule();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.dialogs.SelectionStatusDialog#computeResult()
+ */
+ protected void computeResult() {
+ List objectsToReturn = ((StructuredSelection) list.getSelection())
+ .toList();
+ if (objectsToReturn.isEmpty()) {
+ //Pretend that the first element is selected.
+ Object first = list.getElementAt(0);
+ if (first!=null) {
+ objectsToReturn = Arrays.asList(first);
+ }
+ }
+ setResult(objectsToReturn);
+ }
+
+
+
+ /**
+ * Handles double-click of items, but *also* by pressing the 'enter' key.
+ */
+ protected void handleDoubleClick() {
+ goButtonPressed();
+ }
+
+ protected void refreshButtonPressed() {
+ applyFilter(true);
+ }
+
+ /**
+ * Handles directly clicking the 'go' button.
+ */
+ protected void goButtonPressed() {
+ computeResult();
+ openSelection();
+ if (!toggleKeepOpenAction.isChecked()) {
+ close();
+ }
+ }
+
+ @Override
+ protected void createButtonsForButtonBar(Composite parent) {
+ SelectionListener listener = new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ int buttonId = (int) ((Button)e.widget).getData();
+ if (buttonId==GO_BUTTON_ID) {
+ goButtonPressed();
+ } else if (buttonId==REFRESH_BUTTON_ID) {
+ refreshButtonPressed();
+ }
+ }
+ };
+ createButton(parent, GO_BUTTON_ID, "Go!", false).addSelectionListener(listener);
+ createButton(parent, REFRESH_BUTTON_ID, "Refresh", false).addSelectionListener(listener);
+ refreshWidgets();
+ }
+
+ /**
+ * Sets the initial pattern used by the filter. This text is copied into the
+ * selection input on the dialog. A full selection is used in the pattern
+ * input field.
+ *
+ * @param text
+ * initial pattern for the filter
+ * @see QuickSearchDialog#FULL_SELECTION
+ */
+ public void setInitialPattern(String text) {
+ setInitialPattern(text, FULL_SELECTION);
+ }
+
+ /**
+ * Sets the initial pattern used by the filter. This text is copied into the
+ * selection input on the dialog. The <code>selectionMode</code> is used
+ * to choose selection type for the input field.
+ *
+ * @param text
+ * initial pattern for the filter
+ * @param selectionMode
+ * one of: {@link QuickSearchDialog#NONE},
+ * {@link QuickSearchDialog#CARET_BEGINNING},
+ * {@link QuickSearchDialog#FULL_SELECTION}
+ */
+ public void setInitialPattern(String text, int selectionMode) {
+ this.initialPatternText = text;
+ this.selectionMode = selectionMode;
+ }
+
+ /**
+ * Gets initial pattern.
+ *
+ * @return initial pattern, or <code>null</code> if initial pattern is not
+ * set
+ */
+ protected String getInitialPattern() {
+ return this.initialPatternText;
+ }
+
+ /**
+ * Returns the current selection.
+ *
+ * @return the current selection
+ */
+ protected StructuredSelection getSelectedItems() {
+
+ StructuredSelection selection = (StructuredSelection) list
+ .getSelection();
+
+ List selectedItems = selection.toList();
+
+ return new StructuredSelection(selectedItems);
+ }
+
+ /**
+ * Validates the item. When items on the items list are selected or
+ * deselected, it validates each item in the selection and the dialog status
+ * depends on all validations.
+ *
+ * @param item
+ * an item to be checked
+ * @return status of the dialog to be set
+ */
+ protected IStatus validateItem(Object item) {
+ return new Status(IStatus.OK, QuickSearchActivator.PLUGIN_ID, "fine");
+ }
+
+ /**
+ * Creates an instance of a filter.
+ *
+ * @return a filter for items on the items list. Can be <code>null</code>,
+ * no filtering will be applied then, causing no item to be shown in
+ * the list.
+ */
+ protected QuickTextQuery createFilter() {
+ return new QuickTextQuery(pattern.getText(), toggleCaseSensitiveAction.isChecked());
+ }
+
+ /**
+ * Applies the filter created by <code>createFilter()</code> method to the
+ * items list. When new filter is different than previous one it will cause
+ * refiltering.
+ * <p>
+ * The 'force' parameter forces a full refresh of the search results / filter even
+ * when the filter is unchanged, or when a incremenal filtering optimisation could be
+ * applied based on query structure. (The use case for this, is to trigger forced refresh
+ * because the underlying resources may have changed).
+ */
+ protected void applyFilter(boolean force) {
+ QuickTextQuery newFilter = createFilter();
+ if (this.searcher==null) {
+ if (!newFilter.isTrivial()) {
+ //Create the QuickTextSearcher with the inital query.
+ this.searcher = new QuickTextSearcher(newFilter, context.createPriorityFun(), MAX_LINE_LEN, new QuickTextSearchRequestor() {
+ @Override
+ public void add(LineItem match) {
+ contentProvider.add(match);
+ contentProvider.refresh();
+ }
+ @Override
+ public void clear() {
+ contentProvider.reset();
+ contentProvider.refresh();
+ }
+ @Override
+ public void revoke(LineItem match) {
+ contentProvider.remove(match);
+ contentProvider.refresh();
+ }
+ @Override
+ public void update(LineItem match) {
+ contentProvider.refresh();
+ }
+ });
+ applyPathMatcher();
+ refreshWidgets();
+ }
+// this.list.setInput(input)
+ } else {
+ //The QuickTextSearcher is already active update the query
+ this.searcher.setQuery(newFilter, force);
+ }
+ if (progressJob!=null) {
+ progressJob.schedule();
+ }
+ }
+
+ private void applyPathMatcher() {
+ if (this.searcher!=null) {
+ this.searcher.setPathMatcher(ResourceMatchers.commaSeparatedPaths(searchIn.getText()));
+ }
+ }
+
+
+
+ /**
+ * Returns name for then given object.
+ *
+ * @param item
+ * an object from the content provider. Subclasses should pay
+ * attention to the passed argument. They should either only pass
+ * objects of a known type (one used in content provider) or make
+ * sure that passed parameter is the expected one (by type
+ * checking like <code>instanceof</code> inside the method).
+ * @return name of the given item
+ */
+ public String getElementName(Object item) {
+ return ""+item;
+// return (String)item; // Assuming the items are strings for now
+ }
+
+ /**
+ * Collects filtered elements. Contains one synchronized, sorted set for
+ * collecting filtered elements.
+ * Implementation of <code>ItemsFilter</code> is used to filter elements.
+ * The key function of filter used in to filtering is
+ * <code>matchElement(Object item)</code>.
+ * <p>
+ * The <code>ContentProvider</code> class also provides item filtering
+ * methods. The filtering has been moved from the standard TableView
+ * <code>getFilteredItems()</code> method to content provider, because
+ * <code>ILazyContentProvider</code> and virtual tables are used. This
+ * class is responsible for adding a separator below history items and
+ * marking each items as duplicate if its name repeats more than once on the
+ * filtered list.
+ */
+ private class ContentProvider implements IStructuredContentProvider, ILazyContentProvider {
+
+ private List items;
+
+ /**
+ * Creates new instance of <code>ContentProvider</code>.
+ */
+ public ContentProvider() {
+ this.items = Collections.synchronizedList(new ArrayList(2048));
+// this.duplicates = Collections.synchronizedSet(new HashSet(256));
+// this.lastSortedItems = Collections.synchronizedList(new ArrayList(
+// 2048));
+ }
+
+ public void remove(LineItem match) {
+ this.items.remove(match);
+ }
+
+ /**
+ * Removes all content items and resets progress message.
+ */
+ public void reset() {
+ this.items.clear();
+ }
+
+ /**
+ * Adds filtered item.
+ *
+ * @param match
+ */
+ public void add(LineItem match) {
+ this.items.add(match);
+ }
+
+ /**
+ * Refresh dialog.
+ */
+ public void refresh() {
+ scheduleRefresh();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
+ */
+ public Object[] getElements(Object inputElement) {
+ return items.toArray();
+ }
+
+ public int getNumberOfElements() {
+ return items.size();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.viewers.IContentProvider#dispose()
+ */
+ public void dispose() {
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
+ * java.lang.Object, java.lang.Object)
+ */
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.viewers.ILazyContentProvider#updateElement(int)
+ */
+ public void updateElement(int index) {
+
+ QuickSearchDialog.this.list.replace((items
+ .size() > index) ? items.get(index) : null,
+ index);
+
+ }
+
+ }
+
+ /**
+ * Get the control where the search pattern is entered. Any filtering should
+ * be done using an {@link ItemsFilter}. This control should only be
+ * accessed for listeners that wish to handle events that do not affect
+ * filtering such as custom traversal.
+ *
+ * @return Control or <code>null</code> if the pattern control has not
+ * been created.
+ */
+ public Control getPatternControl() {
+ return pattern;
+ }
+
+ public QuickTextQuery getQuery() {
+ return searcher.getQuery();
+ }
+
+}
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchHandler.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchHandler.java
new file mode 100644
index 00000000000..7f13c6b3714
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchHandler.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Pivotal Software, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.ui;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ui.ISelectionService;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * Our sample handler extends AbstractHandler, an IHandler base class.
+ * @see org.eclipse.core.commands.IHandler
+ * @see org.eclipse.core.commands.AbstractHandler
+ */
+@SuppressWarnings("restriction")
+public class QuickSearchHandler extends AbstractHandler {
+ /**
+ * The constructor.
+ */
+ public QuickSearchHandler() {
+ }
+
+ /**
+ * the command has been executed, so extract extract the needed information
+ * from the application context.
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindowChecked(event);
+ doQuickSearch(window);
+ return null;
+ }
+
+ public static void doQuickSearch(IWorkbenchWindow window) {
+ QuickSearchDialog dialog = new QuickSearchDialog(window);
+ initializeFromSelection(window, dialog);
+ dialog.open();
+ }
+
+ /**
+ * Based on the current active selection initialize the priority function and/or
+ * the initial contents of the search box.
+ */
+ static private void initializeFromSelection(IWorkbenchWindow workbench, QuickSearchDialog dialog) {
+ if (workbench!=null) {
+ ISelectionService selectionService = workbench.getSelectionService();
+ ISelection selection = selectionService.getSelection();
+ if (selection!=null && selection instanceof ITextSelection) {
+ //Use text selection to set initial search pattern.
+ String text = ((ITextSelection) selection).getText();
+ if (text!=null && !"".equals(text)) {
+ dialog.setInitialPattern(text, QuickSearchDialog.FULL_SELECTION);
+ }
+ }
+ }
+// IEditorPart editor = HandlerUtil.getActiveEditor(event);
+// if (editor!=null && editor instanceof ITextEditor) {
+// ITextEditor textEditor = (ITextEditor)editor;
+// ISelection selection = textEditor.getSelectionProvider().getSelection();
+// if (selection!=null && selection instanceof ITextSelection) {
+// String text = ((ITextSelection) selection).getText();
+// if (text!=null && !"".equals(text)) {
+// dialog.setInitialPattern(text, QuickSearchDialog.FULL_SELECTION);
+// }
+// }
+// }
+ }
+}
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchPreferencesPage.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchPreferencesPage.java
new file mode 100644
index 00000000000..d13e9606465
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/QuickSearchPreferencesPage.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Pivotal Software, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.ui;
+
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.jface.preference.IntegerFieldEditor;
+import org.eclipse.jface.preference.StringFieldEditor;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.text.quicksearch.internal.core.preferences.QuickSearchPreferences;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class QuickSearchPreferencesPage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage {
+
+ public QuickSearchPreferencesPage() {
+ super(FieldEditorPreferencePage.GRID);
+ setPreferenceStore(QuickSearchActivator.getDefault().getPreferenceStore());
+ QuickSearchPreferences.initializeDefaults();
+ }
+
+ @Override
+ public void init(IWorkbench arg0) {
+ }
+
+ private static final String[] prefsKeys = {
+ QuickSearchPreferences.IGNORED_EXTENSIONS,
+ QuickSearchPreferences.IGNORED_PREFIXES,
+ QuickSearchPreferences.IGNORED_NAMES
+ };
+
+ private static final String[] fieldNames = {
+ Messages.QuickSearchPreferencesPage_0, Messages.QuickSearchPreferencesPage_1, Messages.QuickSearchPreferencesPage_2
+ };
+
+ private static final String[] toolTips = {
+ Messages.QuickSearchPreferencesPage_3
+ ,
+ Messages.QuickSearchPreferencesPage_4
+ ,
+ Messages.QuickSearchPreferencesPage_5
+ };
+
+ @Override
+ protected void createFieldEditors() {
+ IntegerFieldEditor field_maxLineLen = new IntegerFieldEditor(QuickSearchPreferences.MAX_LINE_LEN, Messages.QuickSearchPreferencesPage_6, getFieldEditorParent());
+ field_maxLineLen.getTextControl(getFieldEditorParent()).setToolTipText(
+ Messages.QuickSearchPreferencesPage_7);
+ addField(field_maxLineLen);
+
+ for (int i = 0; i < fieldNames.length; i++) {
+ final String tooltip = toolTips[i];
+ StringFieldEditor field = new StringFieldEditor(prefsKeys[i], Messages.QuickSearchPreferencesPage_8+" "+fieldNames[i], 45, 5, StringFieldEditor.VALIDATE_ON_FOCUS_LOST, getFieldEditorParent()) {
+ @Override
+ protected Text createTextWidget(Composite parent) {
+ Text w = super.createTextWidget(parent);
+ w.setToolTipText(tooltip);
+ return w;
+ }
+ };
+ addField(field);
+ }
+ }
+}
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/TableResizeHelper.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/TableResizeHelper.java
new file mode 100644
index 00000000000..2b9f2ff7ac9
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/TableResizeHelper.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Pivotal, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.ui;
+
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+
+/**
+ * Resizes table columns such that columns fit the table width. The table is
+ * initially resized when the resize is enabled, and its columns continue being
+ * resized automatically as the user resizes the table.
+ */
+public class TableResizeHelper {
+
+ private final TableViewer tableViewer;
+
+ public TableResizeHelper(TableViewer tableViewer) {
+ this.tableViewer = tableViewer;
+ }
+
+ public void enableResizing() {
+ ControlListener resizeListener = new ControlListener() {
+ public void controlResized(ControlEvent e) {
+ resizeTable();
+ }
+ public void controlMoved(ControlEvent e) {
+ }
+ };
+ tableViewer.getTable().addControlListener(resizeListener);
+ TableColumn[] cols = tableViewer.getTable().getColumns();
+ if (cols!=null) {
+ for (int i = 0; i < cols.length-1; i++) {
+ cols[i].addControlListener(resizeListener);
+ }
+ }
+
+ // Initial resize of the columns
+ resizeTable();
+
+ }
+
+ protected void resizeTable() {
+ Composite tableComposite = tableViewer.getTable();//.getParent();
+ Rectangle tableCompositeArea = tableComposite.getClientArea();
+ int width = tableCompositeArea.width;
+// ScrollBar sb = tableViewer.getTable().getVerticalBar();
+// if (sb!=null && sb.isVisible()) {
+// width = width - sb.getSize().x;
+// }
+ resizeTableColumns(width, tableViewer.getTable());
+ }
+
+ protected void resizeTableColumns(int tableWidth, Table table) {
+ TableColumn[] tableColumns = table.getColumns();
+
+ if (tableColumns.length == 0) {
+ return;
+ }
+
+ int total = 0;
+
+ for (TableColumn column : tableColumns) {
+ total += column.getWidth();
+ }
+
+ TableColumn lastColumn = tableColumns[tableColumns.length - 1];
+ int newWidth = (tableWidth - total) + lastColumn.getWidth();
+ if (newWidth>0) {
+ lastColumn.setWidth(newWidth);
+ }
+
+ }
+
+}
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/messages.properties b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/messages.properties
new file mode 100644
index 00000000000..5d15d07ba64
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/ui/messages.properties
@@ -0,0 +1,9 @@
+QuickSearchPreferencesPage_0=Extensions
+QuickSearchPreferencesPage_1=Prefixes
+QuickSearchPreferencesPage_2=Names
+QuickSearchPreferencesPage_3=Enter a list of file extensions. Elements in the list can be separated by commas or newlines. Any file or folder ending with one of the extensions will be ignored.
+QuickSearchPreferencesPage_4=Enter a list of file prefixes. Elements in the list can be separated by commas or newlines. Any file or folder who's name begins with one of the extensions will be ignored.
+QuickSearchPreferencesPage_5=Enter a list of file names. Elements in the list can be separated by commas or newlines. Any file or folder who's name equals one of the extensions will be ignored.
+QuickSearchPreferencesPage_6=Max Line Length
+QuickSearchPreferencesPage_7=When QuickSearch encounters a line of text longer than 'Max Line Length' it stops searching the current file. This is meant to avoid searching in machine generated text files, such as, minified javascript.
+QuickSearchPreferencesPage_8=Ignore
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/util/DocumentFetcher.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/util/DocumentFetcher.java
new file mode 100644
index 00000000000..52544d206b8
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/util/DocumentFetcher.java
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008, 2013-2019 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Pivotal Inc - Adapted for use in quicksearch
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.util;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.filebuffers.FileBuffers;
+import org.eclipse.core.filebuffers.ITextFileBuffer;
+import org.eclipse.core.filebuffers.ITextFileBufferManager;
+import org.eclipse.core.filebuffers.LocationKind;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.search.internal.ui.SearchPlugin;
+import org.eclipse.text.quicksearch.internal.ui.QuickSearchActivator;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorReference;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.editors.text.TextFileDocumentProvider;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+/**
+ * Useful utilities (private methods) copied from org.eclipse.search.internal.core.text.TextSearchVisitor
+ * and rearanged / massaged to be a more reusable utility class.
+ * <p>
+ * These utilities allow us to access the contents of dirty editors so we can search/read in them as though they
+ * are already saved but without actually requiring the user to save them.
+ *
+ * @author Kris De Volder
+ */
+@SuppressWarnings("restriction")
+public class DocumentFetcher {
+
+ private Map<IFile, IDocument> dirtyEditors;
+
+ //Simple cache remembers the last fetched file and document.
+ private IFile lastFile = null;
+ private IDocument lastDocument = null;
+
+ IDocumentProvider provider = new TextFileDocumentProvider();
+
+ public DocumentFetcher() {
+ if (PlatformUI.isWorkbenchRunning()) {
+ dirtyEditors = evalNonFileBufferDocuments();
+ } else {
+ dirtyEditors = Collections.emptyMap();
+ }
+ }
+
+ /**
+ * Obtains a {@link IDocument} containing the contents of a
+ * {@link IFile}. Two different scenarios are supported depending
+ * on whether or not the file is currently opened in a editor.
+ * <p>
+ * If the IFile is opened in an editor, then the document reflects
+ * the editor contents (including any not-yet saved edits).
+ * <p>
+ * If the file is not open, then the document just reflects the
+ * contents of the file.
+ *
+ * @param file
+ * @return Document containing the contents of the file or editor buffer,
+ * or null if the content can not be found (it exists neither as a editor
+ * buffer nor corresponds to an existing file in the workspace.
+ */
+ public IDocument getDocument(IFile file) {
+ if (file==lastFile) {
+ return lastDocument;
+ }
+ lastFile = file;
+ lastDocument = dirtyEditors.get(file);
+ if (lastDocument==null) {
+ lastDocument = getOpenDocument(file);
+ if (lastDocument==null) {
+ lastDocument = getClosedDocument(file);
+ }
+ }
+ return lastDocument;
+ }
+
+ private IDocument getOpenDocument(IFile file) {
+ ITextFileBufferManager bufferManager= FileBuffers.getTextFileBufferManager();
+ ITextFileBuffer textFileBuffer= bufferManager.getTextFileBuffer(file.getFullPath(), LocationKind.IFILE);
+ if (textFileBuffer != null) {
+ return textFileBuffer.getDocument();
+ }
+ return null;
+ }
+
+ private IDocument getClosedDocument(IFile file) {
+ //No in the manager yet. Try to create a temporary buffer then remove it again.
+ ITextFileBufferManager bufferManager = FileBuffers.getTextFileBufferManager();
+ IPath location = file.getFullPath(); //Must use workspace location, not fs location for API below.
+ ITextFileBuffer buffer = null;
+ try {
+ bufferManager.connect(location, LocationKind.IFILE, new NullProgressMonitor());
+ buffer = bufferManager.getTextFileBuffer(location, LocationKind.IFILE);
+ if (buffer!=null) {
+ return buffer.getDocument();
+ }
+ } catch (Throwable e) {
+ QuickSearchActivator.log(e);
+ } finally {
+ try {
+ bufferManager.disconnect(location, LocationKind.IFILE, new NullProgressMonitor());
+ } catch (CoreException e) {
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return returns a map from IFile to IDocument for all open, dirty editors.
+ */
+ private Map<IFile, IDocument> evalNonFileBufferDocuments() {
+ Map<IFile, IDocument> result= new HashMap<IFile, IDocument>();
+ IWorkbench workbench= SearchPlugin.getDefault().getWorkbench();
+ IWorkbenchWindow[] windows= workbench.getWorkbenchWindows();
+ for (int i= 0; i < windows.length; i++) {
+ IWorkbenchPage[] pages= windows[i].getPages();
+ for (int x= 0; x < pages.length; x++) {
+ IEditorReference[] editorRefs= pages[x].getEditorReferences();
+ for (int z= 0; z < editorRefs.length; z++) {
+ IEditorPart ep= editorRefs[z].getEditor(false);
+ if (ep instanceof ITextEditor && ep.isDirty()) { // only dirty editors
+ evaluateTextEditor(result, ep);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ private void evaluateTextEditor(Map<IFile, IDocument> result, IEditorPart ep) {
+ IEditorInput input= ep.getEditorInput();
+ if (input instanceof IFileEditorInput) {
+ IFile file= ((IFileEditorInput) input).getFile();
+ if (!result.containsKey(file)) { // take the first editor found
+ ITextFileBufferManager bufferManager= FileBuffers.getTextFileBufferManager();
+ ITextFileBuffer textFileBuffer= bufferManager.getTextFileBuffer(file.getFullPath(), LocationKind.IFILE);
+ if (textFileBuffer != null) {
+ // file buffer has precedence
+ result.put(file, textFileBuffer.getDocument());
+ } else {
+ // use document provider
+ IDocument document= ((ITextEditor) ep).getDocumentProvider().getDocument(input);
+ if (document != null) {
+ result.put(file, document);
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/util/LightSchedulingRule.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/util/LightSchedulingRule.java
new file mode 100644
index 00000000000..fa4b2af7e9f
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/util/LightSchedulingRule.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2013-2019 Pivotal Software, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.util;
+
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+/**
+ * A scheduling rule that conflicts only with itself and only contains itself.
+ * <p>
+ * Note that every new instance of this rule is distinct. Different instances do not conflict
+ * with eachother (each instance only conflicts with itself.
+ *
+ * @author Kris De Volder
+ */
+public class LightSchedulingRule implements ISchedulingRule {
+ private final String name;
+
+ /**
+ * Create a scheduling rule that conflicts only with itself and only contains itself.
+ * Runnables that want to have a 'light' impact on blocking other jobs
+ * but still some guarantee that they won't trample over other things that require
+ * access to some internal shared resource that only they can access can use this
+ * rule to protect the resource.
+ */
+ public LightSchedulingRule(String name) {
+ this.name = name;
+ }
+
+ public boolean contains(ISchedulingRule rule) {
+ return rule == this;
+ }
+
+ public boolean isConflicting(ISchedulingRule rule) {
+ return rule == this || rule.contains(this);
+ }
+
+ public String toString() {
+ return name;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/util/LineReader.java b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/util/LineReader.java
new file mode 100644
index 00000000000..065076f0ae8
--- /dev/null
+++ b/org.eclipse.text.quicksearch/src/org/eclipse/text/quicksearch/internal/util/LineReader.java
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Pivotal Software, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Pivotal Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.text.quicksearch.internal.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * Provides a helper to efficiently split a file into
+ * lines. Similar to BufferedReader.readline, but it also keeps
+ * track of character position while reading. This is needed to
+ * ease translation from line-relative offsets into stream-relative
+ * offsets.
+ *
+ * @author Kris De Volder
+ */
+public class LineReader {
+
+ private static final int EXPECTED_LINE_LENGTH = 160;
+ public static final int DEFAULT_MAX_LINE_LENGTH = 1000;
+
+ private BufferedReader input;
+
+ //This simple implementation just wraps a BufferedReader and StringBuilder
+ //to do the buffering and String building.
+ //It may be more efficient to implement our own buffering like BufferedReader
+ //does.
+
+ public LineReader(Reader reader) {
+ this(reader, DEFAULT_MAX_LINE_LENGTH);
+ }
+
+ public LineReader(Reader reader, int maxLineLength) {
+ input = buffered(reader);
+ MAX_LINE_LENGTH = maxLineLength;
+ }
+
+
+ private StringBuilder line = new StringBuilder(EXPECTED_LINE_LENGTH);
+
+ private final int MAX_LINE_LENGTH;
+ private int lineOffset = -1; //Start pos of last line read.
+ private int offset = 0; //position of next char in input.
+ private int mark = 0; //mark offset in underlying stream
+
+ private BufferedReader buffered(Reader reader) {
+ //If already buffered don't wrap it again.
+ if (reader instanceof BufferedReader) {
+ return (BufferedReader) reader;
+ } else {
+ return new BufferedReader(reader);
+ }
+ }
+
+ /**
+ * Close the underlying stream. Does nothing if already closed.
+ */
+ public void close() {
+ BufferedReader toClose = null;
+ synchronized (input) {
+ if (input==null) {
+ return;
+ }
+ toClose = input;
+ input = null;
+ }
+ try {
+ toClose.close();
+ } catch (IOException e) {
+ //Ignore.
+ }
+ }
+
+ public String readLine() throws IOException {
+ lineOffset = offset; //remember start of line
+ int maxOffset = offset + MAX_LINE_LENGTH;
+ //Read text until we see either a CR, CR LF or LF.
+ int c = read();
+ if (c==-1) {
+ return null;
+ }
+ //read until newline
+ while (c!='\r' && c!='\n' && c!=-1) {
+ line.append((char)c);
+ c = read();
+ if (offset>maxOffset) {
+ throw new IOException("Very long lines of text. Minified file?");
+ }
+ }
+ //Last char read was some kind of line terminator. But only read first char of it.
+ if (c=='\r') {
+ mark(); //next char may be part of next line. May need to 'unread' it.
+ int next = read();
+ if (next == '\n') {
+ //skip
+ } else {
+ unread();
+ }
+ }
+ try {
+ return line.toString();
+ } finally {
+ line.setLength(0);
+ }
+ }
+
+ private void unread() throws IOException {
+ offset = mark;
+ input.reset();
+ }
+
+ private void mark() throws IOException {
+ mark = offset;
+ input.mark(1);
+ }
+
+ private int read() throws IOException {
+ try {
+ offset++;
+ return input.read();
+ } catch (IOException e) {
+ //pretend errors are like EOF.
+ return -1;
+ }
+ }
+
+ /**
+ * @return The offset of the start of the last line read relative to beginning of the stream; or -1 if
+ * no line has been read yet.
+ */
+ public int getLastLineOffset() {
+ return lineOffset;
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index 5be07d721d2..435c15ecf7a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -70,6 +70,9 @@
<module>org.eclipse.ui.genericeditor</module>
<module>org.eclipse.ui.genericeditor.tests</module>
<module>org.eclipse.ui.genericeditor.examples</module>
+ <!-- Quick Search -->
+ <module>org.eclipse.text.quicksearch</module>
+ <module>org.eclipse.text.quicksearch.tests</module>
</modules>
</project>

Back to the top