| /*=============================================================================# |
| # Copyright (c) 2015, 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.r.ui.sourceediting; |
| |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.NoSuchElementException; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.debug.core.DebugException; |
| import org.eclipse.debug.core.model.IDebugTarget; |
| import org.eclipse.debug.core.model.IStackFrame; |
| import org.eclipse.debug.core.model.IThread; |
| import org.eclipse.ui.statushandlers.StatusManager; |
| |
| import org.eclipse.statet.jcommons.collections.ImCollections; |
| |
| import org.eclipse.statet.ltk.model.core.elements.IModelElement; |
| import org.eclipse.statet.r.console.core.RProcess; |
| import org.eclipse.statet.r.console.core.RProcessREnvironment; |
| import org.eclipse.statet.r.console.core.RWorkspace; |
| import org.eclipse.statet.r.core.RProject; |
| import org.eclipse.statet.r.core.RProjects; |
| import org.eclipse.statet.r.core.data.CombinedRElement; |
| import org.eclipse.statet.r.core.model.IRFrame; |
| import org.eclipse.statet.r.core.model.IRFrameInSource; |
| import org.eclipse.statet.r.core.model.IRModelInfo; |
| import org.eclipse.statet.r.core.model.IRSourceUnit; |
| import org.eclipse.statet.r.core.model.IRWorkspaceSourceUnit; |
| import org.eclipse.statet.r.core.model.RElementName; |
| import org.eclipse.statet.r.core.model.RModel; |
| import org.eclipse.statet.r.core.rsource.ast.RAstNode; |
| import org.eclipse.statet.r.ui.RUI; |
| |
| |
| public final class RFrameSearchPath implements Iterable<IRFrame> { |
| |
| |
| public static final int WORKSPACE_MODE= 1; |
| public static final int CONSOLE_MODE= 2; |
| public static final int ENGINE_MODE= 3; |
| |
| |
| private static final int LOCAL_ID= 0; |
| private static final int WORKSPACE_ID= 1; |
| private static final int RUNTIME_ID= 2; |
| |
| |
| public class RFrameIterator implements Iterator<IRFrame> { |
| |
| private int listIter0; |
| private int listIter1= -1; |
| private IRFrame next; |
| |
| @Override |
| public boolean hasNext() { |
| if (this.next != null) { |
| return true; |
| } |
| ITER_0 : while (this.listIter0 < RFrameSearchPath.this.frames.length) { |
| if (++this.listIter1 < RFrameSearchPath.this.frames[this.listIter0].size()) { |
| this.next= RFrameSearchPath.this.frames[this.listIter0].get(this.listIter1); |
| return true; |
| } |
| else { |
| this.listIter0++; |
| this.listIter1= -1; |
| continue ITER_0; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public IRFrame next() { |
| if (hasNext()) { |
| final IRFrame frame= this.next; |
| this.next= null; |
| return frame; |
| } |
| throw new NoSuchElementException(); |
| } |
| |
| @Override |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public int getEnvirGroup() { |
| return this.listIter0; |
| } |
| |
| public int getRelevance() { |
| switch (getEnvirGroup()) { |
| case LOCAL_ID: |
| return Math.max(9 - this.listIter1, 3); |
| case WORKSPACE_ID: |
| return 1; |
| default: |
| return -5; |
| } |
| } |
| |
| } |
| |
| |
| private final List<IRFrame>[] frames= new List[3]; |
| |
| private RElementName expliciteScope; |
| private boolean packageMode; |
| private Set<String> importedPackages; |
| |
| private final Set<String> workspacePackages= new HashSet<>(); |
| |
| private RWorkspace runtimeWorkspace; |
| |
| |
| public RFrameSearchPath() { |
| this.frames[RUNTIME_ID]= new ArrayList<>(); |
| } |
| |
| |
| public void init(final RAssistInvocationContext context, final RAstNode node, |
| final int mode, final RElementName expliciteScope) { |
| this.expliciteScope= (expliciteScope != null && RElementName.isPackageFacetScopeType(expliciteScope.getType())) ? |
| expliciteScope : null; |
| |
| final IRFrameInSource envir= RModel.searchFrame(node); |
| if (envir != null && mode <= CONSOLE_MODE) { |
| this.frames[LOCAL_ID]= RModel.createDirectFrameList(envir, this.expliciteScope); |
| } |
| else { |
| this.frames[LOCAL_ID]= new ArrayList<>(); |
| } |
| |
| if (mode == WORKSPACE_MODE) { |
| final IRSourceUnit su= context.getSourceUnit(); |
| if (su != null) { |
| if (this.expliciteScope == null && su instanceof IRWorkspaceSourceUnit) { |
| final RProject rProject= RProjects.getRProject(((IRWorkspaceSourceUnit) su) |
| .getResource().getProject() ); |
| this.packageMode= (rProject != null && rProject.getPkgName() != null); |
| } |
| |
| this.importedPackages= (this.expliciteScope != null) ? |
| ImCollections.newSet(this.expliciteScope.getSegmentName()) : |
| RModel.createImportedPackageList((IRModelInfo) context.getModelInfo()); |
| |
| try { |
| this.frames[WORKSPACE_ID]= RModel.createProjectFrameList(null, |
| su, |
| true, (this.expliciteScope == null), |
| this.importedPackages, this.workspacePackages ); |
| } |
| catch (final CoreException e) { |
| // CANCELLED possible? |
| } |
| if (this.frames[WORKSPACE_ID] != null && !this.frames[WORKSPACE_ID].isEmpty()) { |
| this.frames[LOCAL_ID].add(this.frames[WORKSPACE_ID].remove(0)); |
| } |
| } |
| } |
| if (this.frames[WORKSPACE_ID] == null) { |
| this.frames[WORKSPACE_ID]= new ArrayList<>(); |
| } |
| |
| this.runtimeWorkspace= getRuntimeWorkspace(context); |
| |
| addRuntimeFrames(context, mode >= CONSOLE_MODE); |
| } |
| |
| private RWorkspace getRuntimeWorkspace(final RAssistInvocationContext context) { |
| final RProcess tool= context.getTool(); |
| return (tool != null) ? tool.getWorkspaceData() : null; |
| } |
| |
| private void addRuntimeFrames(final RAssistInvocationContext context, |
| final boolean complete) { |
| if (this.runtimeWorkspace != null && this.runtimeWorkspace.hasRObjectDB()) { |
| if (complete) { |
| if (this.expliciteScope != null) { |
| final IRFrame frame= resolve(this.expliciteScope, context); |
| this.frames[WORKSPACE_ID].add(frame); |
| return; |
| } |
| |
| addDebugFrame(context); |
| |
| final List<? extends RProcessREnvironment> searchEnvs= this.runtimeWorkspace |
| .getRSearchEnvironments(); |
| if (searchEnvs != null && !searchEnvs.isEmpty()) { |
| for (final RProcessREnvironment env : searchEnvs) { |
| final IRFrame frame= (IRFrame) env; |
| if (frame.getFrameType() == IRFrame.PROJECT) { |
| this.frames[LOCAL_ID].add(frame); |
| } |
| else { |
| this.frames[WORKSPACE_ID].add(frame); |
| } |
| } |
| } |
| } |
| else if (this.importedPackages != null) { |
| if (this.expliciteScope != null |
| && !this.workspacePackages.contains( |
| this.expliciteScope.getSegmentName() )) { |
| final IRFrame frame= resolve(this.expliciteScope, context); |
| if (frame != null) { |
| this.frames[RUNTIME_ID].add(frame); |
| } |
| } |
| else if (this.packageMode) { |
| final List<? extends RProcessREnvironment> searchEnvs= |
| this.runtimeWorkspace.getRSearchEnvironments(); |
| |
| for (final String pkgName : this.importedPackages) { |
| if (!this.workspacePackages.contains(pkgName)) { |
| IRFrame frame; |
| frame= resolve(RElementName.create(RElementName.SCOPE_NS, pkgName), |
| context ); |
| if (frame == null) { // timeout |
| frame= searchPackage(searchEnvs, pkgName); |
| } |
| if (frame != null) { |
| this.frames[RUNTIME_ID].add(frame); |
| } |
| } |
| } |
| } |
| else { |
| final List<? extends RProcessREnvironment> searchEnvs= |
| this.runtimeWorkspace.getRSearchEnvironments(); |
| for (final String pkgName : this.importedPackages) { |
| if (!this.workspacePackages.contains(pkgName)) { |
| IRFrame frame; |
| frame= searchPackage(searchEnvs, pkgName); |
| if (frame == null) { |
| frame= resolve(RElementName.create(RElementName.SCOPE_NS, pkgName), |
| context ); |
| } |
| if (frame != null) { |
| this.frames[RUNTIME_ID].add(frame); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| |
| private IRFrame searchPackage(final List<? extends RProcessREnvironment> searchEnvs, |
| final String pkgName) { |
| if (searchEnvs != null) { |
| for (final RProcessREnvironment env : searchEnvs) { |
| final IRFrame frame= (IRFrame) env; |
| if (frame.getFrameType() == IRFrame.PACKAGE |
| && frame.getElementName().getSegmentName().equals(pkgName) ) { |
| return frame; |
| } |
| } |
| } |
| return null; |
| } |
| |
| private IRFrame resolve(final RElementName name, |
| final RAssistInvocationContext context) { |
| CombinedRElement element= this.runtimeWorkspace.resolve(name, |
| RWorkspace.RESOLVE_INDICATE_NA ); |
| if (element != null) { |
| return (!this.runtimeWorkspace.isNA(element) |
| && element instanceof IRFrame) ? |
| (IRFrame) element : null; |
| } |
| |
| element= context.getToolReferencesUtil().resolve(name, 0); |
| |
| return (element instanceof IRFrame) ? |
| (IRFrame) element : null; |
| } |
| |
| private void addDebugFrame(final RAssistInvocationContext context) { |
| final IDebugTarget debugTarget= context.getTool().getLaunch().getDebugTarget(); |
| if (debugTarget == null) { |
| return; |
| } |
| try { |
| final IThread[] threads= debugTarget.getThreads(); |
| if (threads.length > 0) { |
| final IStackFrame top= threads[0].getTopStackFrame(); |
| if (top != null) { |
| final CombinedRElement envir= (CombinedRElement) top.getAdapter(IModelElement.class); |
| if (envir instanceof IRFrame) { |
| final IRFrame frame= (IRFrame) envir; |
| if (frame.getFrameType() != IRFrame.PACKAGE) { |
| this.frames[LOCAL_ID].add(frame); |
| } |
| } |
| } |
| } |
| } |
| catch (final DebugException e) { |
| if (e.getStatus().getCode() == DebugException.NOT_SUPPORTED |
| || e.getStatus().getSeverity() == IStatus.CANCEL) { |
| return; |
| } |
| StatusManager.getManager().handle(new Status(IStatus.ERROR, RUI.BUNDLE_ID, -1, |
| "An error occurred when collecting environments for content assist.", |
| e )); |
| } |
| } |
| |
| |
| @Override |
| public RFrameIterator iterator() { |
| return new RFrameIterator(); |
| } |
| |
| // public RFrameIterator iterator(final RElementName name) { |
| // if (name == null) { |
| // return new RFrameIterator(); |
| // } |
| // if (name.getType() == RElementName.SCOPE_NS_INT) { |
| // this.runtimeWorkspace.getProcess().getQueue().add |
| // } |
| // } |
| |
| |
| public void clear() { |
| this.frames[LOCAL_ID]= null; |
| this.frames[WORKSPACE_ID]= null; |
| this.frames[RUNTIME_ID].clear(); |
| |
| this.expliciteScope= null; |
| this.importedPackages= null; |
| this.workspacePackages.clear(); |
| |
| this.runtimeWorkspace= null; |
| } |
| |
| } |