/* * Copyright (c) 2015-2015 Vladimir Schneider <vladimir.schneider@gmail.com> * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package com.vladsch.idea.multimarkdown.parser; import com.intellij.lexer.Lexer; import com.intellij.lexer.LexerPosition; import com.intellij.psi.tree.IElementType; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.pegdown.ast.RootNode; public class MultiMarkdownLexer extends Lexer { private static final Logger logger = Logger.getLogger(MultiMarkdownLexer.class); protected int startOffset = 0; protected int endOffset = 0; protected int lexemeIndex = 0; protected int currentOffset = 0; protected CharSequence buffer = null; protected MultiMarkdownLexParser.LexerToken lexerToken = null; protected MultiMarkdownLexParser.LexerToken[] lexerTokens = null; protected Integer pegdownExtensions = null; protected Integer parsingTimeout = null; public MultiMarkdownLexer() { super(); } public MultiMarkdownLexer(int pegdownExtensions) { super(); this.pegdownExtensions = pegdownExtensions; } public MultiMarkdownLexer(int pegdownExtensions, int parsingTimeout) { super(); this.pegdownExtensions = pegdownExtensions; this.parsingTimeout = parsingTimeout; } protected void logStackTrace() { StackTraceElement[] traceElements = Thread.currentThread().getStackTrace(); for (StackTraceElement traceElement : traceElements) { logger.info(traceElement.getMethodName() + " at " + traceElement.getFileName() + ":" + traceElement.getLineNumber()); } } @Override public void start(@NotNull CharSequence buffer, int startOffset, int endOffset, int initialState) { //logger = MultiMarkdownPlugin.getInstance().getLogger(); //logger.info("LexStart(" + String.valueOf(startOffset) + ", " + String.valueOf(endOffset) + ", " + String.valueOf(initialState) + " for " + this.toString() + " from " + Thread.currentThread().toString()); //logStackTrace(); this.buffer = buffer; this.currentOffset = this.startOffset = startOffset; this.endOffset = endOffset; lexemeIndex = initialState; lexerTokens = null; if (buffer.length() > 0) { lexerTokens = MultiMarkdownLexParserManager.parseMarkdown(buffer, pegdownExtensions, parsingTimeout); } lexerToken = null; //logger.info(String.format("start lexer buffer end %d, start %d, end %d, state %d", buffer.length(), startOffset, endOffset, initialState)); // prime the lexeme stream, if the first is white space we need to start with that if (lexerTokens != null && lexerTokens.length > 0) { lexerToken = lexerTokens[lexemeIndex]; if (currentOffset <= lexerToken.getRange().getStart()) { lexerToken = MultiMarkdownLexParser.getSkippedSpaceToken(currentOffset, lexerToken.getRange().getStart()); } else { lexemeIndex++; } } if (lexerToken == null) { // create a dummy whitespace token for the whole file lexerToken = MultiMarkdownLexParser.getSkippedSpaceToken(currentOffset, this.endOffset); } currentOffset = lexerToken.getRange().getEnd(); //assert currentOffset <= endOffset; if (currentOffset > endOffset) { currentOffset = endOffset; } } @Override public int getState() { return lexemeIndex; } @Nullable @Override public IElementType getTokenType() { //return lexerToken != null && lexerToken.getRange().getStart() < endOffset ? lexerToken.getElementType() : null; return lexerToken != null ? lexerToken.getElementType() : null; } @Override public int getTokenStart() { return lexerToken != null ? lexerToken.getRange().getStart() : endOffset; } @Override public int getTokenEnd() { //return lexerToken != null && lexerToken.getRange().getEnd() <= endOffset ? lexerToken.getRange().getEnd() : endOffset; return lexerToken != null ? lexerToken.getRange().getEnd() : endOffset; } @Override public void advance() { if (currentOffset < endOffset) { do { if (lexerTokens != null && lexemeIndex >= 0 && lexemeIndex < lexerTokens.length) { if (lexerToken == null || currentOffset < lexerToken.getRange().getStart()) { lexerToken = MultiMarkdownLexParser.getSkippedSpaceToken(currentOffset, lexerTokens[lexemeIndex].getRange().getStart()); } else { lexerToken = lexerTokens[lexemeIndex]; if (currentOffset < lexerToken.getRange().getStart()) { lexerToken = MultiMarkdownLexParser.getSkippedSpaceToken(currentOffset, lexerToken.getRange().getStart()); } else { lexemeIndex++; } } } else { if (currentOffset < endOffset) { lexerToken = MultiMarkdownLexParser.getSkippedSpaceToken(currentOffset, endOffset); } else { lexerToken = null; } } } while (lexerToken != null && lexerToken.getRange().getEnd() < currentOffset); currentOffset = lexerToken == null ? endOffset : lexerToken.getRange().getEnd(); } else { lexerToken = null; } //assert currentOffset <= endOffset; if (currentOffset > endOffset) { lexerToken = null; currentOffset = endOffset; } //logger.info("advanced to " + currentOffset + " (" + (lexerToken == null ? "null" : lexerToken.toString()) + ")"); } class MarkdownLexerPosition implements LexerPosition { protected int offset; protected int state; MarkdownLexerPosition(int offset, int state) { this.offset = offset; this.state = state; } @Override public int getOffset() { return offset; } @Override public int getState() { return state; } } @NotNull @Override public LexerPosition getCurrentPosition() { return new MarkdownLexerPosition(currentOffset, lexemeIndex); } @Override public void restore(@NotNull LexerPosition lexerPosition) { currentOffset = lexerPosition.getOffset(); lexemeIndex = lexerPosition.getState(); lexerToken = null; advance(); } @NotNull @Override public CharSequence getBufferSequence() { return buffer; } @Override public int getBufferEnd() { return buffer.length(); } }