blob: ba0660ec360a46b9b8db3430f7342bdbe775873e [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.debug.core.sourcelookup;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.sourcelookup.AbstractSourceLookupParticipant;
import org.eclipse.debug.core.sourcelookup.ISourceContainer;
import org.eclipse.jface.text.AbstractDocument;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ISynchronizable;
import org.eclipse.osgi.util.NLS;
import org.eclipse.statet.ecommons.io.FileUtil;
import org.eclipse.statet.internal.r.debug.core.RDebugCorePlugin;
import org.eclipse.statet.internal.r.debug.core.model.RStackFrame;
import org.eclipse.statet.ltk.core.Ltk;
import org.eclipse.statet.ltk.model.core.LtkModels;
import org.eclipse.statet.ltk.model.core.ModelManager;
import org.eclipse.statet.ltk.model.core.SourceUnitManager;
import org.eclipse.statet.ltk.model.core.element.LtkModelElement;
import org.eclipse.statet.ltk.model.core.element.SourceUnit;
import org.eclipse.statet.ltk.model.core.element.SourceUnitModelInfo;
import org.eclipse.statet.r.console.core.RDbg;
import org.eclipse.statet.r.console.core.RProcess;
import org.eclipse.statet.r.core.model.RLangMethod;
import org.eclipse.statet.r.core.model.RModel;
import org.eclipse.statet.r.core.model.RSourceUnit;
import org.eclipse.statet.r.core.model.RSourceUnitModelInfo;
import org.eclipse.statet.r.core.rsource.ast.Block;
import org.eclipse.statet.r.core.rsource.ast.FDef;
import org.eclipse.statet.r.core.rsource.ast.NodeType;
import org.eclipse.statet.r.core.rsource.ast.RAsts;
import org.eclipse.statet.r.core.rsource.ast.RAsts.AssignExpr;
import org.eclipse.statet.r.core.rsource.ast.RAstNode;
import org.eclipse.statet.r.debug.core.sourcelookup.IRSourceContainer;
import org.eclipse.statet.r.debug.core.sourcelookup.IRSourceLookupMatch;
import org.eclipse.statet.r.debug.core.sourcelookup.RRuntimeSourceFragment;
import org.eclipse.statet.r.nico.IRSrcref;
import org.eclipse.statet.rj.server.dbg.FrameContext;
import org.eclipse.statet.rj.server.dbg.Srcfiles;
import org.eclipse.statet.rj.server.dbg.Srcref;
public class RSourceLookupParticipant extends AbstractSourceLookupParticipant {
private static final boolean DEBUG_LOG= Boolean.parseBoolean(
Platform.getDebugOption("org.eclipse.statet.r.debug/debug/SourceLookup/log") ); //$NON-NLS-1$
private static final int QUALITY_EXACT_FILE_TIMESTAMP= 23;
private static final int QUALITY_EXACT_FILE_CONTENT= 22;
private static final int QUALITY_EXACT_FUNCTION_CONTENT=13;
private static final int QUALITY_POSITION_FOUND= 10;
protected static class LookupData {
final RStackFrame frame;
final FrameContext context;
RRuntimeSourceFragment fragment;
List<IStatus> status;
public LookupData(final RStackFrame frame) {
this.frame= frame;
this.context= this.frame.getContext();
}
public void addStatus(final IStatus status) {
if (this.status == null) {
this.status= new ArrayList<>();
}
this.status.add(status);
}
}
private static class SourceCorrection {
/** first line of source, same coordinates than expr */
int firstLine;
/** first column of source in first line */
int firstColumn;
int bLineShift;
/** char offset of column 0 in firstLine in b */
int bFirstLineCharOffset;
/** char offset of firstColumn in firstLine in b */
int bFirstColumnCharOffset;
int suLineShift;
/** char offset of column 0 in firstLine in su */
int suFirstLineCharOffset;
/** char offset of firstColumn in firstLine in su */
int suFirstColumnCharOffset;
}
protected class RSourceLookupMatch implements RStackFrame.PositionResolver, IRSourceLookupMatch {
private final RStackFrame frame;
private final Object sourceElement;
private FrameContext currentContext;
private int quality;
private int lineNumber;
private int charStart;
private int charEnd;
public RSourceLookupMatch(final RStackFrame frame, final Object sourceElement) {
this.frame= frame;
this.sourceElement= sourceElement;
}
@Override
public Object getElement() {
return this.sourceElement;
}
@Override
public synchronized int getLineNumber() {
if (this.currentContext != this.frame.getContext()) {
update();
}
return this.lineNumber;
}
@Override
public synchronized int getCharStart() {
if (this.currentContext != this.frame.getContext()) {
update();
}
return this.charStart;
}
@Override
public synchronized int getCharEnd() {
if (this.currentContext != this.frame.getContext()) {
update();
}
return this.charEnd;
}
public void install() {
if (DEBUG_LOG) {
RDebugCorePlugin.log(new Status(IStatus.INFO, RDebugCorePlugin.BUNDLE_ID, 0,
"Installing " + toString(), null));
}
this.frame.setPositionResolver(this.currentContext, this);
}
@Override
public void select() {
install();
}
public void update() {
final LookupData data= new LookupData(this.frame);
if (data.context != null) {
checkPosition(data, this);
}
}
@Override
public int hashCode() {
return this.frame.hashCode() + this.sourceElement.hashCode();
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof RSourceLookupMatch)) {
return false;
}
final RSourceLookupMatch other= (RSourceLookupMatch) obj;
return (this.frame == other.frame
&& this.sourceElement.equals(other.sourceElement) );
}
@Override
public String toString() {
final StringBuilder sb= new StringBuilder(getClass().getName());
sb.append("\n" + "for ").append(this.frame.toString()); //$NON-NLS-1$ //$NON-NLS-2$
sb.append("\n" + "source lookup result:"); //$NON-NLS-1$ //$NON-NLS-2$
sb.append("\n\t" + "sourceElement= ").append(this.sourceElement); //$NON-NLS-1$ //$NON-NLS-2$
sb.append("\n\t" + "lineNumber= ").append(this.lineNumber); //$NON-NLS-1$ //$NON-NLS-2$
sb.append("\n\t" + "charStart= ").append(this.charStart); //$NON-NLS-1$ //$NON-NLS-2$
sb.append("\n\t" + "charEnd= ").append(this.charEnd); //$NON-NLS-1$ //$NON-NLS-2$
return sb.toString();
}
}
/** Created by extension point */
public RSourceLookupParticipant() {
}
@Override
public String getSourceName(final Object object) throws CoreException {
if (object instanceof RStackFrame) {
final RStackFrame frame= (RStackFrame) object;
final FrameContext context= frame.getContext();
if (context.getFileName() != null) {
return context.getFileName();
}
}
return null;
}
@Override
public Object[] findSourceElements(final Object object) throws CoreException {
if (object instanceof RStackFrame) {
final List<RSourceLookupMatch> matches= new ArrayList<>();
final LookupData data= new LookupData((RStackFrame) object);
try {
boolean addedFile= false;
int bestQuality= -1;
final boolean findDuplicates= isFindDuplicates();
if (DEBUG_LOG) {
data.addStatus(new Status(IStatus.INFO, RDebugCorePlugin.BUNDLE_ID, 0,
NLS.bind("Beginning R source lookup for {0}.", object), null));
}
if (data.context == null) {
if (DEBUG_LOG) {
data.addStatus(new Status(IStatus.INFO, RDebugCorePlugin.BUNDLE_ID, 0,
"Context with detail is not available.", null));
}
return new Object[] { IRSourceLookupMatch.NO_CONTEXT_INFORMATION };
}
if (data.context.getFileName() == null && data.context.getSourceCode() == null) {
if (DEBUG_LOG) {
data.addStatus(new Status(IStatus.INFO, RDebugCorePlugin.BUNDLE_ID, 0,
"Context with detail is not available.", null));
}
return new Object[] { IRSourceLookupMatch.NO_CONTEXT_INFORMATION };
}
URI fileUri= null;
if (data.context.getFileName() != null) {
IFile wsFile= null;
IFileStore fileStore= null;
try {
final RProcess process= data.frame.getDebugTarget().getProcess();
if (process.getWorkspaceData().isRemote()) {
fileStore= process.getWorkspaceData().toFileStore(data.context.getFileName());
}
else {
fileStore= FileUtil.getFileStore(data.context.getFileName());
}
}
catch (final Exception e) {
data.addStatus(new Status(IStatus.WARNING, RDebugCorePlugin.BUNDLE_ID, 0,
NLS.bind("An error occured when looking up R sources ({0}).",
data.context.getFileName() ), e ));
}
try {
if (fileStore != null) {
fileUri= fileStore.toURI();
}
if (fileUri == null) {
fileUri= URIUtil.toURI(data.context.getFileName());
}
}
catch (final Exception e) {
data.addStatus(new Status(IStatus.WARNING, RDebugCorePlugin.BUNDLE_ID, 0,
NLS.bind("An error occured when looking up R sources ({0}).",
data.context.getFileName() ), e ));
}
if (fileUri != null && !fileUri.isAbsolute()) {
fileUri= null;
}
if (DEBUG_LOG) {
final StringBuilder sb= new StringBuilder();
sb.append("Resolved filenames:"); //$NON-NLS-1$
sb.append("\n\t" + "fileStore= ").append(fileStore != null ? fileStore.toString() : "<missing>"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
sb.append("\n\t" + "fileUri= ").append(fileUri != null ? fileUri.toString() : "<missing>"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
data.addStatus(new Status(IStatus.INFO, RDebugCorePlugin.BUNDLE_ID, 0,
sb.toString(), null));
}
final IPath path= (data.context.getFilePath() != null) ?
Path.fromPortableString(data.context.getFilePath()) : null;
boolean addedPath= false;
try {
if (fileUri != null) {
final IFile[] files= ResourcesPlugin.getWorkspace().getRoot()
.findFilesForLocationURI(fileUri, IContainer.INCLUDE_HIDDEN);
List<Object> elements= null;
if (files.length > 0 && path != null) {
for (int i= 0; i < files.length; i++) {
if (path.equals(files[i].getFullPath())) {
addedPath= true;
elements= findSourceElement(fileUri,
new IFile[] { files[i] }, data );
break;
}
}
}
if (elements == null) {
elements= findSourceElement(fileUri, files, data);
}
if (elements != null) {
addedFile= true;
final RSourceLookupMatch match= new RSourceLookupMatch(data.frame, elements.get(0));
checkPosition(data, match);
matches.add(match);
bestQuality= Math.max(bestQuality, match.quality);
}
else if (files.length > 0) {
wsFile= files[0];
}
}
}
catch (final Exception e) {
data.addStatus(new Status(IStatus.WARNING, RDebugCorePlugin.BUNDLE_ID, 0,
NLS.bind("An error occured when looking up R sources ({0}).",
fileUri.toString() ), e ));
}
// try workspacePath
if (!addedPath && path != null) {
try {
if (fileUri != null) {
final IFile file= ResourcesPlugin.getWorkspace().getRoot().getFile(path);
addedPath= true;
List<Object> elements= null;
if (file.exists()) {
elements= findSourceElement(file.getLocationURI(),
new IFile[] { file }, data );
}
if (elements == null) {
elements= findSourceElement(path, data);
}
if (elements != null) {
for (int i= 0; i < elements.size(); i++) {
final RSourceLookupMatch match= new RSourceLookupMatch(data.frame, elements.get(i));
checkPosition(data, match);
matches.add(match);
bestQuality= Math.max(bestQuality, match.quality);
if (bestQuality >= QUALITY_EXACT_FILE_CONTENT) {
break;
}
}
}
}
}
catch (final Exception e) {
data.addStatus(new Status(IStatus.WARNING, RDebugCorePlugin.BUNDLE_ID, 0,
NLS.bind("An error occured when looking up R sources ({0}).",
path ), e ));
}
}
// fallback method 1: real file (ws file / filestore)
if ((bestQuality <= 0 || findDuplicates)
&& !addedFile && wsFile != null) {
try {
if (wsFile.exists()) {
addedFile= true;
final RSourceLookupMatch match= new RSourceLookupMatch(data.frame, wsFile);
checkPosition(data, match);
matches.add(match);
bestQuality= Math.max(bestQuality, match.quality);
}
}
catch (final Exception e) {
if (!findDuplicates && !matches.isEmpty()) {
matches.clear();
}
data.addStatus(new Status(IStatus.WARNING, RDebugCorePlugin.BUNDLE_ID, 0,
NLS.bind("An error occured when looking up R sources ({0}).",
wsFile.toString() ), e ));
}
}
if ((bestQuality <= 0 || findDuplicates)
&& !addedFile && fileStore != null) {
try {
if (fileStore.fetchInfo().exists()) {
addedFile= true;
final RSourceLookupMatch match= new RSourceLookupMatch(data.frame, fileStore);
checkPosition(data, match);
matches.add(match);
bestQuality= Math.max(bestQuality, match.quality);
}
}
catch (final Exception e) {
if (!findDuplicates && !matches.isEmpty()) {
matches.clear();
}
data.addStatus(new Status(IStatus.WARNING, RDebugCorePlugin.BUNDLE_ID, 0,
NLS.bind("An error occured when looking up R sources ({0}).",
fileStore.toString() ), e));
}
}
}
// fallback method 2: runtime source
if (data.context.getSourceCode() != null
&& (bestQuality <= 0 || findDuplicates) ) {
try {
createFragment(data);
final RSourceLookupMatch match= new RSourceLookupMatch(data.frame, data.fragment);
checkPosition(data, match);
matches.add(match);
bestQuality= Math.max(bestQuality, match.quality);
}
catch (final Exception e) {
if (!findDuplicates && !matches.isEmpty()) {
matches.clear();
}
data.addStatus(new Status(IStatus.WARNING, RDebugCorePlugin.BUNDLE_ID, 0,
NLS.bind("An error occured when looking up R sources ({0}).",
data.context.getSourceType() ), e));
}
}
if (matches.isEmpty() && data.context.getPosition() <= 0) {
// can we prevent opening the editor?
throw new DebugException(new Status(IStatus.INFO, RDebugCorePlugin.BUNDLE_ID,
DebugException.NOT_SUPPORTED, "Not supported.", null));
}
else if (DEBUG_LOG) {
if (matches.isEmpty()) {
RDebugCorePlugin.log(new Status(IStatus.INFO, RDebugCorePlugin.BUNDLE_ID, 0,
NLS.bind("Could not find R sources ({0}, {1}).", //$NON-NLS-1$
data.context.getSourceType(), data.context.getFileName() ), null));
}
else {
RDebugCorePlugin.log(new Status(IStatus.INFO, RDebugCorePlugin.BUNDLE_ID, 0,
NLS.bind("Could not find R sources ({0}, {1}).", //$NON-NLS-1$
data.context.getSourceType(), data.context.getFileName() ), null));
}
}
if (matches.size() > 0
&& (!findDuplicates || matches.size() == 1) ) {
RSourceLookupMatch match= null;
for (int i= 0; i < matches.size(); i++) {
if (matches.get(i).quality >= bestQuality) {
match= matches.get(i);
break;
}
}
if (!findDuplicates && matches.size() > 1) {
matches.clear();
matches.add(match);
}
match.install();
}
else {
data.frame.setPositionResolver(data.context, null);
}
return matches.toArray();
}
finally {
if (data.status != null) {
RDebugCorePlugin.log(new MultiStatus(RDebugCorePlugin.BUNDLE_ID, 0,
data.status.toArray(new IStatus[data.status.size()]),
"R source lookup report.", null));
}
}
}
return null;
}
protected void createFragment(final LookupData data) {
if (data.fragment == null) {
if (data.context.getSourceCode() == null) {
throw new IllegalStateException();
}
String name;
String fullName;
if (data.context.getSourceType() == 2
&& data.context.getFileName() != null) {
fullName= name= data.context.getFileName();
int idx= name.lastIndexOf('/');
{ final int idx2= name.lastIndexOf('\\');
if (idx2 > idx) {
idx= idx2;
}
}
if (idx >= 0) {
name= name.substring(idx+1);
}
}
else if (data.context.getCall() != null) {
fullName= name= data.context.getCall();
final int idx= name.indexOf('(');
if (idx > 0) {
name= name.substring(0, idx);
}
}
else {
fullName= name= "Frame #" + data.context.getPosition();
}
data.fragment= new RRuntimeSourceFragment(
data.frame.getDebugTarget().getProcess(), name,
fullName, data.context.getSourceCode() );
}
}
protected List<Object> findSourceElement(final URI fileUri, final IFile[] fileInWorkspace, final LookupData data) {
final ISourceContainer[] containers= getSourceContainers();
for (int i= 0; i < containers.length; i++) {
if (containers[i] instanceof IRSourceContainer) {
try {
final Object element= ((IRSourceContainer) containers[i]).findSourceElement(fileUri, fileInWorkspace);
if (element != null) {
if (DEBUG_LOG) {
data.addStatus(new Status(IStatus.INFO, RDebugCorePlugin.BUNDLE_ID, 0,
NLS.bind("Source element found in ''{0}'': {1}",
containers[i].getName(), element.toString() ), null ));
}
final List<Object> list= new ArrayList<>(1);
list.add(element);
return list;
}
}
catch (final Exception e) {
data.addStatus(new Status(IStatus.WARNING, RDebugCorePlugin.BUNDLE_ID, 0,
NLS.bind("An error occurred when the finding source element ''{0}'' in ''{1}''.",
(fileUri != null) ? fileUri.toString() : "<missing>", //$NON-NLS-1$
containers[i].getName() ),
e ));
}
}
}
return null;
}
protected List<Object> findSourceElement(final IPath path, final LookupData data) {
final ISourceContainer[] containers= getSourceContainers();
final List<Object> elements= new ArrayList<>();
for (int i= 0; i < containers.length; i++) {
int j= 0;
if (containers[i] instanceof IRSourceContainer) {
try {
((IRSourceContainer) containers[i]).findSourceElement(path, elements);
if (DEBUG_LOG) {
for (; j < elements.size(); j++) {
data.addStatus(new Status(IStatus.INFO, RDebugCorePlugin.BUNDLE_ID, 0,
NLS.bind("Source element found in ''{0}'': {1}",
containers[i].getName(), elements.get(j).toString() ), null ));
}
}
}
catch (final Exception e) {
data.addStatus(new Status(IStatus.WARNING, RDebugCorePlugin.BUNDLE_ID, 0,
NLS.bind("An error occurred when finding the source element ''{0}'' in ''{1}''.",
(path != null) ? path.toString() : "<missing>", containers[i].getName() ),
e ));
}
}
}
if (elements.size() > 0) {
return elements;
}
return null;
}
private RSourceUnit getSourceUnit(final RSourceLookupMatch match, final IProgressMonitor monitor) {
final SourceUnitManager suManager= LtkModels.getSourceUnitManager();
SourceUnit su= suManager.getSourceUnit(
Ltk.PERSISTENCE_CONTEXT, match.sourceElement, null, true, monitor );
final SourceUnit editorSu= LtkModels.getSourceUnitManager().getSourceUnit(
Ltk.EDITOR_CONTEXT, (su != null) ? su : match.sourceElement, null, true, monitor );
if (editorSu != null) {
su= editorSu;
}
return (RSourceUnit)((su instanceof RSourceUnit) ? su : null);
}
protected int checkPosition(final LookupData data, final RSourceLookupMatch match) {
final IProgressMonitor monitor= new NullProgressMonitor();
try {
match.currentContext= data.context;
match.quality= -1;
match.charStart= -1;
match.charEnd= -1;
match.lineNumber= -1;
final SourceUnit su= getSourceUnit(match, monitor);
SourceUnit fragmentSu= null;
if (su != null) {
try {
final AbstractDocument suDocument= su.getDocument(monitor);
synchronized ((suDocument instanceof ISynchronizable) ?
((ISynchronizable) suDocument).getLockObject() : new Object() ) {
final SourceCorrection corr= new SourceCorrection();
AbstractDocument bDocument= null;
final IRSrcref exprSrcref= (data.context.getExprSrcref() != null) ?
RDbg.createStatetSrcref(data.context.getExprSrcref()) : null;
if (data.context.getFileTimestamp() != 0 && Srcfiles.equalsTimestamp(
RDbg.getTimestamp(su, monitor), data.context.getFileTimestamp() )) {
match.quality= QUALITY_EXACT_FILE_TIMESTAMP;
bDocument= suDocument;
final IRSrcref sourceSrcref= RDbg.createStatetSrcref(data.context.getSourceSrcref());
if (sourceSrcref != null) {
corr.suLineShift= corr.bLineShift= sourceSrcref.getFirstLine();
corr.suFirstColumnCharOffset= corr.bFirstColumnCharOffset= computeOffset(
sourceSrcref.getFirstLine(), sourceSrcref.getFirstColumn(),
bDocument, bDocument, null, 0 );
}
}
else {
final IRSrcref firstSrcref= (data.context.getFirstSrcref() != null) ?
RDbg.createStatetSrcref(data.context.getFirstSrcref()) : null;
final IRSrcref lastSrcref= (data.context.getLastSrcref() != null) ?
RDbg.createStatetSrcref(data.context.getLastSrcref()) : null;
if (data.context.getSourceType() < 3) {
final String suCode= suDocument.get();
if (suCode.equals(data.context.getSourceCode())) {
match.quality= QUALITY_EXACT_FILE_CONTENT;
bDocument= suDocument;
}
else {
bDocument= new Document(data.context.getSourceCode());
int end= -1;
if (firstSrcref != null && lastSrcref != null // body
&& firstSrcref.hasBeginDetail()
&& lastSrcref.hasEndDetail() ) {
corr.firstLine= firstSrcref.getFirstLine();
corr.firstColumn= firstSrcref.getFirstColumn();
corr.bFirstLineCharOffset= bDocument.getLineOffset(corr.firstLine);
corr.bFirstColumnCharOffset= computeOffset(
firstSrcref.getFirstLine(), firstSrcref.getFirstColumn(),
bDocument, bDocument, null, 0 );
end= computeOffset(
lastSrcref.getLastLine(), lastSrcref.getLastColumn(),
bDocument, bDocument, null, 1 );
}
else if (exprSrcref != null // expr only
&& exprSrcref.hasBeginDetail()
&& exprSrcref.hasEndDetail() ) {
corr.firstLine= exprSrcref.getFirstLine();
corr.firstColumn= exprSrcref.getFirstColumn();
corr.bFirstLineCharOffset= bDocument.getLineOffset(corr.firstLine);
corr.bFirstColumnCharOffset= computeOffset(
exprSrcref.getFirstLine(), exprSrcref.getFirstColumn(),
bDocument, bDocument, null, 0 );
end= computeOffset(
exprSrcref.getLastLine(), exprSrcref.getLastColumn(),
bDocument, bDocument, null, 1 );
}
if (end >= 0) {
final String bCode= bDocument.get(corr.bFirstColumnCharOffset,
end - corr.bFirstColumnCharOffset );
match.quality= searchCode(data, corr,
suDocument, suCode, bDocument, bCode);
}
}
}
else if (data.context.getSourceType() == 3) {
createFragment(data);
fragmentSu= LtkModels.getSourceUnitManager().getSourceUnit(RModel.R_TYPE_ID,
Ltk.EDITOR_CONTEXT, data.fragment, true, monitor);
if (fragmentSu != null
&& firstSrcref != null
&& firstSrcref.hasBeginDetail() ) {
final SourceUnitModelInfo modelInfo= fragmentSu.getModelInfo(
RModel.R_TYPE_ID, ModelManager.MODEL_FILE, monitor);
final RAstNode body= findFBody(modelInfo);
if (body != null) {
bDocument= fragmentSu.getDocument(monitor);
corr.firstLine= firstSrcref.getFirstLine();
corr.firstColumn= firstSrcref.getFirstColumn();
corr.bFirstLineCharOffset= bDocument.getLineOffset(corr.firstLine);
corr.bFirstColumnCharOffset= body.getStartOffset();
corr.bLineShift= bDocument.getLineOfOffset(body.getStartOffset()) - corr.firstLine;
final String bCode= bDocument.get(body.getStartOffset(), body.getLength());
match.quality= searchCode(data, corr,
suDocument, suDocument.get(), bDocument, bCode);
}
}
}
}
if (exprSrcref != null) {
match.lineNumber= exprSrcref.getFirstLine() + corr.suLineShift;
if (match.quality >= QUALITY_POSITION_FOUND
&& exprSrcref.hasBeginDetail() && exprSrcref.hasEndDetail() ) {
match.charStart= computeOffset(
exprSrcref.getFirstLine(), exprSrcref.getFirstColumn(),
bDocument, suDocument, corr, 0 );
match.charEnd= computeOffset(
exprSrcref.getLastLine(), exprSrcref.getLastColumn(),
bDocument, suDocument, corr, 1 );
}
}
return match.quality;
}
}
finally {
su.disconnect(monitor);
if (fragmentSu != null) {
fragmentSu.disconnect(monitor);
}
}
}
}
catch (final Exception e) {
data.addStatus(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID, 0,
"An error occurred in advanced position detection.", e));
}
return 0;
}
private int searchCode(final LookupData data, final SourceCorrection corr,
final AbstractDocument suDocument, final String suCode,
final AbstractDocument bDocument, String bCode) throws BadLocationException {
// First match
int offset= suCode.indexOf(bCode);
if (offset < 0
&& bDocument.getDefaultLineDelimiter() != suDocument.getDefaultLineDelimiter() ) {
bCode= bCode.replace(bDocument.getDefaultLineDelimiter(), suDocument.getDefaultLineDelimiter());
offset= suCode.indexOf(bCode);
}
if (offset < 0) {
return 0;
}
int firstLine= corr.firstLine;
if (data.context.getSourceSrcref() != null
&& data.context.getSourceSrcref()[Srcref.BEGIN_LINE] > 0) {
firstLine+= data.context.getSourceSrcref()[Srcref.BEGIN_LINE] - 1;
// firstColumn+= data.context.sourceSrcref.getBeginColumn();
}
// Prefer correct line start, if available
if (offset >= 0
&& offset < suDocument.getLength() && firstLine < suDocument.getNumberOfLines() ) {
final IRegion line= suDocument.getLineInformation(firstLine);
if (offset < line.getOffset()) {
final int offset2= suCode.indexOf(bCode, line.getOffset());
if (offset2 >= 0 && offset2 < line.getOffset()+line.getLength()) {
offset= offset2;
}
}
}
if (offset >= 0) {
final int suLine= suDocument.getLineOfOffset(offset);
corr.suLineShift= suLine - corr.firstLine;
corr.suFirstLineCharOffset= suDocument.getLineOffset(suLine);
corr.suFirstColumnCharOffset= offset;
return QUALITY_EXACT_FUNCTION_CONTENT;
}
return 0;
}
private Block findFBody(final SourceUnitModelInfo modelInfo) {
if (modelInfo instanceof RSourceUnitModelInfo && modelInfo.getSourceElement() != null) {
FDef fDef= null;
final List<? extends LtkModelElement> children= modelInfo.getSourceElement().getSourceChildren(null);
if (children.size() == 1) {
final LtkModelElement modelElement= children.get(0);
if (modelElement instanceof RLangMethod) {
fDef= modelElement.getAdapter(FDef.class);
}
}
if (fDef == null && (modelInfo.getAst().getRoot() instanceof RAstNode)) {
final RAstNode node= ((RAstNode) modelInfo.getAst().getRoot()).getChild(0);
if (node.getNodeType() == NodeType.F_DEF) {
fDef= (FDef) node;
}
if (fDef == null) {
final AssignExpr assign= RAsts.checkAssign(node);
if (assign != null && assign.valueNode != null
&& assign.valueNode.getNodeType() == NodeType.F_DEF) {
fDef= (FDef) assign.valueNode;
}
}
}
if (fDef != null) {
final RAstNode body= fDef.getContChild();
if (body.getNodeType() == NodeType.BLOCK) {
return (Block) body;
}
}
}
return null;
}
/**
* Computes the offset for the position specified by line and column
*
* @param line
* @param column
* @param bDocument
* @param suDocument
* @param corr optional correction
* @param shift optional final shift for char offset
* @return the offset in suDocument
* @throws BadLocationException
*/
private int computeOffset(final int line, final int column,
final AbstractDocument bDocument, final AbstractDocument suDocument,
final SourceCorrection corr, final int shift) throws BadLocationException {
int charOffset= 0; // offset in line
{ int bLine= line;
if (corr != null) {
bLine+= corr.bLineShift;
}
final IRegion lineInfo= bDocument.getLineInformation(bLine);
int currentColumn= 0;
if (corr != null && line == corr.firstLine) {
currentColumn+= corr.firstColumn;
if (currentColumn > column) {
return -1;
}
charOffset+= corr.bFirstColumnCharOffset - lineInfo.getOffset(); // lineInfo.getOffset == corr.bFirstLineCharOffset
}
while (currentColumn < column && charOffset < lineInfo.getLength() ) {
final char c= bDocument.getChar(lineInfo.getOffset() + charOffset++);
if (c == '\t') {
currentColumn+= 8 - (currentColumn % 8);
}
else {
currentColumn++;
}
}
}
{ int suLine= line;
if (corr != null) {
suLine+= corr.suLineShift;
}
final IRegion lineInfo= suDocument.getLineInformation(suLine);
if (corr != null && line == corr.firstLine) {
charOffset+= (corr.suFirstColumnCharOffset - corr.suFirstLineCharOffset) - (corr.bFirstColumnCharOffset - corr.bFirstLineCharOffset);
}
charOffset+= shift;
return lineInfo.getOffset() + Math.min(charOffset, lineInfo.getLength());
}
}
}