package org.python.pydev.core.partition; import java.util.ArrayList; import org.eclipse.jface.text.rules.ICharacterScanner; import org.eclipse.jface.text.rules.IPredicateRule; import org.eclipse.jface.text.rules.IToken; import org.eclipse.jface.text.rules.Token; import org.python.pydev.shared_core.partitioner.IChangeTokenRule; import org.python.pydev.shared_core.partitioner.IMarkScanner; import org.python.pydev.shared_core.string.FastStringBuffer; public class SingleLineRuleWithMultipleStarts implements IPredicateRule, IChangeTokenRule { protected IToken fToken; private final char escapeCharacter; private final boolean escapeContinuesLine; private ArrayList<char[]> fStartSequences; private char[] fEndSequence; @Override public void setToken(IToken token) { this.fToken = token; } public SingleLineRuleWithMultipleStarts(String[] startSequences, String endSequence, Token token, char escapeCharacter, boolean escapeContinuesLine) { ArrayList<char[]> lst = new ArrayList<>(startSequences.length); for (String start : startSequences) { lst.add(start.toCharArray()); } this.fStartSequences = lst; this.fEndSequence = endSequence.toCharArray(); this.fToken = token; this.escapeCharacter = escapeCharacter; this.escapeContinuesLine = escapeContinuesLine; } @Override public IToken evaluate(ICharacterScanner scanner) { return evaluate(scanner, false); } @Override public IToken getSuccessToken() { return fToken; } @Override public IToken evaluate(ICharacterScanner scanner, boolean resume) { if (resume) { if (detectEnd(scanner)) { return fToken; } } else { IMarkScanner markScanner = (IMarkScanner) scanner; int mark = markScanner.getMark(); int c; int size = fStartSequences.size(); for (int j = 0; j < size; j++) { boolean found = true; char[] startSequence = fStartSequences.get(j); for (int i = 0; i < startSequence.length; i++) { c = scanner.read(); if (c != startSequence[i]) { //Backup to where we started found = false; markScanner.setMark(mark); break; } } if (found) { break; } else { //Didn't find... go to next (unless we checked all: in this case return that //we didn't match the start). if (j == size - 1) { return Token.UNDEFINED; } } } //if it got here, the start was detected if (detectEnd(scanner)) { return fToken; } else { markScanner.setMark(mark); } } return Token.UNDEFINED; } private boolean detectEnd(ICharacterScanner scanner) { while (true) { int c = scanner.read(); if (c == ICharacterScanner.EOF) { //match return true; } else if (c == escapeCharacter) { if (escapeContinuesLine) { //Consume new line and keep on matching c = scanner.read(); if (c == '\r') { c = scanner.read(); if (c != '\n') { scanner.unread(); } } } else { //Escape does not continue line: if it's a new line, match it (but don't consume it). c = scanner.read(); if (c == '\r' || c == '\n') { scanner.unread(); } return true; } } else if (c == '\r' || c == '\n') { //If it's a new line, match it (but don't consume it). // scanner.unread(); return true; } else if (c == fEndSequence[0]) { // Let's check if we had a match: if we did, return true, otherwise, keep on going. //matched first. Let's check the remainder boolean found = true; for (int i = 1; i < fEndSequence.length; i++) { c = scanner.read(); if (c != fEndSequence[i]) { found = false; scanner.unread(); for (int j = 0; j < i; j++) { scanner.unread(); } break; } } return found; } } } @Override public String toString() { FastStringBuffer buf = new FastStringBuffer("SingleLineRuleWithMultipleStarts(", fEndSequence.length + 40); buf.append("start: "); for (char[] chars : this.fStartSequences) { buf.append(chars).append(",\n"); } buf.append("end: ") .append(fEndSequence) .append(")"); return buf.toString(); } }