blob: 8f3009323ede77f82d054ad714498f5c72e5db7c [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2010, 2020 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;
}
}