Permalink
Browse files

#20 add complex search infrastructure (without any search logic)

  • Loading branch information...
nittka committed Nov 20, 2016
1 parent b5a7479 commit 21e58fa890c354172d889b236ba2b9be5edd6a8b
@@ -15,7 +15,8 @@ Require-Bundle: de.nittka.tooling.xarchive;visibility:=reexport,
org.antlr.runtime,
org.eclipse.xtext.ui.codetemplates.ui,
org.eclipse.compare,
org.eclipse.xtext.xbase.lib
org.eclipse.xtext.xbase.lib,
org.eclipse.search
Import-Package: org.apache.log4j,
org.eclipse.xtext.xbase.lib
Bundle-RequiredExecutionEnvironment: J2SE-1.5
@@ -17,6 +17,15 @@
</extension>
<extension
point="org.eclipse.ui.handlers">
<handler
class="de.nittka.tooling.xarchive.ui.XarchiveExecutableExtensionFactory:de.nittka.tooling.xarchive.ui.search.XarchiveSearchHandler"
commandId="de.nittka.tooling.xarchive.ui.search">
<activeWhen>
<reference
definitionId="de.nittka.tooling.xarchive.Xarchive.Editor.opened">
</reference>
</activeWhen>
</handler>
<handler
class="de.nittka.tooling.xarchive.ui.XarchiveExecutableExtensionFactory:org.eclipse.xtext.ui.editor.hyperlinking.OpenDeclarationHandler"
commandId="org.eclipse.xtext.ui.editor.hyperlinking.OpenDeclaration">
@@ -140,6 +149,12 @@
description="Copy the qualified name for the selected element"
name="Copy Qualified Name">
</command>
<command
id="de.nittka.tooling.xarchive.ui.search"
categoryId="org.eclipse.ui.category.search"
description="Executes the search corresponding to the context"
name="Xarchive Search">
</command>
</extension>
<extension point="org.eclipse.ui.menus">
<menuContribution
@@ -380,5 +395,14 @@
extensions="xarch">
</provider>
</extension>
<extension
point="org.eclipse.ui.bindings">
<key
commandId="de.nittka.tooling.xarchive.ui.search"
contextId="org.eclipse.xtext.ui.XtextEditorScope"
schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
sequence="M1+X">
</key>
</extension>
</plugin>
@@ -0,0 +1,27 @@
package de.nittka.tooling.xarchive.ui.search;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.resource.IResourceDescription;
import de.nittka.tooling.xarchive.xarchive.Search;
//this class implements the actual logic of checking whether a given resource description matches the search
class XarchiveSearch {
private Search search;
public XarchiveSearch(Search search) {
this.search=search;
}
public Resource getResource(){
return search.eResource();
}
public boolean matches(IResourceDescription description, IProgressMonitor monitor){
//this is where the logic goes
//make sure to check whether the monitor has been cancelled
return true;
}
}
@@ -0,0 +1,66 @@
package de.nittka.tooling.xarchive.ui.search;
import javax.inject.Provider;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.search.ui.NewSearchUI;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.resource.EObjectAtOffsetHelper;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.ui.editor.XtextEditor;
import org.eclipse.xtext.ui.editor.utils.EditorUtils;
import org.eclipse.xtext.util.concurrent.IUnitOfWork;
import com.google.common.base.Predicates;
import com.google.inject.Inject;
import de.nittka.tooling.xarchive.xarchive.Search;
public class XarchiveSearchHandler extends AbstractHandler {
@Inject
private EObjectAtOffsetHelper eObjectAtOffsetHelper;
@Inject
private Provider<XarchiveSearchQuery> queryProvider;
//entry point adapted from FindReferencesHandler
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
try {
XtextEditor editor = EditorUtils.getActiveXtextEditor(event);
if (editor != null) {
final ITextSelection selection = (ITextSelection) editor.getSelectionProvider().getSelection();
editor.getDocument().readOnly(new IUnitOfWork.Void<XtextResource>() {
@Override
public void process(XtextResource state) throws Exception {
EObject target = eObjectAtOffsetHelper.resolveContainedElementAt(state, selection.getOffset());
//at this point we just make sure that seach is only executed on search objects
Search search=EcoreUtil2.getContainerOfType(target, Search.class);
if(search !=null){
execute(search);
}
}
});
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//the essential parts from ReferenceQueryExecutor
public void execute(Search search) {
XarchiveSearchQuery query=queryProvider.get();
String searchName=search.getId()!=null?search.getId():"unnamed Xarchive search";
//dummy initialization of ReferenceQuery
query.init(null, Predicates.alwaysTrue(), searchName);
//the essential initialization of XarchiveSearchQuery (the actual search context)
query.setSearch(search);
NewSearchUI.activateSearchResultView();
NewSearchUI.runQueryInBackground(query);
}
}
@@ -0,0 +1,90 @@
package de.nittka.tooling.xarchive.ui.search;
import java.util.Iterator;
import java.util.List;
import javax.inject.Inject;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.xtext.resource.IContainer;
import org.eclipse.xtext.resource.IContainer.Manager;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.resource.IReferenceDescription;
import org.eclipse.xtext.resource.IResourceDescription;
import org.eclipse.xtext.resource.IResourceDescriptions;
import org.eclipse.xtext.resource.IResourceServiceProvider;
import org.eclipse.xtext.resource.impl.DefaultReferenceDescription;
import org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider;
import org.eclipse.xtext.ui.editor.findrefs.ReferenceQuery;
import org.eclipse.xtext.ui.editor.findrefs.ReferenceSearchResult;
import org.eclipse.xtext.util.IAcceptor;
import de.nittka.tooling.xarchive.xarchive.Search;
import de.nittka.tooling.xarchive.xarchive.XarchivePackage;
//we derive from ReferenceQuery in order to reuse much of Xtext's search infrastructure
//in particular result presentation in the Search View, which is bound to the
//ISearchResult-implementation ReferenceSearchResult
public class XarchiveSearchQuery extends ReferenceQuery {
@Inject
private ResourceDescriptionsProvider indexProvider;
@Inject
private IResourceServiceProvider serviceProvider;
private XarchiveSearch search;
void setSearch(Search search){
this.search=new XarchiveSearch(search);
}
//we override this method in order to provider our own search results
//note that ReferenceSearchResult implements IAcceptor<IReferenceDescription>
//so we just have to create suitable IReferenceDescriptions for our own
//search results
@Override
public IStatus run(IProgressMonitor monitor) throws OperationCanceledException {
ReferenceSearchResult result=(ReferenceSearchResult)getSearchResult();
result.reset();
internalRun(monitor, result);
result.finish();
return (monitor.isCanceled()) ? Status.CANCEL_STATUS : Status.OK_STATUS;
}
private void internalRun(IProgressMonitor monitor, IAcceptor<IReferenceDescription> acceptor){
//our implementation searches the index, matching each visible IResourceDescription against the actual search logic
IResourceDescriptions index = indexProvider.getResourceDescriptions(search.getResource());
Manager containerManager = serviceProvider.getContainerManager();
List<IContainer> visibleContainer = containerManager.getVisibleContainers(serviceProvider.getResourceDescriptionManager().getResourceDescription(search.getResource()), index);
for (IContainer container : visibleContainer) {
if(!monitor.isCanceled()){
Iterator<IResourceDescription> descs = container.getResourceDescriptions().iterator();
while(descs.hasNext() && !monitor.isCanceled()){
IResourceDescription desc = descs.next();
if(search.matches(desc, monitor)){
IReferenceDescription referenceDescription=getReferenceDescriptionForMatch(desc);
//this will cause the reference description to be presented in the search view
acceptor.accept(referenceDescription);
}
}
}
}
}
private IReferenceDescription getReferenceDescriptionForMatch(IResourceDescription desc){
//a dummy ReferenceDescription for the match is created - in our case one pointing to the text region associated to the Document object
//we know that there is a corresponding EObjectDescription - we put it there;
//the URI of the resource (desc.getURI()) would work, but would not provide a nice label for the "referencing" object
Iterator<IEObjectDescription> potentialDoc = desc.getExportedObjectsByType(XarchivePackage.Literals.DOCUMENT).iterator();
if(potentialDoc.hasNext()){
IEObjectDescription doc = potentialDoc.next();
return new DefaultReferenceDescription(doc.getEObjectURI(), doc.getEObjectURI(), null, -1, null);
}else{
throw new IllegalStateException(desc.getURI()+" does not expose an Xarchive document; this is an implementation error");
}
}
}
@@ -5,7 +5,11 @@ generate xarchive "http://www.nittka.de/tooling/xarchive/Xarchive"
Model:
ArchiveConfig|Document;
ArchiveConfig: {ArchiveConfig}types+=CategoryType*;
ArchiveConfig:
{ArchiveConfig}
types+=CategoryType*
searches+=Search*
;
CategoryType: "categoriesFor" name=ID required?="required"? "{"
category+=Category (","category+=Category)*
"}"
@@ -17,6 +21,10 @@ Category: name=ID (description=STRING)?
("shortcutFor" "(" shortCuts+=ShortCut ("," shortCuts+=ShortCut)*")")
)?;
Search:
"search" ("named" id=ID)?
;
Document:
(futureDoc?="@futureDoc")?
file=DocumentFileName

0 comments on commit 21e58fa

Please sign in to comment.