blob: 338b51c1134f166080190f6ffd69dc7d79574c8e [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2012, 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.console.ui.snippets;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.variables.IDynamicVariable;
import org.eclipse.core.variables.IStringVariable;
import org.eclipse.core.variables.IStringVariableManager;
import org.eclipse.core.variables.VariablesPlugin;
import org.eclipse.jface.text.templates.Template;
import org.eclipse.jface.text.templates.TemplateContextType;
import org.eclipse.jface.text.templates.persistence.TemplateStore;
import org.eclipse.text.templates.ContextTypeRegistry;
import org.eclipse.ui.editors.text.templates.ContributionContextTypeRegistry;
import org.eclipse.ui.editors.text.templates.ContributionTemplateStore;
import org.eclipse.ui.statushandlers.StatusManager;
import org.eclipse.statet.ecommons.text.TextUtil;
import org.eclipse.statet.ecommons.ui.util.UIAccess;
import org.eclipse.statet.ecommons.ui.workbench.WorkbenchUIUtils;
import org.eclipse.statet.ecommons.ui.workbench.workspace.ResourceVariableUtil;
import org.eclipse.statet.ecommons.variables.core.DynamicVariable;
import org.eclipse.statet.ecommons.variables.core.StaticVariable;
import org.eclipse.statet.ecommons.variables.core.StringVariable;
import org.eclipse.statet.ecommons.variables.core.UnresolvedVariable;
import org.eclipse.statet.ecommons.variables.core.VariableText2;
import org.eclipse.statet.internal.r.console.ui.RConsoleUIPlugin;
import org.eclipse.statet.ltk.ui.util.LTKWorkbenchUIUtil;
import org.eclipse.statet.r.core.RUtil;
import org.eclipse.statet.r.launching.RCodeLaunching;
import org.eclipse.statet.r.ui.rtool.RElementNameVariableResolver;
import org.eclipse.statet.r.ui.rtool.RResourceEncodingVariableResolver;
public class RSnippets {
public static final String SUBMIT_SNIPPET_COMMAND_ID= "org.eclipse.statet.r.commands.SubmitRSnippet"; //$NON-NLS-1$
public static final String SUBMIT_LAST_COMMAND_ID= "org.eclipse.statet.r.commands.SubmitLastRSnippet"; //$NON-NLS-1$
public static final String SNIPPET_PAR= "snippet"; //$NON-NLS-1$
public static final IStringVariable RESOURCE_ENCODING_VARIABLE= new StringVariable(
"resource_encoding", Messages.Variable_ResourceEncoding_description ); //$NON-NLS-1$
public static final IStringVariable ECHO_ENABLED_VARIABLE= new StringVariable(
"echo", Messages.Variable_Echo_description ); //$NON-NLS-1$
private static final String[] PRECHECKED_NAMES= new String[] {
"selected_text", //$NON-NLS-1$
RElementNameVariableResolver.R_OBJECT_NAME_NAME,
};
private static void add(final Map<String, IStringVariable> map, final IStringVariable var) {
map.put(var.getName(), var);
}
private ContributionContextTypeRegistry templatesContextTypeRegistry;
private TemplateStore templatesStore;
private String lastSnippetName;
private final Runnable lastSnippetRunnable= new Runnable() {
@Override
public void run() {
WorkbenchUIUtils.refreshCommandElements(SUBMIT_LAST_COMMAND_ID, null, null);
}
};
public RSnippets() {
}
public List<IStringVariable> getVariables() {
final IStringVariable[] variables= VariablesPlugin.getDefault()
.getStringVariableManager().getVariables();
final List<IStringVariable> all= new ArrayList<>(variables.length + 2);
for (int i= 0; i < variables.length; i++) {
all.add(variables[i]);
}
all.add(RESOURCE_ENCODING_VARIABLE);
all.add(ECHO_ENABLED_VARIABLE);
return all;
}
private synchronized void initTemplates() {
if (this.templatesContextTypeRegistry == null) {
this.templatesContextTypeRegistry= new ContributionContextTypeRegistry();
this.templatesContextTypeRegistry.addContextType(new RSnippetTemplateContextType());
this.templatesStore= new ContributionTemplateStore(this.templatesContextTypeRegistry,
RConsoleUIPlugin.getInstance().getPreferenceStore(),
RSnippetTemplateContextType.TEMPLATES_KEY );
try {
this.templatesStore.load();
}
catch (final IOException e) {
RConsoleUIPlugin.log(new Status(IStatus.ERROR, RConsoleUIPlugin.BUNDLE_ID, -1,
"An error occured when loading 'R snippet' template store.", e )); //$NON-NLS-1$
}
}
}
/**
* Returns the template context type registry for the R snippets.
*
* @return the template context type registry
*/
public synchronized ContextTypeRegistry getTemplateContextRegistry() {
if (this.templatesContextTypeRegistry == null) {
initTemplates();
}
return this.templatesContextTypeRegistry;
}
/**
* Returns the template store for the R snippets.
*
* @return the template store
*/
public synchronized TemplateStore getTemplateStore() {
if (this.templatesStore == null) {
initTemplates();
}
return this.templatesStore;
}
private Map<String, IStringVariable> createResolveVariables() {
final ResourceVariableUtil util= new ResourceVariableUtil();
final Map<String, IStringVariable> variables= new HashMap<>();
add(variables, new DynamicVariable.ResolverVariable(
RSnippets.RESOURCE_ENCODING_VARIABLE, new RResourceEncodingVariableResolver(util)));
add(variables, new EchoEnabledVariable());
return variables;
}
private void addPrechecked(final Map<String, IStringVariable> variables) {
final IStringVariableManager manager= VariablesPlugin.getDefault().getStringVariableManager();
for (int i= 0; i < PRECHECKED_NAMES.length; i++) {
final IDynamicVariable real= manager.getDynamicVariable(PRECHECKED_NAMES[i]);
if (real == null) {
continue;
}
try {
final String value= real.getValue(null);
add(variables, new StaticVariable(real, value));
}
catch (final CoreException e) {
add(variables, new UnresolvedVariable(real, e));
}
}
}
public String resolve(final Template template) throws CoreException {
final VariableText2 text= new VariableText2(createResolveVariables()) {
@Override
protected String checkValue(final IStringVariable variable, final String value) {
if (!"selected_text".equals(variable.getName()) //$NON-NLS-1$
&& !RElementNameVariableResolver.R_OBJECT_NAME_NAME.equals(variable.getName()) ) {
return RUtil.escapeCompletely(value);
}
return value;
}
};
return text.performStringSubstitution(template.getPattern(), null);
}
public IStatus validate(final TemplateContextType contextType, final String template) {
try {
final VariableText2 text= new VariableText2(createResolveVariables());
text.validate(template, VariableText2.Severities.CHECK_SYNTAX, null);
return Status.OK_STATUS;
}
catch (final CoreException e) {
return e.getStatus();
}
}
public List<Template> validate(final Template[] templates) {
final Map<String, IStringVariable> variables= createResolveVariables();
addPrechecked(variables);
final VariableText2 text= new VariableText2(variables);
final List<Template> tested= new ArrayList<>(templates.length);
for (final Template template : templates) {
try {
text.validate(template.getPattern(), VariableText2.Severities.RESOLVE, null);
tested.add(template);
}
catch (final CoreException e) {}
}
return tested;
}
public void setLastSnippet(final String name) {
synchronized (this.lastSnippetRunnable) {
if ((this.lastSnippetName != null) ? this.lastSnippetName.equals(name) : null == name) {
return;
}
this.lastSnippetName= name;
}
UIAccess.getDisplay().asyncExec(this.lastSnippetRunnable);
}
public String getLastSnippet() {
synchronized (this.lastSnippetRunnable) {
return this.lastSnippetName;
}
}
public void run(final Template template, final ExecutionEvent event) {
setLastSnippet(template.getName());
try {
final String snippet= resolve(template);
final List<String> lines= TextUtil.toLines(snippet);
RCodeLaunching.runRCodeDirect(lines, false, null);
return;
}
catch (final CoreException e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR,
RConsoleUIPlugin.BUNDLE_ID, 0,
"An error occurred while submitting code snippet to R.\n" +
"Template pattern:\n" + template.getPattern(), e ));
LTKWorkbenchUIUtil.indicateStatus(e.getStatus(), event);
}
}
}