]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/search/SCLSearchResultPage.java
Prime SCL BindingRegistry to shave ~0.5s from startup
[simantics/platform.git] / bundles / org.simantics.scl.ui / src / org / simantics / scl / ui / search / SCLSearchResultPage.java
1 package org.simantics.scl.ui.search;
2
3 import java.util.Collection;
4 import java.util.HashMap;
5 import java.util.HashSet;
6 import java.util.Iterator;
7 import java.util.Map;
8 import java.util.Map.Entry;
9 import java.util.Set;
10
11 import org.eclipse.jface.viewers.AbstractTreeViewer;
12 import org.eclipse.jface.viewers.DecoratingStyledCellLabelProvider;
13 import org.eclipse.jface.viewers.DecorationContext;
14 import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
15 import org.eclipse.jface.viewers.IColorProvider;
16 import org.eclipse.jface.viewers.ILabelProvider;
17 import org.eclipse.jface.viewers.ILabelProviderListener;
18 import org.eclipse.jface.viewers.ITreeContentProvider;
19 import org.eclipse.jface.viewers.OpenEvent;
20 import org.eclipse.jface.viewers.StructuredSelection;
21 import org.eclipse.jface.viewers.StyledString;
22 import org.eclipse.jface.viewers.TableViewer;
23 import org.eclipse.jface.viewers.TreeViewer;
24 import org.eclipse.jface.viewers.Viewer;
25 import org.eclipse.jface.viewers.ViewerComparator;
26 import org.eclipse.search.ui.text.AbstractTextSearchResult;
27 import org.eclipse.search.ui.text.AbstractTextSearchViewPage;
28 import org.eclipse.search.ui.text.Match;
29 import org.eclipse.swt.graphics.Color;
30 import org.eclipse.swt.graphics.Image;
31 import org.eclipse.ui.PartInitException;
32 import org.eclipse.ui.PlatformUI;
33 import org.simantics.scl.compiler.errors.Locations;
34 import org.simantics.scl.compiler.module.debug.SymbolReference;
35 import org.simantics.scl.db.SCLExpressionTableEntry;
36 import org.simantics.scl.ui.editor2.OpenSCLDefinition;
37 import org.simantics.ui.workbench.action.DefaultActions;
38
39 public class SCLSearchResultPage extends AbstractTextSearchViewPage {
40
41     private SCLSearchResultContentProvider contentProvider;
42
43     public SCLSearchResultPage() {
44         setElementLimit(-1);
45     }
46     @Override
47     protected void elementsChanged(Object[] objects) {
48         if (contentProvider != null)
49             contentProvider.elementsChanged(objects);
50     }
51
52     @Override
53     protected void clear() {
54
55     }
56
57     private static final ViewerComparator comparator = new ViewerComparator() {
58         @Override
59         public int compare(Viewer viewer, Object e1, Object e2) {
60             if (e1 instanceof SymbolReference && e2 instanceof SymbolReference) {
61                 SymbolReference r1 = (SymbolReference)e1;
62                 SymbolReference r2 = (SymbolReference)e2;
63                 int c = r1.referrer.toString().compareTo(r2.referrer.toString());
64                 if (c != 0) {
65                     return c;
66                 } else {
67                     return Integer.compare(Locations.beginOf(r1.referenceLocation), Locations.beginOf(r2.referenceLocation));
68                 }
69             } else if (e1 instanceof SymbolReference && e2 instanceof SCLExpressionTableEntry) {
70                 return 1;
71             } else if (e2 instanceof SymbolReference && e1 instanceof SCLExpressionTableEntry) {
72                 return -1;
73             } else {
74                 SCLExpressionTableEntry r1 = (SCLExpressionTableEntry)e1;
75                 SCLExpressionTableEntry r2 = (SCLExpressionTableEntry)e2;
76                 return r1.getContent().compareTo(r2.getContent());
77             }
78         }
79     };
80
81     @Override
82     protected void configureTreeViewer(TreeViewer viewer) {
83         viewer.setUseHashlookup(true);
84         contentProvider = new SCLSearchResultContentProvider(this);
85         viewer.setContentProvider(contentProvider);
86         viewer.setComparator(comparator);
87         viewer.setLabelProvider(contentProvider);
88     }
89
90     @Override
91     protected void configureTableViewer(TableViewer viewer) {
92         viewer.setUseHashlookup(true);
93         contentProvider = new SCLSearchResultContentProvider(this);
94         viewer.setContentProvider(contentProvider);
95         viewer.setComparator(comparator);
96         viewer.setLabelProvider(contentProvider);
97     }
98     
99     @Override
100     protected void handleOpen(OpenEvent event) {
101         Object selection = ((StructuredSelection)event.getSelection()).getFirstElement();
102         if (selection != null) {
103             open(selection);
104         }
105     }
106     
107     @Override
108     protected void showMatch(Match match, int currentOffset, int currentLength) throws PartInitException {
109         open(match.getElement());
110     }
111     
112     private void open(Object selection) {
113         if (selection instanceof SymbolReference) {
114             SymbolReference reference = (SymbolReference) selection;
115             OpenSCLDefinition.scheduleOpenDefinition(reference.referrer.module, reference.referenceLocation);
116         } else if (selection instanceof SCLExpressionTableEntry) {
117             SCLExpressionTableEntry entry = (SCLExpressionTableEntry) selection;
118             DefaultActions.performDefaultAction(getControl().getDisplay().getActiveShell(), new StructuredSelection(entry.getResource()));
119         }
120     }
121
122     public static class SCLSearchResultContentProvider extends DecoratingStyledCellLabelProvider implements ITreeContentProvider, ILabelProvider {
123         
124         private Map<Object, Set<Object>> fChildrenMap;
125         private AbstractTextSearchResult result;
126         private SCLSearchResultPage page;
127
128         public SCLSearchResultContentProvider(SCLSearchResultPage sclSearchResultPage) {
129             super(new SCLSearchResultLabelProvider(), PlatformUI.getWorkbench().getDecoratorManager().getLabelDecorator(), DecorationContext.DEFAULT_CONTEXT);
130             this.page = sclSearchResultPage;
131         }
132
133         @Override
134         public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
135             initialize((AbstractTextSearchResult) newInput);
136         }
137
138         protected void initialize(AbstractTextSearchResult result) {
139             this.result = result;
140             fChildrenMap= new HashMap<>();
141             if (result != null) {
142                 Object[] elements= result.getElements();
143                 for (int i= 0; i < elements.length; i++) {
144                     if (getPage().getDisplayedMatchCount(elements[i]) > 0) {
145                         insert(null, null, elements[i]);
146                     }
147                 }
148             }
149         }
150
151         private AbstractTextSearchResult getSearchResult() {
152             return result;
153         }
154         
155         public SCLSearchResultPage getPage() {
156             return page;
157         }
158
159         public void elementsChanged(Object[] updatedElements) {
160             if (getSearchResult() == null)
161                 return;
162             
163             AbstractTreeViewer viewer= (AbstractTreeViewer) getPage().getViewer();
164
165             Set<Object> toRemove= new HashSet<>();
166             Set<Object> toUpdate= new HashSet<>();
167             Map<Object, Set<Object>> toAdd= new HashMap<>();
168             
169             // TODO: Clear this for now at this point but probably this has some 
170             // side-effects once nested children can be shown, never?
171             for (Entry<Object, Set<Object>> e : fChildrenMap.entrySet()) {
172                 Collection<Object> refs = (Collection<Object>) e.getValue();
173                 refs.forEach(r -> {
174                     remove(toRemove, toUpdate, r);
175                 });
176             }
177             
178             
179             for (int i= 0; i < updatedElements.length; i++) {
180                 if (getPage().getDisplayedMatchCount(updatedElements[i]) > 0)
181                     insert(toAdd, toUpdate, updatedElements[i]);
182                 else
183                     remove(toRemove, toUpdate, updatedElements[i]);
184             }
185
186             viewer.remove(toRemove.toArray());
187             for (Iterator<Object> iter= toAdd.keySet().iterator(); iter.hasNext();) {
188                 Object parent= iter.next();
189                 HashSet<Object> children= (HashSet<Object>) toAdd.get(parent);
190                 viewer.add(parent, children.toArray());
191             }
192             for (Iterator<Object> elementsToUpdate= toUpdate.iterator(); elementsToUpdate.hasNext();) {
193                 viewer.refresh(elementsToUpdate.next());
194             }
195         }
196         
197         protected void insert(Map<Object, Set<Object>> toAdd, Set<Object> toUpdate, Object child) {
198             Object parent= getParent(child);
199             while (parent != null) {
200                 if (insertChild(parent, child)) {
201                     if (toAdd != null)
202                         insertInto(parent, child, toAdd);
203                 } else {
204                     if (toUpdate != null)
205                         toUpdate.add(parent);
206                     return;
207                 }
208                 child= parent;
209                 parent= getParent(child);
210             }
211             if (insertChild(getSearchResult(), child)) {
212                 if (toAdd != null)
213                     insertInto(getSearchResult(), child, toAdd);
214             }
215         }
216
217         private boolean insertChild(Object parent, Object child) {
218             return insertInto(parent, child, fChildrenMap);
219         }
220
221         private boolean insertInto(Object parent, Object child, Map<Object, Set<Object>> map) {
222             Set<Object> children= map.get(parent);
223             if (children == null) {
224                 children= new HashSet<>();
225                 map.put(parent, children);
226             }
227             return children.add(child);
228         }
229
230         protected void remove(Set<Object> toRemove, Set<Object> toUpdate, Object element) {
231             // precondition here:  fResult.getMatchCount(child) <= 0
232
233             if (hasChildren(element)) {
234                 if (toUpdate != null)
235                     toUpdate.add(element);
236             } else {
237                 if (getPage().getDisplayedMatchCount(element) == 0) {
238                     fChildrenMap.remove(element);
239                     Object parent= getParent(element);
240                     if (parent != null) {
241                         if (removeFromSiblings(element, parent)) {
242                             remove(toRemove, toUpdate, parent);
243                         }
244                     } else {
245                         if (removeFromSiblings(element, getSearchResult())) {
246                             if (toRemove != null)
247                                 toRemove.add(element);
248                         }
249                     }
250                 } else {
251                     if (toUpdate != null) {
252                         toUpdate.add(element);
253                     }
254                 }
255             }
256         }
257
258         /**
259          * Tries to remove the given element from the list of stored siblings.
260          * 
261          * @param element potential child
262          * @param parent potential parent
263          * @return returns true if it really was a remove (i.e. element was a child of parent).
264          */
265         private boolean removeFromSiblings(Object element, Object parent) {
266             Set<Object> siblings= fChildrenMap.get(parent);
267             if (siblings != null) {
268                 return siblings.remove(element);
269             } else {
270                 return false;
271             }
272         }
273         
274         @Override
275         public Object[] getElements(Object inputElement) {
276             return getChildren(inputElement);
277         }
278
279         @Override
280         public Object getParent(Object element) {
281             return null;
282         }
283
284         protected final Object[] EMPTY_ARR= new Object[0];
285         
286         @Override
287         public Object[] getChildren(Object parentElement) {
288             Set<Object> children= fChildrenMap.get(parentElement);
289             if (children == null)
290                 return EMPTY_ARR;
291             int limit= getPage().getElementLimit().intValue();
292             if (limit != -1 && limit < children.size()) {
293                 Object[] limitedArray= new Object[limit];
294                 Iterator<Object> iterator= children.iterator();
295                 for (int i= 0; i < limit; i++) {
296                     limitedArray[i]= iterator.next();
297                 }
298                 return limitedArray;
299             }
300
301             return children.toArray();
302         }
303
304         @Override
305         public boolean hasChildren(Object element) {
306             Set<Object> children= fChildrenMap.get(element);
307             return children != null && !children.isEmpty();
308         }
309
310         @Override
311         public String getText(Object element) {
312             if (element instanceof SymbolReference) {
313                 SymbolReference ref = (SymbolReference) element;
314                 return ref.referrer.toString();
315             } else if (element instanceof SCLExpressionTableEntry) {
316                 SCLExpressionTableEntry entry = (SCLExpressionTableEntry) element;
317                 return entry.getContent();
318             } else {
319                 return "unsupported element " + String.valueOf(element); //$NON-NLS-1$
320             }
321         }
322
323     }
324     
325     public static class SCLSearchResultLabelProvider implements ILabelProvider, IColorProvider, IStyledLabelProvider {
326
327         @Override
328         public void addListener(ILabelProviderListener listener) {
329             
330         }
331
332         @Override
333         public void dispose() {
334             
335         }
336
337         @Override
338         public boolean isLabelProperty(Object element, String property) {
339             return true;
340         }
341
342         @Override
343         public void removeListener(ILabelProviderListener listener) {
344             
345         }
346
347         @Override
348         public StyledString getStyledText(Object element) {
349             return new StyledString(getText(element)); //+ " " + ref.referred + " " + ref.referenceLocation);
350         }
351
352         @Override
353         public Color getForeground(Object element) {
354             return null;
355         }
356
357         @Override
358         public Color getBackground(Object element) {
359             return null;
360         }
361
362         @Override
363         public Image getImage(Object element) {
364             return null;
365         }
366
367         @Override
368         public String getText(Object element) {
369             if (element instanceof SymbolReference) {
370                 SymbolReference ref = (SymbolReference) element;
371                 return ref.referrer.toString();
372             } else if (element instanceof SCLExpressionTableEntry) {
373                 SCLExpressionTableEntry entry = (SCLExpressionTableEntry) element;
374                 return entry.getContent();
375             } else {
376                 return "unsupported element " + String.valueOf(element); //$NON-NLS-1$
377             }
378         }
379         
380     }
381 }