| author | szarnekow | 2009-03-07 10:34:17 (EST) |
|---|---|---|
| committer | sefftinge | 2009-03-07 10:34:17 (EST) |
| commit | 43a263198000497b75e20df49396e036dde88501 (patch) (side-by-side diff) | |
| tree | 7bce48f3f82d39d3864852b5e0c3d52b36d39ecb | |
| parent | a26a3af393dbecad7102f9b2d21929abead1aea0 (diff) | |
| download | org.eclipse.xtext-43a263198000497b75e20df49396e036dde88501.zip org.eclipse.xtext-43a263198000497b75e20df49396e036dde88501.tar.gz org.eclipse.xtext-43a263198000497b75e20df49396e036dde88501.tar.bz2 | |
Fix: Hyperlinks and go to declaration did not work with proxified EObjects (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=267366)
7 files changed, 276 insertions, 99 deletions
diff --git a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/hyperlinking/ActionBasedHyperlink.java b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/hyperlinking/ActionBasedHyperlink.java new file mode 100644 index 0000000..6e70140 --- a/dev/null +++ b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/hyperlinking/ActionBasedHyperlink.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2009 itemis AG (http://www.itemis.eu) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package org.eclipse.xtext.ui.common.editor.hyperlinking; + +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.hyperlink.IHyperlink; + +/** + * @author Sebastian Zarnekow - Initial contribution and API + */ +public final class ActionBasedHyperlink implements IHyperlink { + + private final String label; + private final OpenDeclarationAction action; + private final Region location; + + public ActionBasedHyperlink(String label, Region location, OpenDeclarationAction action) { + this.label = label; + this.location = location; + this.action = action; + } + + public OpenDeclarationAction getAction() { + return action; + } + + public IRegion getHyperlinkRegion() { + return location; + } + + public String getHyperlinkText() { + return label; + } + + public String getTypeLabel() { + return null; + } + + public void open() { + action.run(); + } +}
\ No newline at end of file diff --git a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/hyperlinking/DefaultHyperlinkDetector.java b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/hyperlinking/DefaultHyperlinkDetector.java index cda351d..acb94b8 100644 --- a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/hyperlinking/DefaultHyperlinkDetector.java +++ b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/hyperlinking/DefaultHyperlinkDetector.java @@ -8,38 +8,17 @@ *******************************************************************************/ package org.eclipse.xtext.ui.common.editor.hyperlinking; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.apache.log4j.Logger; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; -import org.eclipse.core.runtime.Assert; -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.EReference; -import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextViewer; -import org.eclipse.jface.text.Region; import org.eclipse.jface.text.hyperlink.IHyperlink; import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; -import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.widgets.Control; import org.eclipse.ui.handlers.HandlerUtil; -import org.eclipse.xtext.Assignment; import org.eclipse.xtext.CrossReference; -import org.eclipse.xtext.GrammarUtil; -import org.eclipse.xtext.crossref.ILinkingService; -import org.eclipse.xtext.crossref.impl.IllegalNodeException; -import org.eclipse.xtext.parser.IParseResult; -import org.eclipse.xtext.parsetree.AbstractNode; -import org.eclipse.xtext.parsetree.NodeUtil; -import org.eclipse.xtext.parsetree.ParseTreeUtil; import org.eclipse.xtext.resource.XtextResource; -import org.eclipse.xtext.ui.core.ILocationInFileProvider; import org.eclipse.xtext.ui.core.editor.XtextEditor; import org.eclipse.xtext.ui.core.editor.model.IXtextDocument; import org.eclipse.xtext.ui.core.editor.model.UnitOfWork; @@ -57,60 +36,22 @@ import com.google.inject.Inject; */ public class DefaultHyperlinkDetector extends org.eclipse.core.commands.AbstractHandler implements IHyperlinkDetector { - private final ILinkingService linkingService; - private final ILocationInFileProvider locationProvider; - private final ILabelProvider labelProvider; - + private final HyperlinkHelper helper; @Inject - public DefaultHyperlinkDetector(ILinkingService linkingService, ILocationInFileProvider locationProvider, ILabelProvider labelProvider) { + public DefaultHyperlinkDetector(HyperlinkHelper helper) { super(); - this.linkingService = linkingService; - this.locationProvider = locationProvider; - this.labelProvider = labelProvider; + this.helper = helper; } - // logger available to subclasses - protected final Logger logger = Logger.getLogger(getClass()); - /* * (non-Javadoc) * @see org.eclipse.jface.text.hyperlink.IHyperlinkDetector#detectHyperlinks(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion, boolean) */ - public IHyperlink[] detectHyperlinks(ITextViewer textViewer, final IRegion region, boolean canShowMultipleHyperlinks) { + public IHyperlink[] detectHyperlinks(ITextViewer textViewer, final IRegion region, final boolean canShowMultipleHyperlinks) { return ((IXtextDocument)textViewer.getDocument()).readOnly(new UnitOfWork<IHyperlink[]>() { public IHyperlink[] exec(XtextResource resource) throws Exception { - IParseResult parseResult = resource.getParseResult(); - Assert.isNotNull(parseResult); - AbstractNode abstractNode = ParseTreeUtil.getCurrentOrFollowingNodeByOffset(parseResult.getRootNode(), - region.getOffset()); - final Region location = new Region(abstractNode.getOffset(), abstractNode.getLength()); - List<EObject> crossLinkedEObjects = findCrossLinkedEObject(abstractNode); - if (crossLinkedEObjects.isEmpty()) - return null; - List<IHyperlink> links = new ArrayList<IHyperlink>(); - for (EObject crossReffed : crossLinkedEObjects) { - final String label = labelProvider.getText(crossReffed); - final URI uri = EcoreUtil.getURI(crossReffed); - final URI normalized = crossReffed.eResource().getResourceSet().getURIConverter().normalize(uri); - links.add(new IHyperlink() { - - public IRegion getHyperlinkRegion() { - return location; - } - public String getHyperlinkText() { - return label; - } - public String getTypeLabel() { - return null; - } - public void open() { - new OpenDeclarationAction(normalized, locationProvider).run(); - } - - }); - } - return links.toArray(new IHyperlink[links.size()]); + return helper.createHyperlinksByOffset(resource, region.getOffset(), canShowMultipleHyperlinks); } }); } @@ -120,35 +61,13 @@ public class DefaultHyperlinkDetector extends org.eclipse.core.commands.Abstract final IXtextDocument document = activeEditor.getDocument(); final int offset = ((StyledText) activeEditor.getAdapter(Control.class)).getCaretOffset(); document.readOnly(new UnitOfWork<Object>() { - public Object exec(XtextResource resource) throws Exception { - AbstractNode node = ParseTreeUtil.getCurrentOrFollowingNodeByOffset(resource - .getParseResult().getRootNode(), offset); - List<EObject> crossLinkedEObject = findCrossLinkedEObject(node); - if (crossLinkedEObject.isEmpty()) - return null; - URI uri = EcoreUtil.getURI(crossLinkedEObject.get(0)); - new OpenDeclarationAction(uri, locationProvider).run(); + public Void exec(XtextResource resource) throws Exception { + OpenDeclarationAction action = helper.getOpenDeclarationAction(resource, offset); + action.run(); return null; } }); return this; } - protected List<EObject> findCrossLinkedEObject(AbstractNode node) { - AbstractNode nodeToCheck = node; - while(nodeToCheck != null && !(nodeToCheck.getGrammarElement() instanceof Assignment)) { - if (nodeToCheck.getGrammarElement() instanceof CrossReference) { - EObject semanticModel = NodeUtil.getNearestSemanticObject(nodeToCheck); - EReference eReference = GrammarUtil.getReference((CrossReference) nodeToCheck.getGrammarElement(), - semanticModel.eClass()); - try { - return linkingService.getLinkedObjects(semanticModel, eReference, nodeToCheck); - } catch (IllegalNodeException ex) { - return Collections.emptyList(); - } - } - nodeToCheck = nodeToCheck.getParent(); - } - return Collections.emptyList(); - } } diff --git a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/hyperlinking/HyperlinkHelper.java b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/hyperlinking/HyperlinkHelper.java new file mode 100644 index 0000000..c681c48 --- a/dev/null +++ b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/hyperlinking/HyperlinkHelper.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2009 itemis AG (http://www.itemis.eu) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package org.eclipse.xtext.ui.common.editor.hyperlinking; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.resource.URIConverter; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.hyperlink.IHyperlink; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.xtext.Assignment; +import org.eclipse.xtext.CrossReference; +import org.eclipse.xtext.GrammarUtil; +import org.eclipse.xtext.crossref.ILinkingService; +import org.eclipse.xtext.crossref.impl.IllegalNodeException; +import org.eclipse.xtext.parser.IParseResult; +import org.eclipse.xtext.parsetree.AbstractNode; +import org.eclipse.xtext.parsetree.NodeUtil; +import org.eclipse.xtext.parsetree.ParseTreeUtil; +import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.ui.core.ILocationInFileProvider; +import org.eclipse.xtext.util.Wrapper; + +import com.google.inject.Inject; + +/** + * @author Sebastian Zarnekow - Initial contribution and API + */ +public class HyperlinkHelper { + + private final ILinkingService linkingService; + private final ILocationInFileProvider locationProvider; + private final ILabelProvider labelProvider; + + @Inject + public HyperlinkHelper(ILinkingService linkingService, ILocationInFileProvider locationProvider, ILabelProvider labelProvider) { + super(); + this.linkingService = linkingService; + this.locationProvider = locationProvider; + this.labelProvider = labelProvider; + } + + public ActionBasedHyperlink[] createHyperlinksByOffset(XtextResource resource, int offset, boolean createMultipleHyperlinks) { + IParseResult parseResult = resource.getParseResult(); + Assert.isNotNull(parseResult); + AbstractNode abstractNode = ParseTreeUtil.getCurrentOrFollowingNodeByOffset(parseResult.getRootNode(), offset); + final Wrapper<Region> location = Wrapper.wrap(new Region(abstractNode.getOffset(), abstractNode.getLength())); + List<EObject> crossLinkedEObjects = findCrossLinkedEObject(abstractNode, location); + if (crossLinkedEObjects.isEmpty()) + return null; + final URIConverter uriConverter = resource.getResourceSet().getURIConverter(); + List<IHyperlink> links = new ArrayList<IHyperlink>(); + for (EObject crossReffed : crossLinkedEObjects) { + if (!links.isEmpty() && !createMultipleHyperlinks) + break; + final String label = labelProvider.getText(crossReffed); + final URI uri = EcoreUtil.getURI(crossReffed); + final URI normalized = uriConverter.normalize(uri); + links.add(new ActionBasedHyperlink(label, location.get(), new OpenDeclarationAction(normalized, locationProvider))); + } + return links.toArray(new ActionBasedHyperlink[links.size()]); + } + + public OpenDeclarationAction getOpenDeclarationAction(XtextResource resource, int offset) { + AbstractNode node = ParseTreeUtil.getCurrentOrFollowingNodeByOffset(resource.getParseResult().getRootNode(), offset); + List<EObject> crossLinkedEObject = findCrossLinkedEObject(node, null); + if (crossLinkedEObject.isEmpty()) + return null; + final URI uri = EcoreUtil.getURI(crossLinkedEObject.get(0)); + final URI normalized = resource.getResourceSet().getURIConverter().normalize(uri); + return new OpenDeclarationAction(normalized, locationProvider); + } + + protected List<EObject> findCrossLinkedEObject(AbstractNode node, Wrapper<Region> location) { + AbstractNode nodeToCheck = node; + while(nodeToCheck != null && !(nodeToCheck.getGrammarElement() instanceof Assignment)) { + if (nodeToCheck.getGrammarElement() instanceof CrossReference) { + EObject semanticModel = NodeUtil.getNearestSemanticObject(nodeToCheck); + EReference eReference = GrammarUtil.getReference((CrossReference) nodeToCheck.getGrammarElement(), + semanticModel.eClass()); + try { + if (location != null) + location.set(new Region(nodeToCheck.getOffset(), nodeToCheck.getLength())); + return linkingService.getLinkedObjects(semanticModel, eReference, nodeToCheck); + } catch (IllegalNodeException ex) { + return Collections.emptyList(); + } + } + nodeToCheck = nodeToCheck.getParent(); + } + return Collections.emptyList(); + } +} diff --git a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/hyperlinking/OpenDeclarationAction.java b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/hyperlinking/OpenDeclarationAction.java index 13f72b9..19db946 100644 --- a/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/hyperlinking/OpenDeclarationAction.java +++ b/plugins/org.eclipse.xtext.ui.common/src/org/eclipse/xtext/ui/common/editor/hyperlinking/OpenDeclarationAction.java @@ -21,9 +21,9 @@ import org.eclipse.xtext.ui.core.editor.model.UnitOfWork; /** * This action opens a <code>XtextEditor</code> on a selected <code>CrossReference</code> element. - * + * * @author Michael Clay - Initial contribution and API - * + * * @see org.eclipse.jface.action.Action */ public class OpenDeclarationAction extends Action { @@ -31,10 +31,9 @@ public class OpenDeclarationAction extends Action { // logger available to subclasses protected final Logger logger = Logger.getLogger(getClass()); - private URI uri; + private final URI uri; - private ILocationInFileProvider locationProvider; - + private final ILocationInFileProvider locationProvider; public OpenDeclarationAction(URI uri, ILocationInFileProvider locationProvider) { super(); @@ -46,7 +45,11 @@ public class OpenDeclarationAction extends Action { public void run() { doOpen(uri); } - + + public URI getURI() { + return uri; + } + public void doOpen(final URI uri) { IFile file = getContainingResourceSetFile(uri); IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); @@ -73,9 +76,9 @@ public class OpenDeclarationAction extends Action { partInitException); } } - + private IFile getContainingResourceSetFile(URI uri) { - IFile targetFile = uri.isPlatformResource() ? + IFile targetFile = uri.isPlatformResource() ? ResourcesPlugin.getWorkspace().getRoot().getFile( new Path(uri.toPlatformString(true))) : ResourcesPlugin.getWorkspace().getRoot().getFileForLocation( diff --git a/tests/org.eclipse.xtext.ui.common.tests/META-INF/MANIFEST.MF b/tests/org.eclipse.xtext.ui.common.tests/META-INF/MANIFEST.MF index 7290049..3c11294 100644 --- a/tests/org.eclipse.xtext.ui.common.tests/META-INF/MANIFEST.MF +++ b/tests/org.eclipse.xtext.ui.common.tests/META-INF/MANIFEST.MF @@ -16,6 +16,7 @@ Require-Bundle: org.eclipse.ui, org.easymock;bundle-version="2.3.0", org.eclipse.jface.text;bundle-version="3.4.0", org.eclipse.xtext.ui.core;bundle-version="0.7.0", + org.eclipse.xtext.xtext.ui, org.eclipse.xtext.xtend;bundle-version="0.7.0", org.eclipse.xtext.example.domainmodel;bundle-version="0.7.0", org.eclipse.xtext.example.domainmodel.ui;bundle-version="0.7.0", diff --git a/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/editor/hyperlinking/HyperlinkHelperTest.java b/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/editor/hyperlinking/HyperlinkHelperTest.java new file mode 100644 index 0000000..e803c60 --- a/dev/null +++ b/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/editor/hyperlinking/HyperlinkHelperTest.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2009 itemis AG (http://www.itemis.eu) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package org.eclipse.xtext.ui.common.editor.hyperlinking; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.xtext.Grammar; +import org.eclipse.xtext.ISetup; +import org.eclipse.xtext.XtextRuntimeModule; +import org.eclipse.xtext.XtextStandaloneSetup; +import org.eclipse.xtext.junit.AbstractXtextTests; +import org.eclipse.xtext.resource.ClasspathUriUtil; +import org.eclipse.xtext.resource.XtextResource; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * @author Sebastian Zarnekow - Initial contribution and API + */ +public class HyperlinkHelperTest extends AbstractXtextTests { + + private ISetup getSetup() { + return new XtextStandaloneSetup() { + @Override + public Injector createInjector() { + return Guice.createInjector(new XtextRuntimeModule(), new org.eclipse.xtext.XtextUiModule()); + } + }; + } + + private HyperlinkHelper helper; + private XtextResource resource; + private Grammar grammar; + private Grammar terminalGrammar; + private String model; + + @Override + protected void setUp() throws Exception { + with(getSetup()); + helper = get(HyperlinkHelper.class); + model = "grammar org.eclipse.xtext.ui.common.HyperlinkTest with org.eclipse.xtext.common.Terminals\n" + + "generate hyperlinkTest 'http://www.eclipse.org/Xtext/2008/HyperlinkTest'\n" + + "Model: name=STRING;"; + resource = getResourceFromString(model); + grammar = (Grammar) resource.getContents().get(0); + terminalGrammar = grammar.getUsedGrammars().get(0); + terminalGrammar.eResource().unload(); + } + + @Override + protected void tearDown() throws Exception { + resource = null; + helper = null; + grammar = null; + terminalGrammar = null; + model = null; + super.tearDown(); + } + + public void testSetup() { + assertTrue(resource.getErrors().isEmpty()); + assertNull(terminalGrammar.eResource()); + assertTrue(terminalGrammar.eIsProxy()); + assertNotNull(helper); + } + + public void testCreateHyperlinksByOffset() { + ActionBasedHyperlink[] links = helper.createHyperlinksByOffset(resource, model.indexOf("common.Terminals"), true); + assertNotNull(links); + assertEquals(1, links.length); + OpenDeclarationAction action = links[0].getAction(); + checkAction(action); + } + + private void checkAction(OpenDeclarationAction action) { + assertNotNull(action); + URI uri = action.getURI(); + assertNotNull(uri); + assertFalse(ClasspathUriUtil.isClasspathUri(uri)); + assertTrue(ClasspathUriUtil.isClasspathUri(EcoreUtil.getURI(terminalGrammar))); + EObject obj = grammar.eResource().getResourceSet().getEObject(uri, true); + assertNotNull(obj); + Grammar terminalGrammar = grammar.getUsedGrammars().get(0); + assertFalse(terminalGrammar.eIsProxy()); + assertEquals(terminalGrammar, obj); + } + + public void testGetOpenDeclarationAction() { + OpenDeclarationAction action = helper.getOpenDeclarationAction(resource, model.indexOf("common.Terminals")); + checkAction(action); + } + +} diff --git a/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/tests/AllTests.java b/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/tests/AllTests.java index e69b543..ead6287 100644 --- a/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/tests/AllTests.java +++ b/tests/org.eclipse.xtext.ui.common.tests/src/org/eclipse/xtext/ui/common/tests/AllTests.java @@ -12,11 +12,12 @@ import junit.framework.Test; import junit.framework.TestSuite; import org.eclipse.xtext.ui.common.editor.contentassist.impl.DefaultContentAssistProcessorTest; +import org.eclipse.xtext.ui.common.editor.hyperlinking.HyperlinkHelperTest; import org.eclipse.xtext.ui.common.editor.outline.impl.DefaultSemanticModelTransformerTest; /** * @author Dennis Hübner - Initial contribution and API - * + * */ public class AllTests { @@ -24,8 +25,8 @@ public class AllTests { TestSuite suite = new TestSuite("Test for org.eclipse.xtext.ui.common.tests"); // $JUnit-BEGIN$ suite.addTestSuite(DefaultContentAssistProcessorTest.class); - suite.addTestSuite(DefaultSemanticModelTransformerTest.class); + suite.addTestSuite(HyperlinkHelperTest.class); // $JUnit-END$ return suite; } |

