| /*=============================================================================# |
| # Copyright (c) 2007, 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.List; |
| |
| import org.eclipse.core.filesystem.EFS; |
| import org.eclipse.core.filesystem.IFileStore; |
| import org.eclipse.core.filesystem.URIUtil; |
| import org.eclipse.core.resources.IContainer; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.ITypedRegion; |
| import org.eclipse.jface.text.TextUtilities; |
| |
| import org.eclipse.statet.jcommons.text.core.BasicTextRegion; |
| import org.eclipse.statet.jcommons.text.core.TextRegion; |
| import org.eclipse.statet.jcommons.ts.core.Tool; |
| |
| import org.eclipse.statet.ecommons.net.resourcemapping.core.IResourceMapping; |
| import org.eclipse.statet.ecommons.net.resourcemapping.core.IResourceMappingManager; |
| import org.eclipse.statet.ecommons.net.resourcemapping.core.ResourceMappingOrder; |
| import org.eclipse.statet.ecommons.net.resourcemapping.core.ResourceMappingUtils; |
| |
| import org.eclipse.statet.ltk.model.core.elements.ISourceUnit; |
| import org.eclipse.statet.ltk.model.core.elements.IWorkspaceSourceUnit; |
| import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditor; |
| import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistInvocationContext; |
| import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistProposalCollector; |
| import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssist; |
| import org.eclipse.statet.ltk.ui.sourceediting.assist.PathCompletionComputor; |
| import org.eclipse.statet.nico.ui.console.ConsolePageEditor; |
| import org.eclipse.statet.r.console.core.RProcess; |
| import org.eclipse.statet.r.core.RUtil; |
| import org.eclipse.statet.r.ui.RUI; |
| |
| |
| /** |
| * Completion computer for path in R code. |
| * |
| * Supports workspace properties of a R tool process |
| */ |
| public class RPathCompletionComputer extends PathCompletionComputor { |
| |
| |
| private RProcess associatedTool; |
| |
| private IContainer baseResource; |
| private IFileStore baseFileStore; |
| |
| |
| public RPathCompletionComputer() { |
| } |
| |
| |
| @Override |
| public String getPluginId() { |
| return RUI.BUNDLE_ID; |
| } |
| |
| @Override |
| public void sessionStarted(final ISourceEditor editor, final ContentAssist assist) { |
| this.associatedTool= null; |
| this.baseResource= null; |
| this.baseFileStore= null; |
| if (editor instanceof ConsolePageEditor) { |
| final Tool tool= editor.getAdapter(Tool.class); |
| if (tool instanceof RProcess) { |
| this.associatedTool= (RProcess) tool; |
| } |
| } |
| else { |
| final ISourceUnit su= editor.getSourceUnit(); |
| if (su instanceof IWorkspaceSourceUnit) { |
| this.baseResource= null; |
| final IResource resource= ((IWorkspaceSourceUnit) su).getResource(); |
| // final IRProject rProject= RProjects.getRProject(resource.getProject()); |
| // if (rProject != null) { |
| // this.baseResource= rProject.getRawBuildpath(); |
| // } |
| if (this.baseResource == null) { |
| this.baseResource= resource.getParent(); |
| } |
| if (this.baseResource != null) { |
| try { |
| this.baseFileStore= EFS.getStore(this.baseResource.getLocationURI()); |
| } |
| catch (final CoreException e) { |
| } |
| } |
| } |
| } |
| |
| super.sessionStarted(editor, assist); |
| } |
| |
| @Override |
| public void sessionEnded() { |
| super.sessionEnded(); |
| this.associatedTool= null; |
| } |
| |
| @Override |
| protected boolean getIsWindows() { |
| if ((this.associatedTool != null)) { |
| return this.associatedTool.getWorkspaceData().isWindows(); |
| } |
| return super.getIsWindows(); |
| } |
| |
| @Override |
| protected char getDefaultFileSeparator() { |
| if (this.associatedTool != null) { |
| return this.associatedTool.getWorkspaceData().getFileSeparator(); |
| } |
| return super.getDefaultFileSeparator(); |
| } |
| |
| @Override |
| protected TextRegion getContentRange(final AssistInvocationContext context, final int mode) |
| throws BadLocationException { |
| final IDocument document= context.getSourceViewer().getDocument(); |
| final int offset= context.getInvocationOffset(); |
| final ITypedRegion partition= TextUtilities.getPartition(document, |
| getEditor().getDocumentContentInfo().getPartitioning(), offset, true); |
| int start= partition.getOffset(); |
| int end= partition.getOffset() + partition.getLength(); |
| if (start == end) { |
| return null; |
| } |
| |
| final char bound= document.getChar(start); |
| if (bound == '\"' || bound == '\'') { |
| start++; |
| } |
| else { |
| return null; |
| } |
| if (start > offset) { |
| return null; |
| } |
| if (end > start && document.getChar(end-1) == bound) { |
| if (end == offset) { |
| return null; |
| } |
| end--; |
| } |
| |
| return new BasicTextRegion(start, end); |
| } |
| |
| @Override |
| protected IPath getRelativeBasePath() { |
| if (this.associatedTool != null) { |
| final IFileStore wd= this.associatedTool.getWorkspaceData().getWorkspaceDir(); |
| if (wd != null) { |
| return URIUtil.toPath(wd.toURI()); |
| } |
| return null; |
| } |
| if (this.baseResource != null) { |
| return this.baseResource.getLocation(); |
| } |
| return null; |
| } |
| |
| @Override |
| protected IFileStore getRelativeBaseStore() { |
| if (this.associatedTool != null) { |
| return this.associatedTool.getWorkspaceData().getWorkspaceDir(); |
| } |
| if (this.baseFileStore != null) { |
| return this.baseFileStore; |
| } |
| return null; |
| } |
| |
| @Override |
| protected IFileStore resolveStore(final IPath path) throws CoreException { |
| if (this.associatedTool != null) { |
| return this.associatedTool.getWorkspaceData().toFileStore(path); |
| } |
| return super.resolveStore(path); |
| } |
| |
| @Override |
| protected IStatus tryAlternative(final AssistInvocationContext context, |
| final IPath path, |
| final int startOffset, final String segmentPrefix, final String completionPrefix, |
| final AssistProposalCollector proposals) throws CoreException { |
| if (this.associatedTool != null) { |
| final String address= this.associatedTool.getWorkspaceData().getRemoteAddress(); |
| if (address != null) { |
| final IResourceMappingManager rmManager= ResourceMappingUtils.getManager(); |
| if (rmManager != null) { |
| final List<IResourceMapping> mappings= rmManager |
| .getResourceMappingsFor(address, ResourceMappingOrder.REMOTE); |
| for (final IResourceMapping mapping : mappings) { |
| IPath remotePath= mapping.getRemotePath(); |
| if (path.isEmpty()) { |
| // remotePath |
| } |
| else if (path.isPrefixOf(remotePath)) { |
| remotePath= remotePath.setDevice(null).makeRelative().removeFirstSegments(path.segmentCount()); |
| } |
| else { |
| continue; |
| } |
| final String name= remotePath.segment(0); |
| if (segmentPrefix.isEmpty() |
| || (name != null && name.regionMatches(true, 0, segmentPrefix, 0, segmentPrefix.length())) ) { |
| proposals.add(new ResourceProposal(context, startOffset, |
| mapping.getFileStore(), remotePath.toString(), completionPrefix, |
| null )); |
| } |
| } |
| return Status.OK_STATUS; |
| } |
| } |
| } |
| return super.tryAlternative(context, path, startOffset, segmentPrefix, |
| completionPrefix, proposals ); |
| } |
| |
| @Override |
| protected String checkPrefix(final String prefix) { |
| String unescaped= RUtil.unescapeCompletely(prefix); |
| // keep a single (not escaped) backslash |
| if (prefix.length() > 0 && prefix.charAt(prefix.length()-1) == '\\' && |
| (unescaped.isEmpty() || unescaped.charAt(unescaped.length()-1) != '\\')) { |
| unescaped= unescaped + '\\'; |
| } |
| return super.checkPrefix(unescaped); |
| } |
| |
| @Override |
| protected String checkPathCompletion(final IDocument document, final int completionOffset, String completion) |
| throws BadLocationException { |
| completion= RUtil.escapeCompletely(completion); |
| int existingBackslashCount= 0; |
| if (completionOffset >= 1) { |
| if (document.getChar(completionOffset-1) == '\\') { |
| existingBackslashCount++; |
| if (completionOffset >= 2) { |
| if (document.getChar(completionOffset-2) == '\\') { |
| existingBackslashCount++; |
| } |
| } |
| } |
| } |
| final boolean startsWithBackslash= (completion.length() >= 2 && |
| completion.charAt(0) == '\\' && completion.charAt(1) == '\\'); |
| if ((existingBackslashCount % 2) == 1) { |
| if (startsWithBackslash) { |
| completion= completion.substring(1); |
| } |
| else { |
| completion= '\\' + completion; |
| } |
| } |
| else if (existingBackslashCount > 0) { |
| if (startsWithBackslash) { |
| completion= completion.substring(2); |
| } |
| } |
| return completion; |
| } |
| |
| } |