Find SCL references in SCLModuleEditor with Ctrl+Shift+G 21/1321/3
authorjsimomaa <jani.simomaa@gmail.com>
Fri, 15 Dec 2017 10:57:37 +0000 (12:57 +0200)
committerJani Simomaa <jani.simomaa@semantum.fi>
Mon, 18 Dec 2017 05:33:49 +0000 (07:33 +0200)
refs #7683

Change-Id: I553e61518a16f2b711b82f41576e07b3f99df1ba

14 files changed:
bundles/org.simantics.scl.compiler/scl/SCL/CallHierarchy.scl
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/contexts/TranslationContext.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/debug/SymbolReference.java
bundles/org.simantics.scl.ui/META-INF/MANIFEST.MF
bundles/org.simantics.scl.ui/plugin.xml
bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/editor2/FindSCLSearchAction.java [new file with mode: 0644]
bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/search/SCLSearchQuery.java [new file with mode: 0644]
bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/search/SCLSearchResult.java [new file with mode: 0644]
bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/search/SCLSearchResultPage.java [new file with mode: 0644]
features/org.simantics.scl.feature/feature.xml
features/org.simantics.scl.ui.feature/build.properties [new file with mode: 0644]
features/org.simantics.scl.ui.feature/feature.xml [new file with mode: 0644]
features/org.simantics.sdk.feature/feature.xml
features/pom.xml

index 5ab8e8141d0b463504585edc9cc9fce954daecba..4e823970e8b88594de582c6f08ab155329308dbe 100644 (file)
@@ -9,7 +9,7 @@ import "SCL/Reflection"
 data SymbolReference =
     @JavaType "org.simantics.scl.compiler.module.debug.SymbolReference"
     @FieldNames [referred, referrer, referenceLocation]
-    SymbolReference {referred :: Name, referrer :: String, referenceLocation :: Location}
+    SymbolReference {referred :: Name, referrer :: Name, referenceLocation :: Location}
 
 importJava "org.simantics.scl.compiler.module.debug.ModuleDebugInfo" where
     data ModuleDebugInfo
@@ -23,7 +23,7 @@ importJava "org.simantics.scl.compiler.module.Module" where
     
 whoCalls :: String -> String -> <Proc> [(String, String, Long)]
 whoCalls moduleName valueName = 
-    [ (callerModuleName, referrer, referenceLocation)
+    [ (callerModuleName, nameOfName referrer, referenceLocation)
     | callerModuleName <- sclModuleNames
     , Just callerModule = moduleByName callerModuleName
     , Just debugInfo = debugInfo callerModule
index b71c0ad276e69937fbde4d6be0969954b3dd151d..0e7df755c3a5eede4b52db6b355611afe3fd7708 100644 (file)
@@ -201,7 +201,7 @@ public class TranslationContext extends TypeTranslationContext implements Enviro
             if(deprecatedDescription != null)
                 errorLog.logWarning(location, "Deprecated value " + value.getName().name + "." + (deprecatedDescription.isEmpty() ? "" : " " + deprecatedDescription));
             if(moduleDebugInfo != null)
-                moduleDebugInfo.symbolReferences.add(new SymbolReference(value.getName(), definitionName, location));
+                moduleDebugInfo.symbolReferences.add(new SymbolReference(value.getName(), Name.create(compilationContext.module.getName(), definitionName), location));
             return new EConstant(location, value);
         } catch (AmbiguousNameException e) {
             if(SCLCompilerConfiguration.ALLOW_OVERLOADING)
@@ -229,7 +229,7 @@ public class TranslationContext extends TypeTranslationContext implements Enviro
                     EConstant expression = new EConstant(altValue);
                     expression.location = location;
                     if(moduleDebugInfo != null)
-                        moduleDebugInfo.symbolReferences.add(new SymbolReference(altValue.getName(), definitionName, location));
+                        moduleDebugInfo.symbolReferences.add(new SymbolReference(altValue.getName(), Name.create(compilationContext.module.getName(), definitionName), location));
                     return expression;
                 }
 
index a2bea8c597ce4fc6c8b1cff78f1988309e8fe27b..70d57e48226aec986dcfe248f12357d1accb5eed 100644 (file)
@@ -4,10 +4,10 @@ import org.simantics.scl.compiler.common.names.Name;
 
 public class SymbolReference {
     public final Name referred;
-    public final String referrer;
+    public final Name referrer;
     public final long referenceLocation;
 
-    public SymbolReference(Name referred, String referrer, long referenceLocation) {
+    public SymbolReference(Name referred, Name referrer, long referenceLocation) {
         this.referred = referred;
         this.referrer = referrer;
         this.referenceLocation = referenceLocation;
index 60603619abb95d4ea3a20d3a7ff0182f0ddea244..556475c191ce96d51b02835196308c5c0415c266 100644 (file)
@@ -15,7 +15,8 @@ Require-Bundle: org.eclipse.ui.editors;bundle-version="3.6.0",
  org.simantics.scl.compiler;bundle-version="0.6.0",
  org.junit;bundle-version="4.12.0";resolution:=optional,
  com.ibm.icu,
- org.slf4j.api
+ org.slf4j.api,
+ org.eclipse.search;bundle-version="3.11.100"
 Export-Package: org.simantics.scl.ui.console,
  org.simantics.scl.ui.editor,
  org.simantics.scl.ui.editor2,
index 56473fc29c26177f78888cf2201880000ebcb288..885fe77ed9f47b665b135741c06c5a2580b88c83 100644 (file)
             id="org.simantics.scl.editor.openDeclaration"
             name="Open Declaration">
       </command>
+      <command
+            id="org.simantics.scl.editor.findSCLSearchAction"
+            name="Find SCL references">
+      </command>
    </extension>
    <extension
          point="org.eclipse.ui.handlers">
             class="org.simantics.scl.ui.editor2.OpenDeclaration"
             commandId="org.simantics.scl.editor.openDeclaration">
       </handler>
+      <handler
+            class="org.simantics.scl.ui.editor2.FindSCLSearchAction"
+            commandId="org.simantics.scl.editor.findSCLSearchAction">
+      </handler>
    </extension>
    <extension
          point="org.eclipse.ui.bindings">
             schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
             sequence="F3">
       </key>
+      <key
+            commandId="org.simantics.scl.editor.findSCLSearchAction"
+            contextId="org.simantics.scl.ui.editor"
+            schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+            sequence="CTRL+SHIFT+G">
+      </key>
    </extension>
    <extension
          point="org.eclipse.ui.contexts">
          </description>
       </fontDefinition>
    </extension>
+   <extension
+         point="org.eclipse.search.searchResultViewPages">
+      <viewPage
+            class="org.simantics.scl.ui.search.SCLSearchResultPage"
+            id="org.simantics.scl.ui.search.sclSearchResultPage"
+            searchResultClass="org.simantics.scl.ui.search.SCLSearchResult">
+      </viewPage>
+   </extension>
 
 </plugin>
diff --git a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/editor2/FindSCLSearchAction.java b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/editor2/FindSCLSearchAction.java
new file mode 100644 (file)
index 0000000..15da419
--- /dev/null
@@ -0,0 +1,121 @@
+package org.simantics.scl.ui.editor2;
+
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.search.ui.NewSearchUI;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.PlatformUI;
+import org.simantics.scl.compiler.common.names.Name;
+import org.simantics.scl.compiler.elaboration.modules.SCLValue;
+import org.simantics.scl.compiler.errors.Locations;
+import org.simantics.scl.compiler.module.InvalidModulePathException;
+import org.simantics.scl.compiler.module.ModuleUtils;
+import org.simantics.scl.compiler.source.ModuleSource;
+import org.simantics.scl.osgi.SCLOsgi;
+import org.simantics.scl.ui.editor.completion.SCLTextEditorEnvironment;
+import org.simantics.scl.ui.search.SCLSearchQuery;
+
+public class FindSCLSearchAction extends AbstractHandler {
+
+    private static boolean isIdentifierPart(char c) {
+        return Character.isJavaIdentifierPart(c) || c=='.';
+    }
+    
+    private static String extractIdentifierAt(String text, int caretPos) {
+        int startPos = caretPos;
+        while(startPos > 0 && isIdentifierPart(text.charAt(startPos-1)))
+            --startPos;
+        int endPos = caretPos;
+        while(endPos < text.length() && isIdentifierPart(text.charAt(endPos)))
+            ++endPos;
+        return text.substring(startPos, endPos);
+    }
+    
+    private static final String SYMBOL_CHARS = "!$%&*+/<=>?@\\^|-:~.";
+    
+    private static boolean isSymbolPart(char c) {
+        for(int i=0;i<SYMBOL_CHARS.length();++i)
+            if(SYMBOL_CHARS.charAt(i) == c)
+                return true;
+        return false;
+    }
+    
+    private static String extractSymbolAt(String text, int caretPos) {
+        int startPos = caretPos;
+        while(startPos > 0 && isSymbolPart(text.charAt(startPos-1)))
+            --startPos;
+        int endPos = caretPos;
+        while(endPos < text.length() && isSymbolPart(text.charAt(endPos)))
+            ++endPos;
+        return text.substring(startPos, endPos);
+    }
+    
+    public static String extractIdentifierOrSymbolAt(String text, int caretPos) {
+        String result = extractIdentifierAt(text, caretPos);
+        if(!result.isEmpty())
+            return result;
+        return extractSymbolAt(text, caretPos);
+    }
+    
+    private static String extractLineAt(String text, int caretPos) {
+        int startPos = caretPos;
+        while(startPos > 0 && !isNewline(text.charAt(startPos-1)))
+            --startPos;
+        int endPos = caretPos;
+        while(endPos < text.length() && !isNewline(text.charAt(endPos)))
+            ++endPos;
+        return text.substring(startPos, endPos);
+    }
+    
+    private static boolean isNewline(char c) {
+        return c=='\n' || c=='\r';
+    }
+
+    @Override
+    public Object execute(ExecutionEvent event) throws ExecutionException {
+        IEditorPart editor = 
+                PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
+        if(!(editor instanceof SCLModuleEditor2))
+            return null;
+        SCLModuleEditor2 moduleEditor = (SCLModuleEditor2)editor;
+        StyledText styledText = (StyledText)moduleEditor.getAdapter(Control.class);
+        String text = styledText.getText();
+        int caretOffset = styledText.getCaretOffset();
+        
+        // Find the line where the caret is
+//        String lineAtCaret = extractLineAt(text, caretOffset);
+        // Try to find an identifier at caret
+        String identifierAtCaret = extractIdentifierOrSymbolAt(text, caretOffset);
+        if(identifierAtCaret.isEmpty())
+            return null;
+        SCLModuleEditorInput input = (SCLModuleEditorInput)moduleEditor.getEditorInput();
+        String localModuleName = input.getModuleName();
+        
+        SCLTextEditorEnvironment editorEnvironment = moduleEditor.getSCLTextEditorEnvironment();
+        editorEnvironment.updateEnvironment(moduleEditor.getDocument());
+        SCLValue value = editorEnvironment.getValue(identifierAtCaret);
+        Name name;
+        if (value == null) {
+            name = Name.create(localModuleName, identifierAtCaret);
+        } else {
+            name = value.getName();
+        }
+        SCLSearchQuery query = new SCLSearchQuery(name, localModuleName);
+        NewSearchUI.runQueryInBackground(query);
+            
+            
+            
+//            SCLTextEditorEnvironment editorEnvironment = moduleEditor.getSCLTextEditorEnvironment();
+//            editorEnvironment.updateEnvironment(moduleEditor.getDocument());
+//            SCLValue value = editorEnvironment.getValue(identifierAtCaret);
+//            //System.out.println("identifierAtCaret = " + identifierAtCaret + " [" + Locations.beginOf(value.definitionLocation) + ", " + Locations.endOf(value.definitionLocation) + "]");
+//            if(value != null)
+//                OpenSCLDefinition.openDefinition(value);
+        return null;
+    }
+
+}
diff --git a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/search/SCLSearchQuery.java b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/search/SCLSearchQuery.java
new file mode 100644 (file)
index 0000000..9f8ef5a
--- /dev/null
@@ -0,0 +1,91 @@
+package org.simantics.scl.ui.search;
+
+import java.util.ArrayList;
+
+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.search.internal.ui.text.SearchResultUpdater;
+import org.eclipse.search.ui.ISearchQuery;
+import org.eclipse.search.ui.ISearchResult;
+import org.eclipse.search.ui.text.Match;
+import org.simantics.scl.compiler.common.names.Name;
+import org.simantics.scl.compiler.errors.Failable;
+import org.simantics.scl.compiler.module.Module;
+import org.simantics.scl.compiler.module.debug.ModuleDebugInfo;
+import org.simantics.scl.compiler.module.debug.SymbolReference;
+import org.simantics.scl.compiler.module.repository.ModuleRepository;
+import org.simantics.scl.osgi.SCLOsgi;
+
+import gnu.trove.procedure.TObjectProcedure;
+
+public class SCLSearchQuery implements ISearchQuery {
+
+    private SCLSearchResult result;
+    private String moduleName;
+    private Name valueName;
+
+    public SCLSearchQuery(Name valueName, String localModuleName) {
+        this.valueName = valueName;
+        this.moduleName = localModuleName;
+    }
+    
+    @Override
+    public IStatus run(IProgressMonitor monitor) throws OperationCanceledException {
+        SCLSearchResult current = (SCLSearchResult) getSearchResult();
+        current.removeAll();
+        ModuleRepository repo = SCLOsgi.MODULE_REPOSITORY;
+        Name localName = Name.create(moduleName, valueName.name);
+        repo.getSourceRepository().forAllModules(new TObjectProcedure<String>() {
+            
+            @Override
+            public boolean execute(String moduleName) {
+                Failable<Module> failableModule = repo.getModule(moduleName);
+                if (failableModule.didSucceed()) {
+                    Module module = failableModule.getResult();
+                    ModuleDebugInfo info = module.getModuleDebugInfo();
+                    if (info != null) {
+                        ArrayList<SymbolReference> results = info.symbolReferences;
+                        for (SymbolReference ref : results) {
+                            if (ref.referred.equals(valueName) || ref.referred.equals(localName)) {
+                                result.addMatch(new Match(ref, Match.UNIT_LINE, -1, 1));
+                            }
+                        }
+                    }
+                }
+                return true;
+            }
+        });
+        return Status.OK_STATUS;
+    }
+
+    @Override
+    public String getLabel() {
+        return "Search references for ";
+    }
+
+    @Override
+    public boolean canRerun() {
+        return true;
+    }
+
+    @Override
+    public boolean canRunInBackground() {
+        return true;
+    }
+
+    @Override
+    public ISearchResult getSearchResult() {
+        if (result == null) {
+            result = new SCLSearchResult(this);
+            new SearchResultUpdater(result);
+        }
+        return result;
+    }
+
+    public String getValueName() {
+        return valueName.toString();
+    }
+
+}
diff --git a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/search/SCLSearchResult.java b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/search/SCLSearchResult.java
new file mode 100644 (file)
index 0000000..859a29d
--- /dev/null
@@ -0,0 +1,78 @@
+package org.simantics.scl.ui.search;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.search.ui.ISearchQuery;
+import org.eclipse.search.ui.text.AbstractTextSearchResult;
+import org.eclipse.search.ui.text.IEditorMatchAdapter;
+import org.eclipse.search.ui.text.IFileMatchAdapter;
+import org.eclipse.search.ui.text.Match;
+import org.eclipse.ui.IEditorPart;
+
+public class SCLSearchResult  extends AbstractTextSearchResult implements IEditorMatchAdapter, IFileMatchAdapter {
+
+    private SCLSearchQuery query;
+
+    public SCLSearchResult(SCLSearchQuery sclSearchQuery) {
+        this.query = sclSearchQuery;
+    }
+
+    @Override
+    public String getLabel() {
+        return "SCL search result label for " + query.getValueName();
+    }
+
+    @Override
+    public String getTooltip() {
+        return getLabel();
+    }
+
+    @Override
+    public ImageDescriptor getImageDescriptor() {
+        return null;
+    }
+
+    @Override
+    public ISearchQuery getQuery() {
+        return query;
+    }
+
+    @Override
+    public Match[] computeContainedMatches(AbstractTextSearchResult result, IFile file) {
+        return null;
+    }
+
+    @Override
+    public IFile getFile(Object element) {
+        return null;
+    }
+
+    @Override
+    public boolean isShownInEditor(Match match, IEditorPart editor) {
+        Object element= match.getElement();
+//        if (element instanceof IJavaElement) {
+//            element= ((IJavaElement) element).getOpenable(); // class file or compilation unit
+//            return element != null && element.equals(editor.getEditorInput().getAdapter(IJavaElement.class));
+//        } else if (element instanceof IFile) {
+//            return element.equals(editor.getEditorInput().getAdapter(IFile.class));
+//        }
+        return false;
+    }
+
+    @Override
+    public Match[] computeContainedMatches(AbstractTextSearchResult result, IEditorPart editor) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public IEditorMatchAdapter getEditorMatchAdapter() {
+        return this;
+    }
+
+    @Override
+    public IFileMatchAdapter getFileMatchAdapter() {
+        return this;
+    }
+
+}
diff --git a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/search/SCLSearchResultPage.java b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/search/SCLSearchResultPage.java
new file mode 100644 (file)
index 0000000..ebc2cbf
--- /dev/null
@@ -0,0 +1,323 @@
+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<Object, Set<Object>> 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<Object> toRemove= new HashSet<>();
+            Set<Object> toUpdate= new HashSet<>();
+            Map<Object, Set<Object>> 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<Object> iter= toAdd.keySet().iterator(); iter.hasNext();) {
+                Object parent= iter.next();
+                HashSet<Object> children= (HashSet<Object>) toAdd.get(parent);
+                viewer.add(parent, children.toArray());
+            }
+            for (Iterator<Object> elementsToUpdate= toUpdate.iterator(); elementsToUpdate.hasNext();) {
+                viewer.refresh(elementsToUpdate.next());
+            }
+        }
+        
+        protected void insert(Map<Object, Set<Object>> toAdd, Set<Object> 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<Object, Set<Object>> map) {
+            Set<Object> children= map.get(parent);
+            if (children == null) {
+                children= new HashSet<>();
+                map.put(parent, children);
+            }
+            return children.add(child);
+        }
+
+        protected void remove(Set<Object> toRemove, Set<Object> 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<Object> 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<Object> 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<Object> 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<Object> 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;
+        }
+        
+    }
+}
index c0ee33b4ef2f2ad106edabb6d6a0152686bfe504..1e0e2f98b1bd850384dd9ed70fe5d99f27d00d17 100644 (file)
@@ -136,13 +136,6 @@ This Agreement is governed by the laws of the State of New York and the intellec
          version="0.0.0"
          unpack="false"/>
 
-   <plugin
-         id="org.simantics.scl.ui"
-         download-size="0"
-         install-size="0"
-         version="0.0.0"
-         unpack="false"/>
-
    <plugin
          id="org.simantics.scl.db"
          download-size="0"
diff --git a/features/org.simantics.scl.ui.feature/build.properties b/features/org.simantics.scl.ui.feature/build.properties
new file mode 100644 (file)
index 0000000..64f93a9
--- /dev/null
@@ -0,0 +1 @@
+bin.includes = feature.xml
diff --git a/features/org.simantics.scl.ui.feature/feature.xml b/features/org.simantics.scl.ui.feature/feature.xml
new file mode 100644 (file)
index 0000000..239f21e
--- /dev/null
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+      id="org.simantics.scl.ui.feature"
+      label="Simantics SCL UI Feature"
+      version="1.0.0.qualifier">
+
+   <description url="http://www.example.com/description">
+      [Enter Feature Description here.]
+   </description>
+
+   <copyright url="http://www.example.com/copyright">
+      [Enter Copyright Description here.]
+   </copyright>
+
+   <license url="http://www.example.com/license">
+      [Enter License Description here.]
+   </license>
+
+   <includes
+         id="org.simantics.scl"
+         version="0.0.0"/>
+
+   <plugin
+         id="org.simantics.scl.ui"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.search"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.ui.editors"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.jface.text"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.ltk.core.refactoring"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.eclipse.ltk.ui.refactoring"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+</feature>
index 6de4ce19c547a90c692d4f80bbd94c4aff86f353..3c952a4d87c44cf528d95a6b9e08f4fa3b05bd0c 100644 (file)
          id="org.simantics.scl.rest.feature"
          version="0.0.0"/>
 
+   <includes
+         id="org.simantics.scl.ui.feature"
+         version="0.0.0"/>
+
    <plugin
          id="org.simantics.fileimport"
          download-size="0"
index 9ccec7a55498f471e948b29514760cdf8ed12810..972a35a3baa7f564c861b9649f03062efc235965 100644 (file)
         <module>org.simantics.platform.ui.feature</module>
         <module>org.simantics.rcp.feature</module>
         <module>org.simantics.scl.feature</module>
+        <module>org.simantics.scl.ui.feature</module>
         <module>org.simantics.scl.rest.feature</module>
         <module>org.simantics.sdk.feature</module>
         <module>org.simantics.selectionview.feature</module>