| /*=============================================================================# |
| # 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; |
| } |
| |
| } |