// Copyright 2012 Google Inc. All Rights Reserved. // // Licensed 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.google.collide.client.code.gotodefinition; import com.google.collide.client.documentparser.DocumentParser; import com.google.collide.client.util.DeferredCommandExecutor; import com.google.collide.client.util.logging.Log; import com.google.collide.codemirror2.Token; import com.google.collide.json.shared.JsonArray; import com.google.collide.shared.document.LineInfo; import com.google.collide.shared.util.JsonCollections; import com.google.common.base.Preconditions; import javax.annotation.Nullable; /** * Lazily parses source lines. That is, defers parsing for some time. * 2 possible states are - line is parsed, parse results are ready, and * parsing is not finished, no results available. * */ public class DeferringLineParser { private final DocumentParser parser; private LineInfo scheduledParseLineInfo; private int parsedLineNumber = -1; private JsonArray<Token> parsedLineTokens; private final DeferredCommandExecutor parseExecutor = new DeferredCommandExecutor(25) { @Override protected boolean execute() { parseScheduledLine(); return false; } }; private void parseScheduledLine() { Preconditions.checkState(scheduledParseLineInfo != null); parsedLineTokens = parseLine(scheduledParseLineInfo); parsedLineNumber = scheduledParseLineInfo.number(); scheduledParseLineInfo = null; } private JsonArray<Token> parseLine(LineInfo lineInfo) { JsonArray<Token> tokens = parser.parseLineSync(lineInfo.line()); if (tokens == null) { tokens = JsonCollections.createArray(); } Log.debug(getClass(), "Line " + lineInfo.number() + " parsed, number of tokens: " + tokens.size()); return tokens; } /** * Creates a new deferring parser. * * @param parser document parser to use */ public DeferringLineParser(DocumentParser parser) { this.parser = parser; } /** * Returns available parse results or schedules parsing. Does not collect * parse results, i.e. at given time parse results only for a single line * are available. * * @param lineInfo line to parse * @param blocking whether to block until the line is parsed * @return parsed tokens or {@code null} if no results available now */ JsonArray<Token> getParseResult(LineInfo lineInfo, boolean blocking) { // TODO: We should get parser state here. if (parsedLineNumber == lineInfo.number()) { Preconditions.checkState(parsedLineTokens != null); return parsedLineTokens; } else if (blocking) { scheduleParse(null); // Cancel anything currently scheduled. parsedLineTokens = parseLine(lineInfo); parsedLineNumber = lineInfo.number(); return parsedLineTokens; } else { scheduleParse(lineInfo); return null; } } private void scheduleParse(@Nullable LineInfo lineInfo) { if (lineInfo == null && scheduledParseLineInfo == null) { return; } if (scheduledParseLineInfo != null && lineInfo != null && scheduledParseLineInfo.number() == lineInfo.number()) { return; } if (scheduledParseLineInfo != null) { parseExecutor.cancel(); // Cancel anything currently scheduled. } scheduledParseLineInfo = lineInfo; if (scheduledParseLineInfo == null) { return; } parseExecutor.schedule(2); Log.debug(getClass(), "Scheduled line parse for line number " + scheduledParseLineInfo.number()); } }