blob: b133ab4cb20fd208ee210d06caf561080598f1b3 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2010, 2018 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.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.statet.jcommons.text.core.TextRegion;
import org.eclipse.statet.ltk.ast.core.util.AstSelection;
import org.eclipse.statet.ltk.model.core.elements.ISourceUnit;
import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistInvocationContext;
import org.eclipse.statet.ltk.ui.sourceediting.assist.IInfoHover;
import org.eclipse.statet.r.core.IRCoreAccess;
import org.eclipse.statet.r.core.RCore;
import org.eclipse.statet.r.core.model.IRFrame;
import org.eclipse.statet.r.core.model.IRFrameInSource;
import org.eclipse.statet.r.core.model.IRSourceUnit;
import org.eclipse.statet.r.core.model.RElementAccess;
import org.eclipse.statet.r.core.model.RElementName;
import org.eclipse.statet.r.core.model.RModel;
import org.eclipse.statet.r.core.renv.IREnv;
import org.eclipse.statet.r.core.rhelp.IREnvHelp;
import org.eclipse.statet.r.core.rhelp.IRHelpManager;
import org.eclipse.statet.r.core.rhelp.IRHelpPage;
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;
public class RHelpHover implements IInfoHover {
private final int mode;
public RHelpHover() {
this(MODE_TOOLTIP);
}
public RHelpHover(final int mode) {
this.mode= mode;
}
@Override
public Object getHoverInfo(final AssistInvocationContext context) {
final AstSelection selection= context.getAstSelection();
if (!(selection.getCovering() instanceof RAstNode)) {
return null;
}
final RAstNode rNode= (RAstNode) selection.getCovering();
RElementName name= searchName(rNode, context, true);
if (Thread.interrupted()) {
return null;
}
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= searchNameOfFunction(parent, context);
}
break;
case F_CALL:
case F_CALL_ARGS:
case F_CALL_ARG:
name= searchNameOfFunction(rNode, context);
break;
default:
break;
}
if (Thread.interrupted()) {
return null;
}
}
if (name == null) {
return null;
}
final IRCoreAccess rCoreAccess= (context.getSourceUnit() instanceof IRSourceUnit) ?
((IRSourceUnit) context.getSourceUnit()).getRCoreAccess() :
RCore.WORKBENCH_ACCESS;
IREnv rEnv= rCoreAccess.getREnv();
if (rEnv == null) {
rEnv= RCore.WORKBENCH_ACCESS.getREnv();
}
Object helpObject= null;
final IRHelpManager rHelpManager= RCore.getRHelpManager();
final IREnvHelp help= rHelpManager.getHelp(rEnv);
if (help != null) {
try {
if (RElementName.isPackageFacetScopeType(name.getType())) {
helpObject= help.getPkgHelp(name.getSegmentName());
}
else {
if (name.getScope() != null
&& RElementName.isPackageFacetScopeType(name.getScope().getType()) ) {
helpObject= help.getPageForTopic(name.getScope().getSegmentName(),
name.getSegmentName() );
}
if (helpObject== null) {
final List<IRHelpPage> topics= help.getPagesForTopic(name.getSegmentName());
if (topics == null || topics.isEmpty()) {
return null;
}
if (topics.size() == 1) {
helpObject= topics.get(0);
}
else {
final IRFrameInSource frame= RModel.searchFrame((RAstNode) selection.getCovering());
if (frame == null) {
return null;
}
helpObject= searchFrames(topics, RModel.createDirectFrameList(frame));
final ISourceUnit su= context.getSourceUnit();
if (helpObject == null && su instanceof IRSourceUnit) {
helpObject= searchFrames(topics,
RModel.createProjectFrameList(null, (IRSourceUnit) su) );
}
}
}
}
}
catch (final CoreException e) {
// CANCELLED
return null;
}
finally {
help.unlock();
}
}
if (Thread.interrupted() || helpObject == null) {
return null;
}
final String httpUrl= rHelpManager.toHttpUrl(helpObject, RHelpUIServlet.INFO_TARGET);
if (httpUrl != null) {
return new RHelpInfoHoverCreator.Data(context.getSourceViewer().getTextWidget(),
helpObject, httpUrl);
}
return null;
}
private IRHelpPage searchFrames(final List<IRHelpPage> helpPages, final List<IRFrame> frames) {
if (frames == null) {
return null;
}
for (final IRFrame frame : frames) {
if (frame.getFrameType() == IRFrame.PACKAGE) {
for (final IRHelpPage helpPage : helpPages) {
if (helpPage.getPackage().getName().equals(
frame.getElementName().getSegmentName() )) {
return helpPage;
}
}
}
}
return null;
}
@Override
public IInformationControlCreator getHoverControlCreator() {
return new RHelpInfoHoverCreator(this.mode);
}
static RElementName searchName(RAstNode rNode, final TextRegion region, final boolean checkInterrupted) {
RElementAccess access= null;
while (rNode != null && access == null) {
if (checkInterrupted && Thread.currentThread().isInterrupted()) {
return null;
}
final List<Object> attachments= rNode.getAttachments();
for (final Object attachment : attachments) {
if (attachment instanceof RElementAccess) {
access= (RElementAccess) attachment;
final IRFrame frame= access.getFrame();
if ((frame != null && frame.getFrameType() != IRFrame.FUNCTION)
|| (RElementName.isPackageFacetScopeType(access.getType())) ) {
final RElementName e= getElementAccessOfRegion(access, region);
if (e != null) {
return e;
}
}
}
}
rNode= rNode.getRParent();
}
return null;
}
static RElementName searchNameOfFunction(RAstNode rNode, final TextRegion region) {
while (rNode != null) {
if (rNode.getNodeType() == NodeType.F_CALL) {
final FCall fcall= (FCall) rNode;
if (fcall.getArgsOpenOffset() != Integer.MIN_VALUE
&& fcall.getArgsOpenOffset() <= region.getStartOffset()) {
final List<Object> attachments= ((FCall) rNode).getAttachments();
for (final Object attachment : attachments) {
if (attachment instanceof RElementAccess) {
final RElementAccess access= (RElementAccess) attachment;
final IRFrame frame= access.getFrame();
if (access.getNode() == fcall
&& frame != null && frame.getFrameType() != IRFrame.FUNCTION
&& access.getNextSegment() == null) {
final RElementName fName= RElementName.normalize(access);
if (RElementName.isRegularMainType(fName.getType())) {
return fName;
}
}
}
}
}
return null;
}
rNode= rNode.getRParent();
}
return null;
}
static RElementName getElementAccessOfRegion(final RElementAccess access, final TextRegion region) {
if (access.getSegmentName() == null) {
return null;
}
int segmentCount= 0;
RElementAccess current= access;
while (current != null) {
segmentCount++;
final RAstNode nameNode= current.getNameNode();
if (nameNode != null
&& nameNode.getStartOffset() <= region.getStartOffset()
&& nameNode.getEndOffset() >= region.getEndOffset() ) {
if (RElementName.isRegularMainType(access.getType())) {
return access;
}
if (segmentCount == 1) {
if (RElementName.isPackageFacetScopeType(access.getType())) {
return access;
}
}
else /* (segmentCount > 1) */ {
if (RElementName.isPackageFacetScopeType(access.getType())
&& RElementName.isRegularMainType(access.getNextSegment().getType())
&& access.getNextSegment().getSegmentName() != null) {
return RElementName.normalize(access);
}
}
return null;
}
current= current.getNextSegment();
}
return null;
}
}