package com.redhat.ceylon.eclipse.code.search; import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getCeylonClassesOutputFolder; import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getProjectTypeChecker; import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getUnit; import static com.redhat.ceylon.eclipse.util.DocLinks.nameRegion; import static com.redhat.ceylon.eclipse.util.EditorUtil.getActivePage; import static com.redhat.ceylon.eclipse.util.JavaSearch.createSearchPattern; import static com.redhat.ceylon.eclipse.util.JavaSearch.getProjectsToSearch; import static com.redhat.ceylon.eclipse.util.JavaSearch.runSearch; import static com.redhat.ceylon.eclipse.util.InteropUtils.toJavaString; import static java.util.Arrays.asList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; 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.jdt.core.IJavaElement; import org.eclipse.jdt.core.search.SearchEngine; import org.eclipse.jdt.core.search.SearchMatch; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.internal.ui.search.NewSearchResultCollector; import org.eclipse.jface.text.IRegion; import org.eclipse.search.ui.ISearchQuery; import org.eclipse.search.ui.ISearchResult; import org.eclipse.search.ui.text.AbstractTextSearchResult; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbenchPage; import com.redhat.ceylon.compiler.typechecker.TypeChecker; import com.redhat.ceylon.compiler.typechecker.context.PhasedUnit; import com.redhat.ceylon.compiler.typechecker.io.VirtualFile; import com.redhat.ceylon.compiler.typechecker.tree.Node; import com.redhat.ceylon.compiler.typechecker.tree.Tree; import com.redhat.ceylon.eclipse.code.editor.CeylonEditor; import com.redhat.ceylon.eclipse.code.parse.CeylonParseController; import com.redhat.ceylon.eclipse.core.builder.CeylonNature; import com.redhat.ceylon.eclipse.util.Filters; import com.redhat.ceylon.ide.common.model.CeylonBinaryUnit; import com.redhat.ceylon.ide.common.model.CeylonProject; import com.redhat.ceylon.ide.common.model.IJavaModelAware; import com.redhat.ceylon.ide.common.model.IdeModule; import com.redhat.ceylon.model.typechecker.model.Declaration; import com.redhat.ceylon.model.typechecker.model.Module; import com.redhat.ceylon.model.typechecker.model.Modules; import com.redhat.ceylon.model.typechecker.model.Package; import com.redhat.ceylon.model.typechecker.model.Referenceable; import com.redhat.ceylon.model.typechecker.model.Unit; abstract class FindSearchQuery implements ISearchQuery { private Referenceable referencedDeclaration; //private final IProject project; private AbstractTextSearchResult result = new CeylonSearchResult(this); private int count = 0; private IWorkbenchPage page; private String name; private IProject project; FindSearchQuery(Referenceable referencedDeclaration, IProject project) { this.referencedDeclaration = referencedDeclaration; this.project = project; //this.project = project; this.page = getActivePage(); name = referencedDeclaration.getNameAsString(); if (referencedDeclaration instanceof Declaration) { Declaration dec = (Declaration) referencedDeclaration; if (dec.isClassOrInterfaceMember()) { Declaration classOrInterface = (Declaration) dec.getContainer(); name = classOrInterface.getName() + '.' + name; } } } private Filters filters = new Filters(); @Override public IStatus run(IProgressMonitor monitor) throws OperationCanceledException { monitor.beginTask("Searching for " + labelString() + " '" + name + "'", estimateWork(monitor)); if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } findCeylonReferences(monitor); if (referencedDeclaration instanceof Declaration && project!=null) { findJavaReferences(monitor); } monitor.done(); referencedDeclaration = null; return Status.OK_STATUS; } private void findCeylonReferences(IProgressMonitor monitor) { Set<String> searchedArchives = new HashSet<String>(); Package pack = getPackage(); if (pack==null) return; List<IProject> projectsToSearch = asList(getProjectsToSearch(this.project)); for (IProject project: projectsToSearch) { if (CeylonNature.isEnabled(project)) { TypeChecker typeChecker = getProjectTypeChecker(project); List<PhasedUnit> phasedUnits = typeChecker.getPhasedUnits() .getPhasedUnits(); findInUnits(phasedUnits, monitor); monitor.worked(1); if (monitor.isCanceled()) { throw new OperationCanceledException(); } Modules modules = typeChecker.getContext() .getModules(); for (Module mod: modules.getListOfModules()) { if (mod instanceof IdeModule && !filters.isFiltered(mod)) { IdeModule module = (IdeModule) mod; if (module.getIsCeylonArchive() && !module.getIsProjectModule() && module.getArtifact()!=null) { CeylonProject originalProject = module.getOriginalProject(); if (originalProject != null && projectsToSearch.contains( originalProject.getIdeArtifact())) { continue; } String archivePath = module.getArtifact() .getAbsolutePath(); String sourceArchivePath = toJavaString(module.getSourceArchivePath()); if (searchedArchives.add(archivePath) && searchedArchives.add(sourceArchivePath) && mod.getAllReachablePackages() .contains(pack)) { findInUnits( module.getPhasedUnitsAsJavaList(), monitor); monitor.worked(1); if (monitor.isCanceled()) { throw new OperationCanceledException(); } } } } } } } } private Package getPackage() { if (referencedDeclaration instanceof Declaration) { return referencedDeclaration.getUnit() .getPackage(); } else if (referencedDeclaration instanceof Package) { return (Package) referencedDeclaration; } else if (referencedDeclaration instanceof Module) { Module module = (Module) referencedDeclaration; return module.getRootPackage(); } else { return null; } } private int estimateWork(IProgressMonitor monitor) { int work = 0; Set<String> searchedArchives = new HashSet<String>(); Package pack = getPackage(); if (pack==null) return 0; for (IProject project: getProjectsToSearch(this.project)) { if (CeylonNature.isEnabled(project)) { work++; Modules modules = getProjectTypeChecker(project) .getContext() .getModules(); for (Module mod: modules.getListOfModules()) { if (mod instanceof IdeModule && !filters.isFiltered(mod)) { IdeModule module = (IdeModule) mod; if (module.getIsCeylonArchive() && !module.getIsProjectModule() && module.getArtifact()!=null) { String archivePath = module.getArtifact() .getAbsolutePath(); String sourceArchivePath = toJavaString(module.getSourceArchivePath()); if (searchedArchives.add(archivePath) && searchedArchives.add(sourceArchivePath) && mod.getAllReachablePackages() .contains(pack)) { work++; } } } } } } return work; } private void findJavaReferences(IProgressMonitor monitor) { Declaration declaration = (Declaration) referencedDeclaration; SearchPattern searchPattern = createSearchPattern(declaration, limitTo()); if (searchPattern==null) { return; } runSearch(monitor, new SearchEngine(), searchPattern, getProjectsToSearch(project), new NewSearchResultCollector(result, true) { @Override public void acceptSearchMatch(SearchMatch match) throws CoreException { IJavaElement enclosingElement = (IJavaElement) match.getElement(); if (!filters.isFiltered(enclosingElement)) { IJavaModelAware unit = getUnit(enclosingElement); if (unit == null) { return; } if (unit instanceof CeylonBinaryUnit) { CeylonBinaryUnit binaryUnit = (CeylonBinaryUnit) unit; if (binaryUnit.getCeylonSourceRelativePath()!=null) { // it's a class file built from Ceylon : // we should find it from the Ceylon source archives. return; } } IResource resource = match.getResource(); IFolder exploded; if (resource==null) { exploded = null; } else { exploded = getCeylonClassesOutputFolder( resource.getProject()); } if (exploded==null || !exploded.contains(resource)) { super.acceptSearchMatch(match); if (enclosingElement!=null && match.getAccuracy() !=SearchMatch.A_INACCURATE) { count++; } } } } }); } abstract int limitTo(); private void findInUnits( Iterable<? extends PhasedUnit> units, IProgressMonitor monitor) { for (PhasedUnit pu: units) { if (filters.isFiltered(pu.getPackage())) { continue; } VirtualFile unitFile = pu.getUnitFile(); monitor.subTask("Searching source file " + unitFile.getPath()); Tree.CompilationUnit rootNode = getRootNode(pu); Set<Node> nodes = getNodes(rootNode, referencedDeclaration); //TODO: should really add these as we find them: for (Node node: nodes) { if (node.getToken()==null) { //a synthetic node inserted in the tree } else { CeylonSearchMatch match = CeylonSearchMatch.create(node, rootNode, unitFile); if (node instanceof Tree.DocLink) { Tree.DocLink link = (Tree.DocLink) node; if (link.getBase() .equals(referencedDeclaration)) { IRegion r = nameRegion(link, 0); match.setOffset(r.getOffset()); match.setLength(r.getLength()); } else { for (Declaration d: link.getQualified()) { if (d.equals(referencedDeclaration)) { IRegion r = nameRegion(link, 0); match.setOffset(r.getOffset()); match.setLength(r.getLength()); } } } } result.addMatch(match); count++; } } if (monitor.isCanceled()) { throw new OperationCanceledException(); } } } Tree.CompilationUnit getRootNode(PhasedUnit pu) { for (IEditorPart editor: page.getDirtyEditors()) { if (editor instanceof CeylonEditor) { CeylonEditor ce = (CeylonEditor) editor; CeylonParseController cpc = ce.getParseController(); Unit editorUnit = cpc.getLastCompilationUnit() .getUnit(); if (/*editor.isDirty() &&*/ pu.getUnit().equals(editorUnit)) { return cpc.getLastCompilationUnit(); } } } return pu.getCompilationUnit(); } protected abstract Set<Node> getNodes( Tree.CompilationUnit cu, Referenceable referencedDeclaration); protected abstract String labelString(); @Override public ISearchResult getSearchResult() { return result; } @Override public String getLabel() { return "Displaying " + count + " " + labelString() + " '" + name + "'"; } @Override public boolean canRunInBackground() { return true; } @Override public boolean canRerun() { return false; } }