package org.simantics.scl.ui.search; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.eclipse.jface.viewers.AbstractTreeViewer; import org.eclipse.jface.viewers.DecoratingStyledCellLabelProvider; import org.eclipse.jface.viewers.DecorationContext; import org.eclipse.jface.viewers.IBaseLabelProvider; import org.eclipse.jface.viewers.IColorProvider; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.OpenEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.StyledString; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider; import org.eclipse.search.ui.text.AbstractTextSearchResult; import org.eclipse.search.ui.text.AbstractTextSearchViewPage; import org.eclipse.search.ui.text.Match; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Image; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.simantics.scl.compiler.module.debug.SymbolReference; import org.simantics.scl.ui.editor2.OpenDeclaration; import org.simantics.scl.ui.editor2.OpenSCLDefinition; public class SCLSearchResultPage extends AbstractTextSearchViewPage { private SCLSearchResultContentProvider contentProvider; public SCLSearchResultPage() { setElementLimit(-1); } @Override protected void elementsChanged(Object[] objects) { if (contentProvider != null) contentProvider.elementsChanged(objects); } @Override protected void clear() { } private static final ViewerComparator comparator = new ViewerComparator((o1, o2) -> o1.compareTo(o2)); @Override protected void configureTreeViewer(TreeViewer viewer) { viewer.setUseHashlookup(true); contentProvider = new SCLSearchResultContentProvider(this); viewer.setContentProvider(contentProvider); viewer.setComparator(comparator); viewer.setLabelProvider(contentProvider); } @Override protected void configureTableViewer(TableViewer viewer) { viewer.setUseHashlookup(true); contentProvider = new SCLSearchResultContentProvider(this); viewer.setContentProvider(contentProvider); viewer.setComparator(comparator); viewer.setLabelProvider(contentProvider); } @Override protected void handleOpen(OpenEvent event) { Object selection = ((StructuredSelection)event.getSelection()).getFirstElement(); if (selection != null) { SymbolReference reference = (SymbolReference) selection; OpenSCLDefinition.openDefinition(reference.referrer.module, reference.referenceLocation); } } @Override protected void showMatch(Match match, int currentOffset, int currentLength) throws PartInitException { SymbolReference reference = (SymbolReference) match.getElement(); OpenSCLDefinition.openDefinition(reference.referrer.module, reference.referenceLocation); } public static class SCLSearchResultContentProvider extends DecoratingStyledCellLabelProvider implements ITreeContentProvider, ILabelProvider { private Map> fChildrenMap; private AbstractTextSearchResult result; private SCLSearchResultPage page; public SCLSearchResultContentProvider(SCLSearchResultPage sclSearchResultPage) { super(new SCLSearchResultLabelProvider(), PlatformUI.getWorkbench().getDecoratorManager().getLabelDecorator(), DecorationContext.DEFAULT_CONTEXT); this.page = sclSearchResultPage; } @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { initialize((AbstractTextSearchResult) newInput); } protected void initialize(AbstractTextSearchResult result) { this.result = result; fChildrenMap= new HashMap<>(); if (result != null) { Object[] elements= result.getElements(); for (int i= 0; i < elements.length; i++) { if (getPage().getDisplayedMatchCount(elements[i]) > 0) { insert(null, null, elements[i]); } } } } private AbstractTextSearchResult getSearchResult() { return result; } public SCLSearchResultPage getPage() { return page; } public void elementsChanged(Object[] updatedElements) { if (getSearchResult() == null) return; AbstractTreeViewer viewer= (AbstractTreeViewer) getPage().getViewer(); Set toRemove= new HashSet<>(); Set toUpdate= new HashSet<>(); Map> toAdd= new HashMap<>(); for (int i= 0; i < updatedElements.length; i++) { if (getPage().getDisplayedMatchCount(updatedElements[i]) > 0) insert(toAdd, toUpdate, updatedElements[i]); else remove(toRemove, toUpdate, updatedElements[i]); } viewer.remove(toRemove.toArray()); for (Iterator iter= toAdd.keySet().iterator(); iter.hasNext();) { Object parent= iter.next(); HashSet children= (HashSet) toAdd.get(parent); viewer.add(parent, children.toArray()); } for (Iterator elementsToUpdate= toUpdate.iterator(); elementsToUpdate.hasNext();) { viewer.refresh(elementsToUpdate.next()); } } protected void insert(Map> toAdd, Set toUpdate, Object child) { Object parent= getParent(child); while (parent != null) { if (insertChild(parent, child)) { if (toAdd != null) insertInto(parent, child, toAdd); } else { if (toUpdate != null) toUpdate.add(parent); return; } child= parent; parent= getParent(child); } if (insertChild(getSearchResult(), child)) { if (toAdd != null) insertInto(getSearchResult(), child, toAdd); } } private boolean insertChild(Object parent, Object child) { return insertInto(parent, child, fChildrenMap); } private boolean insertInto(Object parent, Object child, Map> map) { Set children= map.get(parent); if (children == null) { children= new HashSet<>(); map.put(parent, children); } return children.add(child); } protected void remove(Set toRemove, Set toUpdate, Object element) { // precondition here: fResult.getMatchCount(child) <= 0 if (hasChildren(element)) { if (toUpdate != null) toUpdate.add(element); } else { if (getPage().getDisplayedMatchCount(element) == 0) { fChildrenMap.remove(element); Object parent= getParent(element); if (parent != null) { if (removeFromSiblings(element, parent)) { remove(toRemove, toUpdate, parent); } } else { if (removeFromSiblings(element, getSearchResult())) { if (toRemove != null) toRemove.add(element); } } } else { if (toUpdate != null) { toUpdate.add(element); } } } } /** * Tries to remove the given element from the list of stored siblings. * * @param element potential child * @param parent potential parent * @return returns true if it really was a remove (i.e. element was a child of parent). */ private boolean removeFromSiblings(Object element, Object parent) { Set siblings= fChildrenMap.get(parent); if (siblings != null) { return siblings.remove(element); } else { return false; } } @Override public Object[] getElements(Object inputElement) { return getChildren(inputElement); } @Override public Object getParent(Object element) { return null; } protected final Object[] EMPTY_ARR= new Object[0]; @Override public Object[] getChildren(Object parentElement) { Set children= fChildrenMap.get(parentElement); if (children == null) return EMPTY_ARR; int limit= getPage().getElementLimit().intValue(); if (limit != -1 && limit < children.size()) { Object[] limitedArray= new Object[limit]; Iterator iterator= children.iterator(); for (int i= 0; i < limit; i++) { limitedArray[i]= iterator.next(); } return limitedArray; } return children.toArray(); } @Override public boolean hasChildren(Object element) { Set children= fChildrenMap.get(element); return children != null && !children.isEmpty(); } @Override public String getText(Object element) { SymbolReference ref = (SymbolReference) element; return ref.referrer.toString(); } } public static class SCLSearchResultLabelProvider implements ILabelProvider, IColorProvider, IStyledLabelProvider { @Override public void addListener(ILabelProviderListener listener) { } @Override public void dispose() { } @Override public boolean isLabelProperty(Object element, String property) { return true; } @Override public void removeListener(ILabelProviderListener listener) { } @Override public StyledString getStyledText(Object element) { SymbolReference ref = (SymbolReference) element; return new StyledString(ref.referrer.toString()); //+ " " + ref.referred + " " + ref.referenceLocation); } @Override public Color getForeground(Object element) { return null; } @Override public Color getBackground(Object element) { return null; } @Override public Image getImage(Object element) { return null; } @Override public String getText(Object element) { return null; } } }