| /*=============================================================================# |
| # Copyright (c) 2010, 2019 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.internal.r.ui.rhelp; |
| |
| import java.net.URI; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.jface.text.IInformationControlCreator; |
| |
| import org.eclipse.statet.jcommons.lang.NonNullByDefault; |
| import org.eclipse.statet.jcommons.lang.Nullable; |
| import org.eclipse.statet.jcommons.status.InterruptNullProgressMonitor; |
| import org.eclipse.statet.jcommons.status.ProgressMonitor; |
| import org.eclipse.statet.jcommons.status.StatusException; |
| |
| import org.eclipse.statet.ecommons.runtime.core.util.StatusUtils; |
| |
| import org.eclipse.statet.ltk.ast.core.util.AstSelection; |
| import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistInvocationContext; |
| import org.eclipse.statet.ltk.ui.sourceediting.assist.InfoHover; |
| import org.eclipse.statet.r.core.RCore; |
| import org.eclipse.statet.r.core.model.IRSourceUnit; |
| import org.eclipse.statet.r.core.model.RElementName; |
| import org.eclipse.statet.r.core.rsource.ast.FCall; |
| import org.eclipse.statet.r.core.rsource.ast.NodeType; |
| import org.eclipse.statet.r.core.rsource.ast.RAstNode; |
| import org.eclipse.statet.rhelp.core.REnvHelp; |
| import org.eclipse.statet.rhelp.core.RPkgHelp; |
| |
| |
| @NonNullByDefault |
| public class RHelpHover implements InfoHover { |
| |
| |
| private final int mode; |
| |
| private @Nullable IInformationControlCreator controlCreator; |
| |
| |
| public RHelpHover() { |
| this(MODE_TOOLTIP); |
| } |
| |
| public RHelpHover(final int mode) { |
| this.mode= mode; |
| } |
| |
| |
| @Override |
| public @Nullable Object getHoverInfo(final AssistInvocationContext context, |
| final ProgressMonitor m) throws StatusException { |
| m.beginSubTask("Looking up R help..."); |
| |
| final AstSelection astSelection= context.getAstSelection(); |
| if (!(astSelection.getCovering() instanceof RAstNode)) { |
| return null; |
| } |
| final IRSourceUnit rSourceUnit= (context.getSourceUnit() instanceof IRSourceUnit) ? |
| (IRSourceUnit) context.getSourceUnit() : null; |
| |
| final RAstNode rNode= (RAstNode) astSelection.getCovering(); |
| RElementName name= RHelpLtkUI.searchName(rNode, context, true); |
| if ((this.mode & MODE_FOCUS) != 0 && name == null) { |
| RAstNode parent; |
| switch (rNode.getNodeType()) { |
| case SYMBOL: |
| case STRING_CONST: |
| parent= rNode.getRParent(); |
| if (parent != null && parent.getNodeType() == NodeType.F_CALL_ARG |
| && ((FCall.Arg) parent).getNameChild() == rNode) { |
| name= RHelpLtkUI.searchNameOfFunction(parent, context); |
| } |
| break; |
| case F_CALL: |
| case F_CALL_ARGS: |
| case F_CALL_ARG: |
| name= RHelpLtkUI.searchNameOfFunction(rNode, context); |
| break; |
| default: |
| break; |
| } |
| } |
| if (name == null || m.isCanceled()) { |
| return null; |
| } |
| |
| final REnvHelp help; |
| Object helpObject= null; |
| try { |
| help= RHelpLtkUI.getEnvHelp(rSourceUnit); |
| } |
| catch (final StatusException e) { |
| return false; |
| } |
| try { |
| if (RElementName.isPackageFacetScopeType(name.getType())) { |
| helpObject= help.getPkgHelp(name.getSegmentName()); |
| } |
| else { |
| if (name.getScope() != null |
| && RElementName.isPackageFacetScopeType(name.getScope().getType()) ) { |
| final RPkgHelp pkgHelp= help.getPkgHelp(name.getScope().getSegmentName()); |
| if (pkgHelp != null) { |
| helpObject= pkgHelp.getPageForTopic(name.getSegmentName()); |
| } |
| } |
| if (helpObject== null) { |
| helpObject= RHelpLtkUI.searchTopicObject1(help, name.getSegmentName(), |
| (RAstNode) astSelection.getCovering(), rSourceUnit ); |
| if (helpObject == null && !m.isCanceled()) { |
| helpObject= (m instanceof InterruptNullProgressMonitor) ? |
| doSearch2Safe(help, name.getSegmentName(), m) : |
| RHelpLtkUI.searchTopicObject2(help, name.getSegmentName(), m); |
| } |
| } |
| } |
| } |
| catch (final CoreException e) { |
| // CANCELLED |
| return null; |
| } |
| finally { |
| help.unlock(); |
| } |
| if (helpObject == null || m.isCanceled()) { |
| return null; |
| } |
| |
| final URI httpUrl= RCore.getRHelpHttpService().toHttpUrl(helpObject, |
| RHelpLtkUI.INFO_TARGET ); |
| if (httpUrl != null) { |
| return new RHelpInfoHoverCreator.Data(context.getSourceViewer().getTextWidget(), |
| helpObject, httpUrl); |
| } |
| return null; |
| } |
| |
| /** Runs search in separate job to avoid interrupted thread in lucene */ |
| private @Nullable Object doSearch2Safe(final REnvHelp help, final String topic, final ProgressMonitor m) { |
| class SafeJob extends Job { |
| Object helpObject; |
| public SafeJob() { |
| super(String.format("Lookup R Help for '%1$s'", topic)); |
| setPriority(Job.SHORT); |
| setUser(false); |
| } |
| @Override |
| protected IStatus run(final IProgressMonitor monitor) { |
| final ProgressMonitor m= StatusUtils.convert(monitor); |
| try { |
| this.helpObject= RHelpLtkUI.searchTopicObject2(help, topic, m); |
| return Status.OK_STATUS; |
| } |
| catch (final StatusException e) { |
| return Status.CANCEL_STATUS; |
| } |
| } |
| } |
| final SafeJob job= new SafeJob(); |
| job.schedule(); |
| while (true) { |
| try { |
| job.join(); |
| return job.helpObject; |
| } |
| catch (final InterruptedException e) { |
| m.setCanceled(true); |
| } |
| if (m.isCanceled()) { |
| if (job.cancel()) { |
| return null; |
| } |
| } |
| } |
| } |
| |
| @Override |
| public IInformationControlCreator getHoverControlCreator() { |
| IInformationControlCreator controlCreator= this.controlCreator; |
| if (controlCreator == null) { |
| controlCreator= new RHelpInfoHoverCreator(this.mode); |
| this.controlCreator= controlCreator; |
| } |
| return controlCreator; |
| } |
| |
| } |