All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fife.rsta.ac.js.JavaScriptParser Maven / Gradle / Ivy

/*
 * 01/28/2012
 *
 * Copyright (C) 2012 Robert Futrell
 * robert_futrell at users.sourceforge.net
 * http://fifesoft.com/rsyntaxtextarea
 *
 * This library is distributed under a modified BSD license.  See the included
 * RSTALanguageSupport.License.txt file for details.
 */
package org.fife.rsta.ac.js;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.io.IOException;
import java.util.List;

import javax.swing.text.Element;

import org.fife.io.DocumentReader;
import org.fife.rsta.ac.js.ast.VariableResolver;
import org.fife.ui.rsyntaxtextarea.RSyntaxDocument;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.TextEditorPane;
import org.fife.ui.rsyntaxtextarea.parser.AbstractParser;
import org.fife.ui.rsyntaxtextarea.parser.DefaultParseResult;
import org.fife.ui.rsyntaxtextarea.parser.DefaultParserNotice;
import org.fife.ui.rsyntaxtextarea.parser.ParseResult;
import org.fife.ui.rsyntaxtextarea.parser.ParserNotice;
import org.mozilla.javascript.CompilerEnvirons;
import org.mozilla.javascript.ErrorReporter;
import org.mozilla.javascript.EvaluatorException;
import org.mozilla.javascript.Parser;
import org.mozilla.javascript.RhinoException;
import org.mozilla.javascript.ast.AstRoot;
import org.mozilla.javascript.ast.ErrorCollector;
import org.mozilla.javascript.ast.ParseProblem;


/**
 * Parses JavaScript code in an RSyntaxTextArea.
 * 

* * Like all RSTA Parsers, a JavaScriptParser instance is * notified when the RSTA's text content changes. After a small delay, it will * parse the content as JS code, building an AST and looking for any errors. * When parsing is complete, a property change event of type * {@link #PROPERTY_AST} is fired. Listeners can check the new value of the * property for the AstRoot built that represents the source code * in the text area. *

* * This parser cannot be shared amongst multiple instances of * RSyntaxTextArea. *

* * Please keep in mind that this class is a work-in-progress! * * @author Robert Futrell * @version 1.0 */ public class JavaScriptParser extends AbstractParser { /** * The property change event that's fired when the document is re-parsed. * Applications can listen for this property change and update themselves * accordingly. The "new" value of this property will be an instance of * org.mozilla.javascript.ast.AstRoot. */ public static final String PROPERTY_AST = "AST"; private RSyntaxTextArea textArea; private AstRoot astRoot; private JavaScriptLanguageSupport langSupport; private PropertyChangeSupport support; private DefaultParseResult result; private VariableResolver variableResolver; /** * Constructor. */ public JavaScriptParser(JavaScriptLanguageSupport langSupport, RSyntaxTextArea textArea) { this.textArea = textArea; this.langSupport = langSupport; support = new PropertyChangeSupport(this); result = new DefaultParseResult(this); } /** * Registers a property change listener on this parser. You'll probably * want to listen for changes to {@link #PROPERTY_AST}. * * @param prop The property to listen for changes in. * @param l The listener to add. * @see #removePropertyChangeListener(String, PropertyChangeListener) */ public void addPropertyChangeListener(String prop, PropertyChangeListener l) { support.addPropertyChangeListener(prop, l); } /** * Creates options for Rhino based off of the user's preferences. * * @param errorHandler The container for errors found while parsing. * @return The properties for the JS compiler to use. */ public static CompilerEnvirons createCompilerEnvironment(ErrorReporter errorHandler, JavaScriptLanguageSupport langSupport) { CompilerEnvirons env = new CompilerEnvirons(); env.setErrorReporter(errorHandler); env.setIdeMode(true); env.setRecordingComments(true); env.setRecordingLocalJsDocComments(true); env.setRecoverFromErrors(true); if(langSupport != null) { env.setXmlAvailable(langSupport.isXmlAvailable()); env.setStrictMode(langSupport.isStrictMode()); int version = langSupport.getLanguageVersion(); if (version > 0) { Logger.log("[JavaScriptParser]: JS language version set to: " + version); env.setLanguageVersion(version); } } return env; } /** * Launches jshint as an external process, and gathers syntax errors from * it. * * @param doc the document to parse. * @see #gatherParserErrorsRhino(ErrorCollector, Element) */ private void gatherParserErrorsJsHint(RSyntaxDocument doc) { try { JsHinter.parse(this, textArea, result); } catch (IOException ioe) { // TODO: Localize me? String msg = "Error launching jshint: " + ioe.getMessage(); result.addNotice(new DefaultParserNotice(this, msg, 0)); ioe.printStackTrace(); } } /** * Gathers the syntax errors found by Rhino in-process when parsing the * document. * * @param errorHandler The errors found by Rhino. * @param root The root element of the document parsed. * @see #gatherParserErrorsJsHint(RSyntaxDocument) */ private void gatherParserErrorsRhino(ErrorCollector errorHandler, Element root) { List errors = errorHandler.getErrors(); if (errors != null && errors.size() > 0) { for (ParseProblem problem : errors) { int offs = problem.getFileOffset(); int len = problem.getLength(); int line = root.getElementIndex(offs); String desc = problem.getMessage(); DefaultParserNotice notice = new DefaultParserNotice(this, desc, line, offs, len); if (problem.getType() == ParseProblem.Type.Warning) { notice.setLevel(ParserNotice.Level.WARNING); } result.addNotice(notice); } } } /** * Returns the AST, or null if the editor's content has not * yet been parsed. * * @return The AST, or null. */ public AstRoot getAstRoot() { return astRoot; } public int getJsHintIndent() { return langSupport.getJsHintIndent(); } /** * Returns the location of the .jshintrc file to use if using * JsHint as your error parser. * * @param textArea The text component. * @return The .jshintrc file, or null if none; * in that case, the JsHint defaults will be used. */ public File getJsHintRCFile(RSyntaxTextArea textArea) { // First, get the .jshintrc file in the current file's folder // hierarchy, if it exists. if (textArea instanceof TextEditorPane) { TextEditorPane tep = (TextEditorPane)textArea; File file = new File(tep.getFileFullPath()); File parent = file.getParentFile(); while (parent != null) { File possibleJsHintRc = new File(parent, ".jshintrc"); if (possibleJsHintRc.isFile()) { return possibleJsHintRc; } parent = parent.getParentFile(); } } // If no .jshintrc is found, use the specified fallback. return langSupport.getDefaultJsHintRCFile(); } /** * {@inheritDoc} */ @Override public ParseResult parse(RSyntaxDocument doc, String style) { astRoot = null; result.clearNotices(); // Always spell check all lines, for now. Element root = doc.getDefaultRootElement(); int lineCount = root.getElementCount(); result.setParsedLines(0, lineCount - 1); DocumentReader r = new DocumentReader(doc); ErrorCollector errorHandler = new ErrorCollector(); CompilerEnvirons env = createCompilerEnvironment(errorHandler, langSupport); long start = System.currentTimeMillis(); try { Parser parser = new Parser(env); astRoot = parser.parse(r, null, 0); long time = System.currentTimeMillis() - start; result.setParseTime(time); } catch (IOException ioe) { // Never happens result.setError(ioe); ioe.printStackTrace(); } catch (RhinoException re) { // Shouldn't happen since we're passing an ErrorCollector in int line = re.lineNumber(); // if (line>0) { Element elem = root.getElement(line); int offs = elem.getStartOffset(); int len = elem.getEndOffset() - offs - 1; String msg = re.details(); result.addNotice(new DefaultParserNotice(this, msg, line, offs, len)); // } } catch (Exception e) { result.setError(e); // catch all } r.close(); // Get any parser errors. switch (langSupport.getErrorParser()) { default: case RHINO: gatherParserErrorsRhino(errorHandler, root); break; case JSHINT: gatherParserErrorsJsHint(doc); break; } // addNotices(doc); support.firePropertyChange(PROPERTY_AST, null, astRoot); return result; } public void setVariablesAndFunctions(VariableResolver variableResolver) { this.variableResolver = variableResolver; } public VariableResolver getVariablesAndFunctions() { return variableResolver; } /** * Removes a property change listener from this parser. * * @param prop The property that was being listened to. * @param l The listener to remove. * @see #addPropertyChangeListener(String, PropertyChangeListener) */ public void removePropertyChangeListener(String prop, PropertyChangeListener l) { support.removePropertyChangeListener(prop, l); } /** * Error reporter for Rhino-based parsing. */ public static class JSErrorReporter implements ErrorReporter { @Override public void error(String message, String sourceName, int line, String lineSource, int lineOffset) { } @Override public EvaluatorException runtimeError(String message, String sourceName, int line, String lineSource, int lineOffset) { return null; } @Override public void warning(String message, String sourceName, int line, String lineSource, int lineOffset) { } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy