blob: abffdfc9df9b4e04701f39313d885a4dcad0936e [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2007, 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.core.sourcemodel;
import java.util.List;
import com.ibm.icu.text.DecimalFormat;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.string.InternStringCache;
import org.eclipse.statet.jcommons.string.StringFactory;
import org.eclipse.statet.jcommons.text.core.input.OffsetStringParserInput;
import org.eclipse.statet.jcommons.text.core.input.StringParserInput;
import org.eclipse.statet.jcommons.text.core.input.TextParserInput;
import org.eclipse.statet.ltk.ast.core.AstInfo;
import org.eclipse.statet.ltk.core.SourceContent;
import org.eclipse.statet.ltk.issues.core.ProblemRequestor;
import org.eclipse.statet.ltk.model.core.IModelManager;
import org.eclipse.statet.ltk.model.core.elements.ISourceUnitModelInfo;
import org.eclipse.statet.ltk.model.core.impl.SourceModelStamp;
import org.eclipse.statet.r.core.model.IRModelInfo;
import org.eclipse.statet.r.core.model.IRSourceUnit;
import org.eclipse.statet.r.core.model.RChunkElement;
import org.eclipse.statet.r.core.model.RModel;
import org.eclipse.statet.r.core.model.RProblemReporter;
import org.eclipse.statet.r.core.model.RSuModelContainer;
import org.eclipse.statet.r.core.rsource.ast.RAstNode;
import org.eclipse.statet.r.core.rsource.ast.RScanner;
import org.eclipse.statet.r.core.rsource.ast.RoxygenScanner;
import org.eclipse.statet.r.core.rsource.ast.SourceComponent;
/**
* Worker for r model manager
*/
public class RReconciler {
private static final boolean LOG_TIME= false;
protected static class Data {
public final RSuModelContainer adapter;
public final SourceContent content;
public AstInfo ast;
public IRModelInfo oldModel;
public IRModelInfo newModel;
public Data(final RSuModelContainer adapter, final IProgressMonitor monitor) {
this.adapter= adapter;
this.content= adapter.getParseContent(monitor);
}
}
private final RModelManager rManager;
protected boolean stop= false;
private final Object raLock= new Object();
private final StringFactory raAstStringCache;
private final StringParserInput raInput= new StringParserInput(0x1000);
private final RoxygenScanner raRoxygenScanner;
private final Object rmLock= new Object();
private final SourceAnalyzer rmScopeAnalyzer;
private final Object rpLock= new Object();
private final RProblemReporter rpReporter;
public RReconciler(final RModelManager manager) {
this.rManager= manager;
this.raAstStringCache= new InternStringCache(0x20);
this.raRoxygenScanner= new RoxygenScanner(this.raAstStringCache);
this.rmScopeAnalyzer= new SourceAnalyzer();
this.rpReporter= new RProblemReporter();
}
/** for editor reconciling */
public void reconcile(final RSuModelContainer adapter, final int flags,
final IProgressMonitor monitor) {
final IRSourceUnit su= adapter.getSourceUnit();
final int type= (su.getModelTypeId().equals(RModel.R_TYPE_ID) ? su.getElementType() : 0);
if (type == 0) {
return;
}
final Data data= new Data(adapter, monitor);
if (data.content == null) {
return;
}
synchronized (this.raLock) {
if (this.stop || monitor.isCanceled()) {
return;
}
updateAst(data, monitor);
}
if (this.stop || monitor.isCanceled()
|| (flags & 0xf) < IModelManager.MODEL_FILE) {
return;
}
synchronized (this.rmLock) {
if (this.stop || monitor.isCanceled()) {
return;
}
final boolean updated= updateModel(data);
if (this.stop) {
return;
}
if (updated) {
this.rManager.getEventJob().addUpdate(su, data.oldModel, data.newModel);
}
}
if ((flags & IModelManager.RECONCILE) != 0 && data.newModel != null) {
if (this.stop || monitor.isCanceled()) {
return;
}
ProblemRequestor problemRequestor= null;
synchronized (this.rpLock) {
if (!this.stop && !monitor.isCanceled()
&& data.newModel == adapter.getCurrentModel() ) {
problemRequestor= adapter.createProblemRequestor();
if (problemRequestor != null) {
this.rpReporter.run(su, data.content,
(RAstNode) data.ast.getRoot(), problemRequestor );
}
}
}
if (problemRequestor != null) {
problemRequestor.finish();
}
}
}
public IRModelInfo reconcile(final IRSourceUnit su, final ISourceUnitModelInfo modelInfo,
final List<? extends RChunkElement> chunkElements, final List<? extends SourceComponent> inlineNodes,
final int level, final IProgressMonitor monitor) {
synchronized (this.rmLock) {
return updateModel(su, modelInfo, chunkElements, inlineNodes);
}
}
protected final void updateAst(final Data data, final IProgressMonitor monitor) {
final SourceModelStamp stamp= new SourceModelStamp(data.content.getStamp());
data.ast= data.adapter.getCurrentAst();
if (data.ast != null && !stamp.equals(data.ast.getStamp())) {
data.ast= null;
}
if (data.ast == null) {
final long startAst;
final long stopAst;
startAst= System.nanoTime();
final TextParserInput input;
if (data.content.getStartOffset() != 0) {
input= new OffsetStringParserInput(data.content.getText(), data.content.getStartOffset());
}
else {
input= this.raInput.reset(data.content.getText());
}
final RScanner scanner= new RScanner(AstInfo.LEVEL_MODEL_DEFAULT,
this.raAstStringCache );
scanner.setCommentLevel(100);
final SourceComponent sourceComponent= scanner.scanSourceRange(
input.init(data.content.getStartOffset(), data.content.getEndOffset()),
null );
data.ast= new AstInfo(scanner.getAstLevel(), stamp, sourceComponent);
stopAst= System.nanoTime();
this.raRoxygenScanner.init(
input.init(data.content.getStartOffset(), data.content.getEndOffset()));
this.raRoxygenScanner.update(sourceComponent);
if (LOG_TIME) {
System.out.println(this.raAstStringCache.toString());
System.out.println("RReconciler/createAST : " + DecimalFormat.getInstance().format(stopAst - startAst)); //$NON-NLS-1$
}
synchronized (data.adapter) {
data.adapter.setAst(data.ast);
}
}
}
protected final boolean updateModel(final Data data) {
data.newModel= data.adapter.getCurrentModel();
if (data.newModel != null && !data.ast.getStamp().equals(data.newModel.getStamp())) {
data.newModel= null;
}
if (data.newModel == null) {
final long startModel;
final long stopModel;
startModel= System.nanoTime();
final IRModelInfo model= this.rmScopeAnalyzer.createModel(data.adapter.getSourceUnit(), data.ast);
final boolean isOK= (model != null);
stopModel= System.nanoTime();
if (LOG_TIME) {
System.out.println("RReconciler/createMODEL : " + DecimalFormat.getInstance().format(stopModel - startModel));
}
if (isOK) {
synchronized (data.adapter) {
data.oldModel= data.adapter.getCurrentModel();
data.adapter.setModel(model);
}
data.newModel= model;
return true;
}
}
return false;
}
private IRModelInfo updateModel(final IRSourceUnit su, final ISourceUnitModelInfo modelInfo,
final List<? extends RChunkElement> chunkElements,
final List<? extends SourceComponent> inlineNodes) {
IRModelInfo model;
try {
final AstInfo ast= modelInfo.getAst();
this.rmScopeAnalyzer.beginChunkSession(su, ast);
for (final RChunkElement chunkElement : chunkElements) {
final List<SourceComponent> rootNodes;
{ final Object source= chunkElement.getAdapter(SourceComponent.class);
if (source instanceof SourceComponent) {
rootNodes= ImCollections.newList((SourceComponent) source);
}
else if (source instanceof List<?>) {
rootNodes= (List<SourceComponent>) source;
}
else {
continue;
}
}
this.rmScopeAnalyzer.processChunk(chunkElement, rootNodes);
}
for (final SourceComponent inlineNode : inlineNodes) {
this.rmScopeAnalyzer.processInlineNode(inlineNode);
}
}
finally {
model= this.rmScopeAnalyzer.stopChunkSession();
}
return model;
}
void stop() {
this.stop= true;
}
}