/*=============================================================================#
# Copyright (c) 2010-2016 Stephan Wahlbrink (WalWare.de) 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
#
# Contributors:
# Stephan Wahlbrink - initial API and implementation
#=============================================================================*/
package de.walware.statet.r.internal.ui.rhelp;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IRegion;
import de.walware.ecommons.ltk.ast.AstSelection;
import de.walware.ecommons.ltk.core.model.ISourceUnit;
import de.walware.ecommons.ltk.ui.sourceediting.assist.AssistInvocationContext;
import de.walware.ecommons.ltk.ui.sourceediting.assist.IInfoHover;
import de.walware.statet.r.core.IRCoreAccess;
import de.walware.statet.r.core.RCore;
import de.walware.statet.r.core.model.IRFrame;
import de.walware.statet.r.core.model.IRFrameInSource;
import de.walware.statet.r.core.model.IRSourceUnit;
import de.walware.statet.r.core.model.RElementAccess;
import de.walware.statet.r.core.model.RElementName;
import de.walware.statet.r.core.model.RModel;
import de.walware.statet.r.core.renv.IREnv;
import de.walware.statet.r.core.rhelp.IREnvHelp;
import de.walware.statet.r.core.rhelp.IRHelpManager;
import de.walware.statet.r.core.rhelp.IRHelpPage;
import de.walware.statet.r.core.rsource.ast.FCall;
import de.walware.statet.r.core.rsource.ast.NodeType;
import de.walware.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 IRegion 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 IRegion region) {
while (rNode != null) {
if (rNode.getNodeType() == NodeType.F_CALL) {
final FCall fcall = (FCall) rNode;
if (fcall.getArgsOpenOffset() != Integer.MIN_VALUE
&& fcall.getArgsOpenOffset() <= region.getOffset()) {
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 IRegion 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.getOffset() <= region.getOffset()
&& nameNode.getEndOffset() >= region.getOffset() + region.getLength() ) {
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;
}
}