blob: 4db6fc96030282b1cb78c3f8cf7ec344a054809c [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2005, 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.editors.templates;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.templates.TemplateContextType;
import org.eclipse.jface.text.templates.TemplateVariable;
import org.eclipse.text.edits.DeleteEdit;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.jcommons.text.core.TextRegion;
import org.eclipse.statet.jcommons.text.core.input.StringParserInput;
import org.eclipse.statet.ecommons.text.IndentUtil;
import org.eclipse.statet.ecommons.text.IndentUtil.IndentEditAction;
import org.eclipse.statet.internal.r.ui.RUIPlugin;
import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditor;
import org.eclipse.statet.ltk.ui.templates.SourceEditorTemplateContext;
import org.eclipse.statet.r.core.IRCoreAccess;
import org.eclipse.statet.r.core.RCodeStyleSettings;
import org.eclipse.statet.r.core.RCore;
import org.eclipse.statet.r.core.rlang.RTerminal;
import org.eclipse.statet.r.core.rlang.RTokens;
import org.eclipse.statet.r.core.rsource.RLexer;
import org.eclipse.statet.r.ui.editors.IRSourceEditor;
@NonNullByDefault
public class REditorContext extends SourceEditorTemplateContext {
public REditorContext(final TemplateContextType type, final IDocument document,
final TextRegion region,
final ISourceEditor editor, final int flags) {
super(type, document, region, editor, flags);
}
protected IRCoreAccess getRCoreAccess() {
final ISourceEditor editor= getEditor();
return (editor instanceof IRSourceEditor) ?
((IRSourceEditor) editor).getRCoreAccess() :
RCore.WORKBENCH_ACCESS;
}
@Override
public void setVariable(final String name, @Nullable String value) {
if (SELECTION_VAR_NAME.equals(name) && value != null && value.length() > 0) {
try {
final IDocument valueDoc = new Document(value);
final IndentUtil util = new IndentUtil(valueDoc, getRCoreAccess().getRCodeStyle());
final int column = util.getMultilineIndentColumn(0, valueDoc.getNumberOfLines()-1);
if (column > 0) {
final IndentEditAction action = new IndentEditAction(column) {
@Override
public void doEdit(final int line, final int offset, final int length, final StringBuilder text)
throws BadLocationException {
TextEdit edit;
if (text != null) {
final int position = util.getIndentedOffsetAt(text, column);
edit = new ReplaceEdit(offset, length, text.substring(position, text.length()));
}
else {
final int end = util.getIndentedOffsetAt(line, column);
edit = new DeleteEdit(offset, end-offset);
}
edit.apply(valueDoc, 0);
}
};
util.editInIndent(0, valueDoc.getNumberOfLines()-1, action);
setVariable(INDENTATION_VAR_NAME, util.createIndentString(column));
value = valueDoc.get();
}
}
catch (final BadLocationException e) {
RUIPlugin.logError(RUIPlugin.INTERNAL_ERROR, "An error occurred while computing indentation variable for R editor templates.", e); //$NON-NLS-1$
}
}
super.setVariable(name, value);
}
@Override
protected void format(final MultiTextEdit root, final IDocument templateDoc,
final TemplateVariable[] variables, final IDocument baseDoc)
throws BadLocationException {
indent(root, templateDoc, variables, baseDoc);
if ((getFlags() & FORMAT_START) != 0 && getVariable(INDENTATION_VAR_NAME) == null
&& getStart() > 0 && !RTokens.isWhitespace(baseDoc.getChar(getStart() - 1))
&& templateDoc.getLength() > 0 && !RTokens.isWhitespace(templateDoc.getChar(0)) ) {
final String line= templateDoc.get(0, templateDoc.getLineLength(0));
final RLexer lexer= new RLexer();
lexer.reset(new StringParserInput(line).init());
final RCodeStyleSettings rCodeStyle= getRCoreAccess().getRCodeStyle();
final RTerminal token= lexer.next();
if (getSpaceBefore(token, rCodeStyle)) {
root.addChild(new InsertEdit(0, " ")); //$NON-NLS-1$
}
if (variables.length == 0 && line.length() == templateDoc.getLength()
&& lexer.next() == RTerminal.EOF
&& getSpaceAfter(token, rCodeStyle) ) {
root.addChild(new InsertEdit(line.length(), " ")); //$NON-NLS-1$
}
}
}
protected boolean getSpaceBefore(final RTerminal terminal, final RCodeStyleSettings rCodeStyle) {
switch (terminal) {
case EQUAL:
return rCodeStyle.getWhitespaceArgAssignBefore();
case ARROW_LEFT_S:
case ARROW_LEFT_D:
return rCodeStyle.getWhitespaceAssignBefore();
case ARROW_RIGHT_S:
case ARROW_RIGHT_D:
return rCodeStyle.getWhitespaceArgAssignAfter();
case PLUS:
case MINUS:
case MULT:
case DIV:
case OR:
case OR_D:
case AND:
case AND_D:
case NOT:
case POWER:
case SEQ:
case SPECIAL:
case COLON_EQUAL:
case TILDE:
case REL_NE:
case REL_EQ:
case REL_LT:
case REL_LE:
case REL_GT:
case REL_GE:
return rCodeStyle.getWhitespaceOtherOpBefore();
default:
return true;
}
}
protected boolean getSpaceAfter(final RTerminal terminal, final RCodeStyleSettings rCodeStyle) {
switch (terminal) {
case EQUAL:
return rCodeStyle.getWhitespaceArgAssignAfter();
case ARROW_LEFT_S:
case ARROW_LEFT_D:
return rCodeStyle.getWhitespaceAssignAfter();
case ARROW_RIGHT_S:
case ARROW_RIGHT_D:
return rCodeStyle.getWhitespaceArgAssignBefore();
case PLUS:
case MINUS:
case MULT:
case DIV:
case OR:
case OR_D:
case AND:
case AND_D:
case NOT:
case POWER:
case SEQ:
case SPECIAL:
case COLON_EQUAL:
case TILDE:
case REL_NE:
case REL_EQ:
case REL_LT:
case REL_LE:
case REL_GT:
case REL_GE:
return rCodeStyle.getWhitespaceOtherOpAfter();
default:
return true;
}
}
}