blob: 44d798768f42a61bb393144344268af64f8f3411 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2008, 2019 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.r.ui.sourceediting;
import java.util.concurrent.TimeUnit;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.statet.jcommons.collections.ImList;
import org.eclipse.statet.ecommons.text.core.FragmentDocument;
import org.eclipse.statet.ecommons.text.core.PartitionConstraint;
import org.eclipse.statet.internal.r.ui.editors.RContextInformationValidator;
import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistInvocationContext;
import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssist;
import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssistComputerRegistry;
import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssistProcessor;
import org.eclipse.statet.r.console.core.util.LoadReferencesUtil;
import org.eclipse.statet.r.core.data.CombinedRElement;
import org.eclipse.statet.r.core.source.IRDocumentConstants;
import org.eclipse.statet.r.core.source.RHeuristicTokenScanner;
import org.eclipse.statet.r.ui.editors.IRSourceEditor;
public class RContentAssistProcessor extends ContentAssistProcessor {
private static PartitionConstraint NO_R_COMMENT_CONSTRAINT = new PartitionConstraint() {
@Override
public boolean matches(final String partitionType) {
return (partitionType != IRDocumentConstants.R_COMMENT_CONTENT_TYPE);
}
};
private class Context extends RAssistInvocationContext {
public Context(final int offset, final boolean isProposal,
final IProgressMonitor monitor) {
super((IRSourceEditor) RContentAssistProcessor.this.getEditor(),
offset, getContentType(),
isProposal,
RContentAssistProcessor.this.scanner,
monitor );
}
@Override
protected int getToolReferencesWaitTimeout() {
return (getAssistant().isCompletionProposalAutoRequest() ?
LoadReferencesUtil.MAX_AUTO_WAIT : LoadReferencesUtil.MAX_EXPLICITE_WAIT );
}
@Override
protected void toolReferencesResolved(final ImList<CombinedRElement> resolvedElements) {
reloadPossibleCompletions(this);
}
}
private final RHeuristicTokenScanner scanner;
private int timeoutCounter;
public RContentAssistProcessor(final ContentAssist assistant, final String partition,
final ContentAssistComputerRegistry registry, final IRSourceEditor editor) {
super(assistant, partition, registry, editor);
this.scanner= RHeuristicTokenScanner.create(editor.getDocumentContentInfo());
}
@Override
protected AssistInvocationContext createCompletionProposalContext(final int offset,
final IProgressMonitor monitor) {
return new Context(offset, true, monitor);
}
@Override
protected AssistInvocationContext createContextInformationContext(final int offset,
final IProgressMonitor monitor) {
final Context context;
if (this.timeoutCounter <= 3) {
final long startTime= System.nanoTime();
context= new Context(offset, true, monitor);
if (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) > 50) {
this.timeoutCounter= Math.min(this.timeoutCounter + 1, 10);
}
else {
this.timeoutCounter= Math.max(this.timeoutCounter - 1, 0);
}
}
else {
context= new Context(offset, false, monitor);
}
return context;
}
@Override
protected IContextInformationValidator createContextInformationValidator() {
return new RContextInformationValidator();
}
@Override
protected boolean forceContextInformation(final AssistInvocationContext context) {
try {
int offset = context.getInvocationOffset();
if (context.getIdentifierPrefix().length() > 0
|| this.scanner == null) {
return false;
}
IDocument document = context.getSourceViewer().getDocument();
if (document instanceof FragmentDocument) {
final FragmentDocument inputDoc = (FragmentDocument) document;
document = inputDoc.getMasterDocument();
offset = offset + inputDoc.getOffsetInMasterDocument();
}
if (offset < 2) {
return false;
}
this.scanner.configure(document, NO_R_COMMENT_CONSTRAINT);
final int previousOffset = this.scanner.findNonBlankBackward(offset, RHeuristicTokenScanner.UNBOUND, true);
if (previousOffset > 0) {
final char c = document.getChar(previousOffset);
if (c == '(' || c == ',') {
final String partitionType = this.scanner.getPartition(previousOffset).getType();
return (IRDocumentConstants.R_DEFAULT_CONTENT_CONSTRAINT.matches(partitionType));
}
}
}
catch (final BadLocationException e) {}
return false;
}
}