| /*=============================================================================# |
| # Copyright (c) 2007, 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.redocs.tex.r.core.source; |
| |
| import static org.eclipse.statet.ecommons.text.core.treepartitioner.TreePartitionNode.END_UNCLOSED; |
| |
| import org.eclipse.jface.text.rules.ICharacterScanner; |
| |
| import org.eclipse.statet.ecommons.text.core.treepartitioner.TreePartitionNode; |
| import org.eclipse.statet.ecommons.text.core.treepartitioner.TreePartitionNodeScan; |
| import org.eclipse.statet.ecommons.text.core.treepartitioner.WrappedPartitionNodeScan; |
| |
| import org.eclipse.statet.docmlet.tex.core.source.LtxPartitionNodeScanner; |
| import org.eclipse.statet.docmlet.tex.core.source.LtxPartitionNodeType; |
| import org.eclipse.statet.docmlet.tex.core.source.LtxPartitionNodeType.VerbatimInline; |
| import org.eclipse.statet.r.core.source.RPartitionNodeScanner; |
| import org.eclipse.statet.r.core.source.RPartitionNodeType; |
| |
| |
| /** |
| * Paritition scanner for LaTeX with chunks. |
| * |
| * Stops if find '<<' at column 0 and handles 'Sexpr' control word. |
| */ |
| public class LtxRweavePartitionNodeScanner extends LtxPartitionNodeScanner { |
| |
| |
| protected static final int S_RVERB= S_EXT_LTX + 1; |
| protected static final int S_RCHUNK= S_EXT_LTX + 2; |
| |
| private static final VerbatimInline SEXPR_LTX_TYPE= new VerbatimInline('}') { |
| |
| @Override |
| public byte getScannerState() { |
| return S_RVERB; |
| } |
| |
| }; |
| |
| private static final LtxPartitionNodeType RCHUNK_LTX_TYPE= new LtxPartitionNodeType() { |
| |
| @Override |
| public String getPartitionType() { |
| return TexRweaveDocumentConstants.RCHUNK_BASE_CONTENT_TYPE; |
| } |
| |
| @Override |
| public byte getScannerState() { |
| return S_RCHUNK; |
| } |
| }; |
| |
| private static final char[] SEQ_Sexpr= "Sexpr".toCharArray(); //$NON-NLS-1$ |
| |
| |
| private final RChunkPartitionNodeScanner rScanner= new RChunkPartitionNodeScanner(); |
| |
| private WrappedPartitionNodeScan rScan; |
| |
| private int rStartOffset; |
| private TreePartitionNode rStartNode; |
| |
| |
| public LtxRweavePartitionNodeScanner() { |
| super(); |
| } |
| |
| public LtxRweavePartitionNodeScanner(final boolean templateMode) { |
| super(templateMode); |
| } |
| |
| |
| @Override |
| public void execute(final TreePartitionNodeScan scan) { |
| this.rScan= new WrappedPartitionNodeScan(scan); |
| |
| super.execute(scan); |
| |
| this.rScan= null; |
| } |
| |
| |
| @Override |
| protected void init() { |
| final TreePartitionNode beginNode= getScan().getBeginNode(); |
| if (beginNode.getType() instanceof RPartitionNodeType) { |
| this.rStartOffset= getScan().getStartOffset(); |
| this.rStartNode= beginNode; |
| |
| final TreePartitionNode rRootNode= RPartitionNodeScanner.findRRootNode(beginNode); |
| if (rRootNode.getType() == RChunkPartitionNodeScanner.R_CHUNK_BASE_TYPE) { |
| initNode(rRootNode, RCHUNK_LTX_TYPE); |
| } |
| else { |
| initNode(rRootNode, SEXPR_LTX_TYPE); |
| } |
| return; |
| } |
| |
| this.rStartNode= null; |
| super.init(); |
| } |
| |
| |
| @Override |
| protected void handleNewLine(final LtxPartitionNodeType type) { |
| if (this.reader.readTemp('<', '<')) { |
| if (type == RCHUNK_LTX_TYPE) { // setup by #init |
| assert (this.rStartNode != null); |
| return; |
| } |
| assert (this.rStartNode == null); |
| this.rStartOffset= this.reader.getOffset(); |
| addNode(RChunkPartitionNodeScanner.R_CHUNK_BASE_TYPE, RCHUNK_LTX_TYPE, |
| this.rStartOffset ); |
| this.rStartNode= getNode(); |
| } |
| } |
| |
| @Override |
| protected boolean searchExtCommand(final int c) { |
| if (c == 'S' && this.reader.readConsuming2(SEQ_Sexpr)) { |
| this.reader.readConsumingWhitespace(); |
| if (this.reader.read('{')) { |
| assert (this.rStartNode == null); |
| this.rStartOffset= this.reader.getOffset(); |
| addNode(this.rScanner.getDefaultRootType(), SEXPR_LTX_TYPE, |
| this.rStartOffset ); |
| this.rStartNode= getNode(); |
| processExt(SEXPR_LTX_TYPE); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| protected void processExt(final LtxPartitionNodeType type) { |
| final TreePartitionNode node; |
| switch (type.getScannerState()) { |
| |
| case S_RVERB: |
| node= this.rStartNode; |
| this.rStartNode= null; |
| |
| processInline(); // includes exitNode |
| |
| if (node.getLength() > 0) { |
| this.rScan.init(this.rStartOffset, node.getEndOffset(), node); |
| this.rScanner.execute(this.rScan); |
| this.rScan.exit(); |
| } |
| return; |
| |
| case S_RCHUNK: |
| node= this.rStartNode; |
| this.rStartNode= null; |
| |
| this.rScan.init(this.rStartOffset, this.rScan.getDocument().getLength(), node); |
| this.rScanner.execute(this.rScan); |
| this.rScan.exit(); |
| |
| final TreePartitionNode chunkNode= getNode(); |
| exitNode(); |
| setRange(chunkNode.getEndOffset(), getScan().getDocument().getLength()); |
| return; |
| } |
| |
| super.processExt(type); |
| } |
| |
| protected void processInline() { |
| int expandVar= 0; |
| LOOP: while (true) { |
| switch (this.reader.read()) { |
| case ICharacterScanner.EOF: |
| exitNode(this.reader.getOffset(), END_UNCLOSED); // required for rweave |
| this.last= LAST_EOF; |
| return; |
| case '\r': |
| exitNode(this.reader.getOffset() - 1, END_UNCLOSED); |
| this.reader.read('\n'); |
| this.last= LAST_NEWLINE; |
| return; |
| case '\n': |
| exitNode(this.reader.getOffset() - 1, END_UNCLOSED); |
| this.last= LAST_NEWLINE; |
| return; |
| case '{': |
| if (this.reader.read('{')) { |
| expandVar++; |
| continue LOOP; |
| } |
| continue LOOP; |
| case '}': |
| if (expandVar > 0 && this.reader.read('}')) { |
| expandVar--; |
| continue LOOP; |
| } |
| exitNode(this.reader.getOffset() - 1, 0); |
| this.last= LAST_OTHER; |
| return; |
| default: |
| continue LOOP; |
| } |
| } |
| } |
| |
| } |