blob: f72330713d5ed2e9105b0ed8a0f694b33431accb [file] [log] [blame]
/*=============================================================================#
# 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.r.ui.text.r;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.jface.text.rules.ITokenScanner;
import org.eclipse.jface.text.rules.Token;
import org.eclipse.statet.ecommons.text.core.input.DocumentParserInput;
import org.eclipse.statet.ecommons.text.ui.presentation.ITextPresentationConstants;
import org.eclipse.statet.ecommons.text.ui.settings.TextStyleManager;
import org.eclipse.statet.ecommons.ui.ISettingsChangedHandler;
import org.eclipse.statet.internal.r.ui.RIdentifierGroups;
import org.eclipse.statet.internal.r.ui.RUIPlugin;
import org.eclipse.statet.r.core.rlang.RTerminal;
import org.eclipse.statet.r.core.rsource.RLexer;
/**
* Text token scanner for R code.
* Mainly for R default partitions, but also works for other partitions (without special styles).
*
* Version 2 uses RLexer instead of rules to parse the sources.
*/
public class RDefaultTextStyleScanner extends DocumentParserInput implements ITokenScanner, ISettingsChangedHandler {
protected static void putAll(final Map<String, IToken> map, final String[] symbols, final IToken token) {
for (int i= 0; i < symbols.length; i++) {
map.put(symbols[i], token);
}
}
protected static void putAll(final Map<RTerminal, IToken> map, final RTerminal[] types, final IToken token) {
for (int i= 0; i < types.length; i++) {
map.put(types[i], token);
}
}
private final RLexer lexer;
private RTerminal lexerToken;
private final EnumMap<RTerminal, IToken> tokens;
private final IToken defaultToken;
private final Map<String, IToken> specialSymbols;
private final TextStyleManager textStyles;
private int currentOffset;
private int currentLength;
public RDefaultTextStyleScanner(final TextStyleManager textStyles) {
this.lexer= createLexer();
this.lexer.reset(this);
this.textStyles= textStyles;
this.defaultToken= getToken(IRTextTokens.SYMBOL_KEY);
this.tokens= new EnumMap<>(RTerminal.class);
registerTokens(this.tokens);
// checkTokenMap();
this.specialSymbols= new HashMap<>();
updateSymbols(this.specialSymbols);
}
protected RLexer createLexer() {
return new RLexer((RLexer.DEFAULT | RLexer.SKIP_WHITESPACE | RLexer.SKIP_LINEBREAK |
RLexer.ENABLE_QUICK_CHECK ));
}
protected void checkTokenMap() {
final RTerminal[] all= RTerminal.values();
for (final RTerminal t : all) {
if (this.tokens.get(t) == null) {
System.out.println("Style Missing for: " + t.name()); //$NON-NLS-1$
}
}
}
protected final RLexer getLexer() {
return this.lexer;
}
protected final TextStyleManager getTextStyles() {
return this.textStyles;
}
@Override
public void setRange(final IDocument document, final int offset, final int length) {
reset(document);
init(offset, offset + length);
this.lexer.reset(this);
this.currentOffset= offset;
this.currentLength= 0;
}
protected void resetSpecialSymbols() {
this.specialSymbols.clear();
updateSymbols(this.specialSymbols);
}
@Override
public IToken nextToken() {
this.currentOffset+= this.currentLength;
if (this.lexerToken == null) {
this.lexerToken= this.lexer.next();
}
this.currentLength= this.lexer.getOffset() - this.currentOffset;
if (this.currentLength != 0) {
return this.defaultToken;
}
this.currentLength= this.lexer.getLength();
return getTokenFromScannerToken();
}
protected IToken getTokenFromScannerToken() {
IToken token;
if (this.lexerToken == RTerminal.SYMBOL) {
final String text= this.lexer.getText();
if (text != null) {
token= this.specialSymbols.get(text);
if (token != null) {
this.lexerToken= null;
return token;
}
}
this.lexerToken= null;
return this.defaultToken;
}
token= this.tokens.get(this.lexerToken);
this.lexerToken= null;
return token;
}
@Override
public int getTokenOffset() {
return this.currentOffset;
}
@Override
public int getTokenLength() {
return this.currentLength;
}
protected IToken getToken(final String key) {
return this.textStyles.getToken(key);
}
@Override
public void handleSettingsChanged(final Set<String> groupIds, final Map<String, Object> options) {
if (groupIds.contains(RIdentifierGroups.GROUP_ID)) {
resetSpecialSymbols();
options.put(ITextPresentationConstants.SETTINGSCHANGE_AFFECTSPRESENTATION_KEY, Boolean.TRUE);
}
}
//-- Concrete associations
protected void registerTokens(final EnumMap<RTerminal,IToken> map) {
map.put(RTerminal.EOF, Token.EOF);
putAll(map, IRTextTokens.FLOWCONTROL, getToken(IRTextTokens.FLOWCONTROL_KEY));
putAll(map, IRTextTokens.GROUPING, getToken(IRTextTokens.GROUPING_KEY));
putAll(map, IRTextTokens.SEPARATOR, getToken(IRTextTokens.SEPARATOR_KEY));
putAll(map, IRTextTokens.ASSIGN, getToken(IRTextTokens.ASSIGN_KEY));
putAll(map, IRTextTokens.ASSIGN_SUB_EQUAL, getToken(IRTextTokens.ASSIGN_SUB_EQUAL_KEY));
putAll(map, IRTextTokens.OP, getToken(IRTextTokens.OP_KEY));
putAll(map, IRTextTokens.OP_SUB_LOGICAL, getToken(IRTextTokens.OP_SUB_LOGICAL_KEY));
putAll(map, IRTextTokens.OP_SUB_RELATIONAL, getToken(IRTextTokens.OP_SUB_RELATIONAL_KEY));
putAll(map, IRTextTokens.SUBACCESS, getToken(IRTextTokens.SUBACCESS_KEY));
putAll(map, IRTextTokens.NSGET, getToken(IRTextTokens.SUBACCESS_KEY));
putAll(map, IRTextTokens.SPECIALCONST, getToken(IRTextTokens.SPECIALCONST_KEY));
putAll(map, IRTextTokens.LOGICALCONST, getToken(IRTextTokens.LOGICALCONST_KEY));
putAll(map, IRTextTokens.SYMBOL, getToken(IRTextTokens.SYMBOL_KEY));
putAll(map, IRTextTokens.NUM, getToken(IRTextTokens.NUM_KEY));
putAll(map, IRTextTokens.NUM_SUB_INT, getToken(IRTextTokens.NUM_SUB_INT_KEY));
putAll(map, IRTextTokens.NUM_SUB_CPLX, getToken(IRTextTokens.NUM_SUB_CPLX_KEY));
putAll(map, IRTextTokens.UNDEFINED, getToken(IRTextTokens.UNDEFINED_KEY));
// usually not in default partition
putAll(map, IRTextTokens.STRING, getToken(IRTextTokens.STRING_KEY));
map.put(RTerminal.SYMBOL_G, getToken(IRTextTokens.STRING_KEY));
map.put(RTerminal.SPECIAL, getToken(IRTextTokens.OP_KEY));
putAll(map, IRTextTokens.COMMENT, getToken(IRTextTokens.COMMENT_KEY));
}
protected void updateSymbols(final Map<String, IToken> map) {
final RIdentifierGroups groups= RUIPlugin.getInstance().getRIdentifierGroups();
groups.getReadLock().lock();
try {
putAll(map, groups.getAssignmentIdentifiers(),
getToken(IRTextTokens.SYMBOL_SUB_ASSIGN_KEY));
putAll(map, groups.getLogicalIdentifiers(),
getToken(IRTextTokens.SYMBOL_SUB_LOGICAL_KEY));
putAll(map, groups.getFlowcontrolIdentifiers(),
getToken(IRTextTokens.SYMBOL_SUB_FLOWCONTROL_KEY));
putAll(map, groups.getCustom1Identifiers(),
getToken(IRTextTokens.SYMBOL_SUB_CUSTOM1_KEY));
putAll(map, groups.getCustom2Identifiers(),
getToken(IRTextTokens.SYMBOL_SUB_CUSTOM2_KEY));
}
finally {
groups.getReadLock().unlock();
}
}
}