blob: 2c50c59d8b969febd819e3d85f3ee8828422319c [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2007, 2021 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.core.rsource;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.text.AbstractDocument;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.statet.ecommons.text.IndentUtil;
import org.eclipse.statet.ecommons.text.IndentUtil.IndentEditAction;
import org.eclipse.statet.internal.r.core.RCorePlugin;
import org.eclipse.statet.ltk.ast.core.AstNode;
import org.eclipse.statet.ltk.ast.core.AstVisitor;
import org.eclipse.statet.r.core.RCodeStyleSettings;
import org.eclipse.statet.r.core.RCore;
import org.eclipse.statet.r.core.RCoreAccess;
import org.eclipse.statet.r.core.rsource.ast.Block;
import org.eclipse.statet.r.core.rsource.ast.CForLoop;
import org.eclipse.statet.r.core.rsource.ast.CIfElse;
import org.eclipse.statet.r.core.rsource.ast.CRepeatLoop;
import org.eclipse.statet.r.core.rsource.ast.CWhileLoop;
import org.eclipse.statet.r.core.rsource.ast.FCall;
import org.eclipse.statet.r.core.rsource.ast.FDef;
import org.eclipse.statet.r.core.rsource.ast.GenericVisitor;
import org.eclipse.statet.r.core.rsource.ast.Group;
import org.eclipse.statet.r.core.rsource.ast.NodeType;
import org.eclipse.statet.r.core.rsource.ast.RAstNode;
import org.eclipse.statet.r.core.rsource.ast.SourceComponent;
import org.eclipse.statet.r.core.rsource.ast.SubIndexed;
import org.eclipse.statet.r.core.source.RHeuristicTokenScanner;
/**
* Indents R source code in a document
*/
public class RSourceIndenter {
private IndentUtil util;
private final RHeuristicTokenScanner scanner;
private final ComputeIndentVisitor computeVisitor;
private AbstractDocument document;
private AstNode rootNode;
private RCodeStyleSettings codeStyle;
private int refLine;
private int firstLine;
private int lastLine;
private int[] lineOffsets;
private int[] lineLevels;
private ScopeFactory factory;
private class ComputeIndentVisitor extends GenericVisitor implements AstVisitor {
private int startOffset;
private int stopOffset;
private int currentLine;
void computeIndent() throws InvocationTargetException {
try {
this.currentLine= (RSourceIndenter.this.refLine >= 0) ? RSourceIndenter.this.refLine : RSourceIndenter.this.firstLine;
this.startOffset= RSourceIndenter.this.document.getLineOffset(this.currentLine);
this.stopOffset= RSourceIndenter.this.document.getLineOffset(RSourceIndenter.this.lastLine) + RSourceIndenter.this.document.getLineLength(RSourceIndenter.this.lastLine);
RSourceIndenter.this.rootNode.accept(this);
}
catch (final BadLocationException e) {
throw new InvocationTargetException(e);
}
}
private final boolean checkOffset(final int offset) {
if (offset >= RSourceIndenter.this.lineOffsets[this.currentLine]) { // offset is first char in line
do {
RSourceIndenter.this.lineLevels[this.currentLine]= RSourceIndenter.this.factory.getIndent(this.currentLine);
} while (offset >= RSourceIndenter.this.lineOffsets[++this.currentLine]);
return true;
}
return false;
}
private void checkBeforeOffset(final int offset) {
if (offset >= RSourceIndenter.this.lineOffsets[this.currentLine + 1]) { // offset is first char in line
final int level= RSourceIndenter.this.factory.getIndent(this.currentLine);
do {
RSourceIndenter.this.lineLevels[this.currentLine++]= level;
} while (offset >= RSourceIndenter.this.lineOffsets[this.currentLine + 1]);
}
}
private boolean checkNode(final RAstNode node) throws InvocationTargetException {
final int offset= node.getStartOffset();
if (checkOffset(offset)) {
return (node.getEndOffset() >= RSourceIndenter.this.lineOffsets[this.currentLine]);
}
// touches format region
if (node.getEndOffset() >= this.startOffset && offset <= this.stopOffset) {
return true;
}
// not interesting
return false;
}
private final void checkExprListChilds(final RAstNode node) throws InvocationTargetException {
try {
final int count= node.getChildCount();
for (int i= 0; i < count; i++) {
final RAstNode child= node.getChild(i);
RSourceIndenter.this.factory.createCommonExprScope(child.getStartOffset(), child);
child.acceptInR(this);
RSourceIndenter.this.factory.leaveScope();
}
} catch (final BadLocationException e) {
throw new InvocationTargetException(e);
}
}
@Override
public void visit(final AstNode node) throws InvocationTargetException {
if (node.getEndOffset() >= this.startOffset && node.getStartOffset() <= this.stopOffset) {
if (node instanceof RAstNode) {
((RAstNode) node).acceptInR(this);
}
else {
node.acceptInChildren(this);
}
}
}
@Override
public void visit(final SourceComponent node) throws InvocationTargetException {
try {
RSourceIndenter.this.factory.createSourceScope(0, node);
if (node.getEndOffset() >= this.startOffset && node.getStartOffset() <= this.stopOffset) {
checkExprListChilds(node);
}
checkOffset(Integer.MAX_VALUE - 2);
RSourceIndenter.this.factory.leaveScope();
} catch (final BadLocationException e) {
throw new InvocationTargetException(e);
}
}
@Override
public void visit(final Block node) throws InvocationTargetException {
try {
RSourceIndenter.this.factory.createBlockScope(node.getStartOffset(), node);
if (checkNode(node)) {
RSourceIndenter.this.factory.updateEnterBrackets();
checkExprListChilds(node);
checkBeforeOffset(node.getEndOffset());
RSourceIndenter.this.factory.updateLeaveBrackets();
checkOffset(node.getEndOffset());
}
RSourceIndenter.this.factory.leaveScope();
} catch (final BadLocationException e) {
throw new InvocationTargetException(e);
}
}
@Override
public void visit(final Group node) throws InvocationTargetException {
try {
if (checkNode(node)) {
RSourceIndenter.this.factory.createGroupContScope(node.getStartOffset() + 1, node.getExprChild());
node.getExprChild().acceptInR(this);
checkBeforeOffset(node.getEndOffset());
checkOffset(node.getEndOffset());
RSourceIndenter.this.factory.leaveScope();
}
} catch (final BadLocationException e) {
throw new InvocationTargetException(e);
}
}
private final void checkControlCondChild(final int open, final RAstNode child, final int close) throws InvocationTargetException {
try {
if (open >= 0) {
checkOffset(open);
RSourceIndenter.this.factory.createControlCondScope(open + 1, child);
child.acceptInR(this);
checkBeforeOffset(close);
checkOffset(close);
RSourceIndenter.this.factory.leaveScope();
}
} catch (final BadLocationException e) {
throw new InvocationTargetException(e);
}
}
private final void checkControlContChild(final RAstNode child) throws InvocationTargetException {
try {
RSourceIndenter.this.factory.createControlContScope(child.getStartOffset(), child);
child.acceptInR(this);
RSourceIndenter.this.factory.leaveScope();
} catch (final BadLocationException e) {
throw new InvocationTargetException(e);
}
}
@Override
public void visit(final CIfElse node) throws InvocationTargetException {
try {
boolean inElseIf= false;
if (node.getRParent().getNodeType() == NodeType.C_IF
&& ((CIfElse) node.getParent()).getElseChild() == node) {
RSourceIndenter.this.factory.leaveScope();
inElseIf= true;
}
else {
RSourceIndenter.this.factory.createControlScope(node.getStartOffset(), node);
}
if (checkNode(node)) {
checkControlCondChild(node.getCondOpenOffset(), node.getCondChild(), node.getCondCloseOffset());
checkControlContChild(node.getThenChild());
if (node.hasElse()) {
checkOffset(node.getElseOffset());
checkControlContChild(node.getElseChild());
}
checkOffset(node.getEndOffset());
}
if (inElseIf) {
RSourceIndenter.this.factory.createDummy();
}
else {
RSourceIndenter.this.factory.leaveScope();
}
} catch (final BadLocationException e) {
throw new InvocationTargetException(e);
}
}
@Override
public void visit(final CForLoop node) throws InvocationTargetException {
try {
RSourceIndenter.this.factory.createControlScope(node.getStartOffset(), node);
if (checkNode(node)) {
checkControlCondChild(node.getCondOpenOffset(), node.getCondChild(), node.getCondCloseOffset());
checkControlContChild(node.getContChild());
checkOffset(node.getEndOffset());
}
RSourceIndenter.this.factory.leaveScope();
} catch (final BadLocationException e) {
throw new InvocationTargetException(e);
}
}
@Override
public void visit(final CWhileLoop node) throws InvocationTargetException {
try {
RSourceIndenter.this.factory.createControlScope(node.getStartOffset(), node);
if (checkNode(node)) {
checkControlCondChild(node.getCondOpenOffset(), node.getCondChild(), node.getCondCloseOffset());
checkControlContChild(node.getContChild());
checkOffset(node.getEndOffset());
}
RSourceIndenter.this.factory.leaveScope();
} catch (final BadLocationException e) {
throw new InvocationTargetException(e);
}
}
@Override
public void visit(final CRepeatLoop node) throws InvocationTargetException {
try {
RSourceIndenter.this.factory.createControlScope(node.getStartOffset(), node);
if (checkNode(node)) {
checkControlContChild(node.getContChild());
checkOffset(node.getEndOffset());
}
RSourceIndenter.this.factory.leaveScope();
} catch (final BadLocationException e) {
throw new InvocationTargetException(e);
}
}
private final void checkArglist(final RAstNode node) throws InvocationTargetException {
try {
RSourceIndenter.this.factory.createArglistScope(node.getStartOffset(), node);
if (checkNode(node)) {
node.acceptInRChildren(this);
// checkBeforeOffset(node.getEndOffset());
checkOffset(node.getEndOffset());
}
RSourceIndenter.this.factory.leaveScope();
} catch (final BadLocationException e) {
throw new InvocationTargetException(e);
}
}
private final void checkFDeflist(final RAstNode node) throws InvocationTargetException {
try {
RSourceIndenter.this.factory.createFDeflistScope(node.getStartOffset(), node);
if (checkNode(node)) {
node.acceptInRChildren(this);
// checkBeforeOffset(node.getEndOffset());
checkOffset(node.getEndOffset());
}
RSourceIndenter.this.factory.leaveScope();
} catch (final BadLocationException e) {
throw new InvocationTargetException(e);
}
}
private final void checkArg(final RAstNode node) throws InvocationTargetException {
try {
RSourceIndenter.this.factory.createCommonExprScope(node.getStartOffset(), node);
if (checkNode(node)) {
node.acceptInRChildren(this);
checkOffset(node.getEndOffset());
}
RSourceIndenter.this.factory.leaveScope();
} catch (final BadLocationException e) {
throw new InvocationTargetException(e);
}
}
@Override
public void visit(final FDef node) throws InvocationTargetException {
try {
RSourceIndenter.this.factory.createFDefScope(node.getStartOffset(), node);
if (checkNode(node)) {
node.getArgsChild().acceptInR(this);
RSourceIndenter.this.factory.updateEnterFDefBody();
checkControlContChild(node.getContChild());
checkOffset(node.getEndOffset());
}
RSourceIndenter.this.factory.leaveScope();
} catch (final BadLocationException e) {
throw new InvocationTargetException(e);
}
}
@Override
public void visit(final FDef.Args node) throws InvocationTargetException {
checkFDeflist(node);
}
@Override
public void visit(final FDef.Arg node) throws InvocationTargetException {
checkArg(node);
}
@Override
public void visit(final FCall node) throws InvocationTargetException {
try {
RSourceIndenter.this.factory.createFCallScope(node.getStartOffset(), node);
if (checkNode(node)) {
node.getRefChild().acceptInR(this);
node.getArgsChild().acceptInR(this);
checkOffset(node.getEndOffset());
}
RSourceIndenter.this.factory.leaveScope();
} catch (final BadLocationException e) {
throw new InvocationTargetException(e);
}
}
@Override
public void visit(final FCall.Args node) throws InvocationTargetException {
checkArglist(node);
}
@Override
public void visit(final FCall.Arg node) throws InvocationTargetException {
checkArg(node);
}
@Override
public void visit(final SubIndexed node) throws InvocationTargetException {
try {
RSourceIndenter.this.factory.createFCallScope(node.getStartOffset(), node);
if (checkNode(node)) {
node.getRefChild().acceptInR(this);
node.getArgsChild().acceptInR(this);
checkOffset(node.getEndOffset());
}
RSourceIndenter.this.factory.leaveScope();
} catch (final BadLocationException e) {
throw new InvocationTargetException(e);
}
}
@Override
public void visit(final SubIndexed.Args node) throws InvocationTargetException {
checkArglist(node);
}
@Override
public void visit(final SubIndexed.Arg node) throws InvocationTargetException {
checkArg(node);
}
@Override
public void visitNode(final RAstNode node) throws InvocationTargetException {
if (checkNode(node)) {
node.acceptInRChildren(this);
checkOffset(node.getEndOffset());
}
}
}
public RSourceIndenter(final RHeuristicTokenScanner scanner) {
this.scanner= scanner;
this.computeVisitor= new ComputeIndentVisitor();
}
public RSourceIndenter(final RHeuristicTokenScanner scanner, final RCoreAccess access) {
this(scanner);
setup(access);
}
// public void indent(final AbstractDocument document, final AstInfo<RAstNode> ast, final int firstLine, final int lastLine,
// final RCoreAccess access, final WorkingContext context) throws CoreException {
// try {
// setup(document, ast, access);
// computeIndent(firstLine, lastLine);
// final MultiTextEdit edits= createEdits();
// if (edits != null && edits.getChildrenSize() > 0) {
// context.syncExec(new SourceDocumentRunnable(document, this.ast.stamp,
// (edits.getChildrenSize() > 50) ? DocumentRewriteSessionType.SEQUENTIAL : DocumentRewriteSessionType.SEQUENTIAL) {
// @Override
// public void run(final AbstractDocument document) throws InvocationTargetException {
// try {
// edits.apply(document, TextEdit.NONE);
// }
// catch (final MalformedTreeException e) {
// throw new InvocationTargetException(e);
// }
// catch (final BadLocationException e) {
// throw new InvocationTargetException(e);
// }
// }
// });
// }
// }
// catch (final InvocationTargetException e) {
// throw createFailedException(e);
// }
// catch (final BadLocationException e) {
// throw createFailedException(e);
// }
// }
// public void indentLine(final AbstractDocument document, final AstInfo<RAstNode> ast, final int line,
// final RCoreAccess access) throws CoreException {
// try {
// setup(document, ast, access);
// computeIndent(line, 0);
// final MultiTextEdit edits= createEdits();
// if (edits != null && edits.getChildrenSize() > 0) {
// edits.apply(document);
// }
// }
// catch (BadLocationException e) {
// throw createFailedException(e);
// }
// }
public void setup(final RCoreAccess access) {
this.codeStyle= access.getRCodeStyle();
}
/**
* Release resources from last computation.
* After clear, you can not longer call the <code>get...(...)</code> methods.
*/
public void clear() {
this.document= null;
this.rootNode= null;
this.codeStyle= null;
this.util= null;
this.lineLevels= null;
}
public int getNewIndentColumn(final int line) throws BadLocationException {
return getNewIndentColumn(line, this.document.getLineOffset(line));
}
private int getNewIndentColumn(final int line, final int lineOffset) throws BadLocationException {
if (getDocumentChar(lineOffset) == '#' && getDocumentChar(lineOffset + 1) != '#') {
return 0;
}
return this.lineLevels[line];
}
public int getNewIndentOffset(final int line) {
try {
return this.util.getIndentedOffsetAt(line, this.lineLevels[line]);
} catch (final BadLocationException e) {
return -1;
}
}
public TextEdit getIndentEdits(final AbstractDocument document, final AstNode root,
final int codeOffset, final int firstLine, final int lastLine) throws CoreException {
try {
this.document= document;
this.rootNode= root;
computeIndent(codeOffset, firstLine, lastLine);
return createEdits();
}
catch (final BadLocationException e) {
throw createFailedException(e);
}
}
protected void computeIndent(final int codeOffset, final int firstLine, final int lastLine) throws BadLocationException {
try {
this.codeStyle.getReadLock().lock();
this.util= new IndentUtil(this.document, this.codeStyle);
this.firstLine= firstLine;
this.lastLine= lastLine;
this.scanner.configure(this.document);
this.refLine= -1;
int cand= this.firstLine;
SEARCH_REF_LINE : while (cand > 0) {
int refOffset= this.scanner.findAnyNonBlankBackward(this.document.getLineOffset(cand), RHeuristicTokenScanner.UNBOUND, true);
if (refOffset >= codeOffset) { // line found
cand= this.document.getLineOfOffset(refOffset);
refOffset= this.scanner.findAnyNonBlankForward(this.document.getLineOffset(cand), refOffset + 1, true);
if (this.document.getChar(refOffset) != '#' || this.document.getChar(refOffset + 1) == '#') {
this.refLine= cand;
break SEARCH_REF_LINE;
}
}
break SEARCH_REF_LINE;
}
final int startLine= (this.refLine >= 0) ? this.refLine : this.firstLine;
final int count= this.document.getNumberOfLines(0, this.document.getLineOffset(this.lastLine));
this.lineLevels= new int[count + 2];
Arrays.fill(this.lineLevels, -1);
this.lineOffsets= new int[count + 3];
for (int i= startLine; i < count; i++) {
this.lineOffsets[i]= this.document.getLineOffset(i);
}
this.lineOffsets[count]= (count < this.document.getNumberOfLines()) ? this.document.getLineOffset(count) : this.document.getLength();
this.lineOffsets[count]= Integer.MAX_VALUE;
this.lineOffsets[count + 1]= Integer.MAX_VALUE;
this.lineOffsets[count + 2]= Integer.MAX_VALUE;
this.factory= new ScopeFactory(this.util, this.codeStyle, this.document);
this.computeVisitor.computeIndent();
correctLevels();
} catch (final InvocationTargetException e) {
final Throwable targetException= e.getTargetException();
if (targetException instanceof BadLocationException) {
throw (BadLocationException) targetException;
}
else {
RCorePlugin.logError("Unexpected error while indent sources", e); //$NON-NLS-1$
}
}
finally {
if (this.codeStyle != null) {
this.codeStyle.getReadLock().unlock();
this.codeStyle= null;
}
this.factory= null;
this.lineOffsets= null;
}
}
protected void correctLevels() throws BadLocationException {
int shift= 0;
if (this.refLine >= 0) {
this.lineLevels[this.refLine]= this.lineLevels[this.refLine];
shift= this.util.getLineIndent(this.refLine, false)[IndentUtil.COLUMN_IDX] - this.lineLevels[this.refLine];
this.lineLevels[this.refLine]+= shift;
}
else {
shift= 0;
}
// System.out.println("SHIFT=" + shift);
// if (fRefLine > 0) {
// System.out.println("REF=" + " " + (fRefLine + 1) + " (" + fLineOffsets[fRefLine] + " ): " + fLineLevels[fRefLine]);
// }
// else {
// System.out.println("NOREF");
// }
// for (int i= this.firstLine; i <= this.lastLine; i++) {
// System.out.println(" " + (i + 1) + " (" + this.lineOffsets[i] + " ): " + this.lineLevels[i]);
// }
// System.out.println();
this.lineLevels[this.firstLine]+= + shift;
if (this.lineLevels[this.firstLine] < 0) {
shift-= this.lineLevels[this.firstLine];
this.lineLevels[this.firstLine]= 0;
}
for (int line= this.firstLine + 1; line <= this.lastLine; line++) {
this.lineLevels[line]+= shift;
if (this.lineLevels[line] < 0) {
this.lineLevels[line]= 0;
}
}
}
protected MultiTextEdit createEdits() throws BadLocationException, CoreException {
final MultiTextEdit edits= new MultiTextEdit();
final IndentEditAction action= new IndentEditAction() {
@Override
public int getIndentColumn(final int line, final int lineOffset) throws BadLocationException {
return getNewIndentColumn(line, lineOffset);
}
@Override
public void doEdit(final int line, final int offset, final int length, final StringBuilder text)
throws BadLocationException {
if (text != null) {
edits.addChild(new ReplaceEdit(offset, length, text.toString()));
}
}
};
this.util.changeIndent(this.firstLine, this.lastLine, action);
return edits;
}
protected final int getDocumentChar(final int idx) throws BadLocationException {
if (idx >= 0 && idx < this.document.getLength()) {
return this.document.getChar(idx);
}
return -1;
}
protected CoreException createFailedException(final Throwable e) {
return new CoreException(new Status(Status.ERROR, RCore.BUNDLE_ID, -1, "Indentation failed", e));
}
}
class ScopeFactory {
private static interface IndentStrategy {
int getIndent(Scope scope, int line);
}
public final static class Scope {
int baseColumn;
int startLine;
RAstNode commandNode;
Scope parent;
IndentStrategy strategy;
int getIndent(final int line) {
return this.strategy.getIndent(this, line);
}
}
private static final int POOL_SIZE= 50;
private final int levelMult;
private final int wrappedCol;
private final int blockCol;
private Scope scope;
private final Scope[] pool= new Scope[POOL_SIZE];
private int poolPointer= 0;
private final IndentUtil util;
private final RCodeStyleSettings style;
private final AbstractDocument doc;
public ScopeFactory(final IndentUtil util, final RCodeStyleSettings style, final AbstractDocument doc) {
this.util= util;
this.style= style;
this.doc= doc;
this.levelMult= this.util.getLevelColumns();
this.wrappedCol= this.style.getIndentWrappedCommandDepth()*this.levelMult;
this.blockCol= this.style.getIndentBlockDepth()*this.levelMult;
}
private class FirstLineStrategy implements IndentStrategy {
@Override
public int getIndent(final Scope scope, final int line) {
if (line <= scope.startLine) {
return scope.baseColumn;
}
else {
return scope.baseColumn + ScopeFactory.this.wrappedCol;
}
}
}
private class FixStrategy implements IndentStrategy {
@Override
public int getIndent(final Scope scope, final int line) {
return scope.baseColumn;
}
}
private final IndentStrategy FIX_STRAT= new FixStrategy();
private final IndentStrategy FIRSTLINE_STRAT= new FirstLineStrategy();
private final void initNew(final int offset, final int line, final RAstNode node, final IndentStrategy strat, final int baseColumn) {
Scope scope;
if (this.poolPointer < POOL_SIZE) {
if (this.pool[this.poolPointer] == null) {
this.pool[this.poolPointer]= new Scope();
}
scope= this.pool[this.poolPointer];
}
else {
scope= new Scope();
}
this.poolPointer++;
scope.parent= this.scope;
scope.baseColumn= baseColumn;
scope.strategy= strat;
scope.startLine= line;
scope.commandNode= node;
this.scope= scope;
}
// private final void updateCommandLine(final int offset, final RAstNode node) {
// if (this.scope.commandDepth <= 1) {
// while (offset >= this.lineOffsets[this.commandStartLine + 1]) {
// this.commandStartLine++;
// }
// this.scope.commandNode= node;
// this.scope.commandStartLine= this.commandStartLine;
// }
// }
public final Scope createDummy() {
initNew(0, 0, null, null, 0);
return this.scope;
}
public final void createSourceScope(final int offset, final RAstNode node) throws BadLocationException {
final int line= this.doc.getLineOfOffset(offset);
initNew(offset, line, node, this.FIX_STRAT, 0);
}
public final void createBlockScope(final int offset, final RAstNode node) throws BadLocationException {
final int line= this.doc.getLineOfOffset(offset);
switch (node.getRParent().getNodeType()) {
case C_IF:
case C_FOR:
case C_WHILE:
case F_DEF:
if (node.getRParent().getChild(0) == node) {
// first are conditions
break;
}
//$FALL-THROUGH$
case C_REPEAT:
// use control level instead of cont level
initNew(node.getStartOffset(), line, node, this.FIX_STRAT, this.scope.parent.baseColumn);
return;
default:
break;
}
initNew(node.getStartOffset(), line, node, this.FIX_STRAT, this.scope.getIndent(line));
}
public final void createCommonExprScope(final int offset, final RAstNode node) throws BadLocationException {
final int line= this.doc.getLineOfOffset(offset);
initNew(offset, line, node, this.FIRSTLINE_STRAT, this.scope.getIndent(line));
}
public final void createGroupContScope(final int offset, final RAstNode node) throws BadLocationException {
final int line= this.doc.getLineOfOffset(offset);
initNew(offset, line, node, this.FIX_STRAT, this.scope.getIndent(line + 1) + this.style.getIndentGroupDepth()*this.levelMult);
}
public final void createControlScope(final int offset, final RAstNode node) throws BadLocationException {
final int line= this.doc.getLineOfOffset(offset);
initNew(offset, line, node, this.FIX_STRAT, this.scope.getIndent(line));
boolean compact= true;
if (compact && node.getNodeType() == NodeType.C_IF
&& ((CIfElse) node).hasElse()) {
compact= false;
}
if (!useParent(compact, false, node)) {
this.scope.baseColumn= this.scope.parent.getIndent(line + 1);
}
}
public final void createControlCondScope(final int offset, final RAstNode node) throws BadLocationException {
final int line= this.doc.getLineOfOffset(offset);
initNew(offset, line, node, this.FIRSTLINE_STRAT, this.scope.getIndent(line));
}
public final void createControlContScope(final int offset, final RAstNode node) throws BadLocationException {
final int line= this.doc.getLineOfOffset(offset);
initNew(offset, line, node, this.FIRSTLINE_STRAT, this.scope.getIndent(line) + this.blockCol);
}
public final void createFCallScope(final int offset, final RAstNode node) throws BadLocationException {
final int line= this.doc.getLineOfOffset(offset);
initNew(offset, line, node, this.FIX_STRAT, this.scope.getIndent(line));
if (!useParent(true, true, node)) {
this.scope.baseColumn= this.scope.parent.getIndent(line + 1);
}
}
public final void createFDefScope(final int offset, final RAstNode node) throws BadLocationException {
final int line= this.doc.getLineOfOffset(offset);
initNew(offset, line, node, this.FIX_STRAT, this.scope.getIndent(line));
}
public final void createFDeflistScope(final int offset, final RAstNode node) throws BadLocationException {
final int line= this.doc.getLineOfOffset(offset);
initNew(offset, line, node, this.FIX_STRAT, this.scope.getIndent(line) + this.wrappedCol);
}
public final void createArglistScope(final int offset, final RAstNode node) throws BadLocationException {
final int line= this.doc.getLineOfOffset(offset);
// TODO: this can cause deep indentation - use compact detection of parent?
initNew(offset, line, node, this.FIX_STRAT, this.scope.getIndent(line) + this.wrappedCol);
}
public final void leaveScope() {
this.scope= this.scope.parent;
this.poolPointer--;
}
private final boolean useParent(final boolean compact, final boolean onlyAssignments, RAstNode node) throws BadLocationException {
if (this.scope.parent.commandNode == node) {
return true;
}
if (compact
&& this.scope.startLine == this.scope.parent.startLine
&& sameLine(this.scope.commandNode.getEndOffset(), this.scope.parent.commandNode.getEndOffset())
) {
return true;
}
if (onlyAssignments) {
ITER_OPS : while (true) {
node= node.getRParent();
switch (node.getNodeType()) {
case A_LEFT:
case A_RIGHT:
case A_EQUALS:
case A_COLON:
continue ITER_OPS;
case BLOCK:
case SOURCELINES:
case C_IF:
case C_FOR:
case C_WHILE:
case C_REPEAT:
case F_CALL_ARG:
case F_DEF_ARG:
case SUB_INDEXED_ARG:
return true;
default:
break ITER_OPS;
}
}
}
return false;
}
private final boolean sameLine(final int offset1, final int offset2) throws BadLocationException {
return (offset1 == offset2
|| this.doc.getLineOfOffset(offset1) == this.doc.getLineOfOffset(offset2));
}
public final void updateEnterBrackets() {
this.scope.baseColumn+= this.blockCol;
}
public final void updateLeaveBrackets() {
this.scope.baseColumn-= this.blockCol;
}
public final void updateEnterFDefBody() throws BadLocationException {
if (useParent(true, true, this.scope.commandNode)) {
this.scope.baseColumn= this.scope.parent.baseColumn;
}
}
public final int getIndent(final int line) {
return this.scope.getIndent(line);
}
}