| /*=============================================================================# |
| # Copyright (c) 2009, 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.internal.r.ui.editors; |
| |
| import org.eclipse.jface.text.AbstractDocument; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.ITextViewer; |
| import org.eclipse.jface.text.TextPresentation; |
| import org.eclipse.jface.text.contentassist.IContextInformation; |
| import org.eclipse.jface.text.contentassist.IContextInformationPresenter; |
| import org.eclipse.jface.text.contentassist.IContextInformationValidator; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.StyleRange; |
| |
| import org.eclipse.statet.jcommons.text.core.input.OffsetStringParserInput; |
| |
| import org.eclipse.statet.ltk.ast.core.AstInfo; |
| import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistCompletionInformationProposalWrapper; |
| import org.eclipse.statet.r.core.model.ArgsDefinition; |
| import org.eclipse.statet.r.core.rsource.ast.FCall; |
| import org.eclipse.statet.r.core.rsource.ast.FCall.Args; |
| import org.eclipse.statet.r.core.rsource.ast.RAst; |
| import org.eclipse.statet.r.core.rsource.ast.RAst.FCallArgMatch; |
| import org.eclipse.statet.r.core.rsource.ast.RScanner; |
| |
| |
| public class RContextInformationValidator implements IContextInformationValidator, IContextInformationPresenter { |
| |
| |
| private ITextViewer fViewer; |
| private int fStartOffset; |
| |
| private IContextInformation fInfo; |
| |
| private RArgumentListContextInformation fArgInfo; |
| private int fCurrentParameter; |
| // private RScanner scanner; |
| private long fScannedArgsStamp; |
| private Args fScannedArgs; |
| |
| private int fLastPresentation = -2; |
| |
| |
| public RContextInformationValidator() { |
| } |
| |
| |
| @Override |
| public boolean isContextInformationValid(final int offset) { |
| if (fInfo == null) { |
| return false; |
| } |
| final IDocument document = fViewer.getDocument(); |
| if (offset < fStartOffset || offset > document.getLength()) { |
| return false; |
| } |
| if (fArgInfo != null) { |
| final Args args = getScannedArgs(); |
| if (args != null) { |
| return (offset <= args.getEndOffset()); |
| } |
| } |
| return (offset == fStartOffset); |
| } |
| |
| @Override |
| public void install(IContextInformation info, final ITextViewer viewer, final int offset) { |
| if (info instanceof AssistCompletionInformationProposalWrapper) { |
| info= ((AssistCompletionInformationProposalWrapper) info).getContextInformation(); |
| } |
| |
| fScannedArgs = null; |
| fLastPresentation = -2; |
| if (info instanceof RArgumentListContextInformation) { |
| fInfo = fArgInfo = (RArgumentListContextInformation) info; |
| fCurrentParameter = -1; |
| } |
| else { |
| fInfo = info; |
| fArgInfo = null; |
| return; |
| } |
| fViewer = viewer; |
| fStartOffset = offset; |
| } |
| |
| @Override |
| public boolean updatePresentation(final int offset, final TextPresentation presentation) { |
| if (fArgInfo != null) { |
| final ArgsDefinition args = fArgInfo.getArguments(); |
| if (args != null && args.size() > 0) { |
| final int argIndex = getCurrentArgInFDef(offset); |
| final int[] idxs = fArgInfo.getInformationDisplayStringArgumentIdxs(); |
| if (argIndex >= 0 && argIndex < idxs.length) { |
| if (argIndex == fLastPresentation) { |
| return false; |
| } |
| final int start = idxs[argIndex]; |
| final int stop = (argIndex+1 < idxs.length) ? idxs[argIndex+1] : fArgInfo.getInformationDisplayString().length(); |
| presentation.clear(); |
| presentation.addStyleRange(new StyleRange(start, stop-start, null, null, SWT.BOLD)); |
| fLastPresentation = argIndex; |
| return true; |
| } |
| } |
| } |
| if (fLastPresentation == -1) { |
| return false; |
| } |
| presentation.clear(); |
| presentation.addStyleRange(new StyleRange(0, fInfo.getInformationDisplayString().length(), |
| null, null, SWT.NORMAL)); |
| fLastPresentation = -1; |
| return true; |
| } |
| |
| private FCall.Args getScannedArgs() { |
| final AbstractDocument document = (AbstractDocument) fViewer.getDocument(); |
| final long stamp = document.getModificationStamp(); |
| if (fScannedArgs == null || fScannedArgsStamp != stamp) { |
| try { |
| final String text= document.get(fStartOffset, Math.min(0x800, document.getLength() - fStartOffset)); |
| final RScanner scanner= new RScanner(AstInfo.LEVEL_MODEL_DEFAULT); |
| fScannedArgs= scanner.scanFCallArgs( |
| new OffsetStringParserInput(text, fStartOffset) |
| .init(fStartOffset, fStartOffset + text.length()), |
| true ); |
| fScannedArgsStamp = stamp; |
| } |
| catch (final Exception e) { |
| fScannedArgs = null; |
| } |
| } |
| |
| return fScannedArgs; |
| } |
| |
| private int getCurrentArgInFDef(final int offset) { |
| final int callArgIdx= getCurrentArgInFCall(offset); |
| if (callArgIdx >= 0) { |
| final FCallArgMatch match= RAst.matchArgs(getScannedArgs(), fArgInfo.getArguments()); |
| final ArgsDefinition.Arg argDef= match.getArgDef(callArgIdx); |
| if (argDef != null) { |
| return argDef.index; |
| } |
| } |
| return -1; |
| } |
| |
| private int getCurrentArgInFCall(final int offset) { |
| final Args args = getScannedArgs(); |
| if (args != null) { |
| final int last = args.getChildCount()-1; |
| if (last == -1) { |
| return 0; |
| } |
| for (int i = 0; i < last; i++) { |
| if (offset <= args.getSeparatorOffset(i)) { |
| return i; |
| } |
| } |
| return last; |
| } |
| return -1; |
| } |
| |
| } |