blob: 8f95b73d0c94515667eb15f70922dbb328b5b6a4 [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.internal.r.core.builder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.statet.jcommons.collections.IntArrayList;
import org.eclipse.statet.jcommons.collections.IntList;
import org.eclipse.statet.jcommons.text.core.TextLineInformation;
import org.eclipse.statet.jcommons.text.core.TextRegion;
import org.eclipse.statet.ecommons.text.core.JFaceTextRegion;
public class RdParser {
public class LineInformation implements TextLineInformation {
private final IntList offsets;
public LineInformation() {
this.offsets= new IntArrayList();
}
public void addLine(final int offset) {
this.offsets.add(offset);
}
@Override
public int getNumberOfLines() {
return this.offsets.size();
}
@Override
public int getLineOfOffset(final int offset) {
if (this.offsets.size() == 0) {
return -1;
}
int low= 0;
int high= this.offsets.size() - 1;
while (low <= high) {
final int mid= (low + high) >> 1;
final int lineOffset= this.offsets.getAt(mid);
if (lineOffset < offset) {
low= mid + 1;
}
else if (lineOffset > offset) {
high= mid - 1;
}
else {
return mid;
}
}
return low - 1;
}
@Override
public int getStartOffset(final int line) {
if (line < 0 || line >= this.offsets.size()) {
return -1;
}
return this.offsets.getAt(line);
}
@Override
public int getEndOffset(final int line) {
return (line + 1 == this.offsets.size()) ? RdParser.this.content.length : this.offsets.getAt(line + 1);
}
@Override
public int getLength(final int line) {
return (line + 1 == this.offsets.size()) ?
(RdParser.this.content.length - this.offsets.getAt(line)) :
(this.offsets.getAt(line + 1) - this.offsets.getAt(line) );
}
@Override
public TextRegion getRegion(final int line) {
return JFaceTextRegion.newByStartEnd(this.offsets.getAt(line),
(line + 1 == this.offsets.size()) ?
RdParser.this.content.length :
this.offsets.getAt(line + 1) );
}
}
private static final char[][] PLATFORM_KEYWORDS= { // without '#'
"ifdef".toCharArray(), "ifndef".toCharArray(), "endif".toCharArray() }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
private enum Last { NONE, NEWLINE, BACKSLASH }
private final RTaskMarkerHandler markers;
private final char[] content;
private int currentOffset= 0;
private final int currentLine= 1;
private Last lastChar= Last.NONE;
private final LineInformation lineStructure;
public RdParser(final char[] content, final RTaskMarkerHandler markers) {
this.content= content;
this.markers= markers;
this.lineStructure= new LineInformation();
}
public void check() throws CoreException {
READ: for (; this.currentOffset < this.content.length; this.currentOffset++) {
if (checkNewLine()) {
continue READ;
}
if (checkBackslash()) {
continue READ;
}
final char current= this.content[this.currentOffset];
switch (current) {
case '%':
readComment();
continue READ;
case '#':
if (this.lastChar == Last.NEWLINE) {
CHECK_KEYS: for (int i= 0; i < PLATFORM_KEYWORDS.length; i++) {
int offset= this.currentOffset + 1;
CHECK_KEYCHARS: for (int j= 0; j < PLATFORM_KEYWORDS[i].length; j++) {
if (offset < this.content.length && PLATFORM_KEYWORDS[i][j] == this.content[offset++]) {
continue CHECK_KEYCHARS;
}
continue CHECK_KEYS;
}
readPlatformInstruction(PLATFORM_KEYWORDS[i]);
}
}
continue READ;
}
}
}
private void readPlatformInstruction(final char[] keyword) {
final int start= this.currentOffset;
int end= this.currentOffset;
READ: for (this.currentOffset++; this.currentOffset < this.content.length; this.currentOffset++) {
end= this.currentOffset;
if (checkNewLine()) {
break READ;
}
}
}
private void readComment() throws CoreException {
final int start= this.currentOffset;
int end= this.currentOffset;
READ: for (this.currentOffset++; this.currentOffset < this.content.length; this.currentOffset++) {
end= this.currentOffset;
if (checkNewLine()) {
end--;
break READ;
}
}
try {
this.markers.checkForTasks(new String(this.content, start, end - start + 1), start, this.lineStructure);
}
catch (final BadLocationException e) {
}
}
private boolean checkNewLine() {
final char current= this.content[this.currentOffset];
if (current == '\r' || current == '\n') {
if (current == '\r' && this.currentOffset + 1 < this.content.length
&& this.content[this.currentOffset + 1] == '\n' ) {
this.currentOffset++;
}
this.lineStructure.addLine(this.currentOffset);
this.lastChar= Last.NEWLINE;
return true;
}
return false;
}
private boolean checkBackslash() {
if (this.content[this.currentOffset] == '\\') {
this.lastChar= Last.BACKSLASH;
return true;
}
if (this.lastChar == Last.BACKSLASH) {
this.lastChar= Last.NONE;
return true;
}
return false;
}
}