Skip to content

Commit

Permalink
fixes #1 - incorrect line numbers
Browse files Browse the repository at this point in the history
Replaced the "Official LESS CSS Java library" w/ pure Rhino JS / less.js library to get accurate line numbers for syntax errors, etc.

Added error notifications w/ clickable line numbers that place the caret at the source of the error.  Notifications persist until the user interacts w/ them and are logged in IntelliJ's "Event Log" window.
  • Loading branch information
acdvorak committed Oct 26, 2012
1 parent 1a05527 commit c6dfa45
Show file tree
Hide file tree
Showing 33 changed files with 22,583 additions and 40 deletions.
4 changes: 4 additions & 0 deletions .gitignore
@@ -1,2 +1,6 @@
.idea
/out
target
build
build-archive

18 changes: 18 additions & 0 deletions asual-lesscss-engine/asual-lesscss-engine.iml
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="lib" level="project" />
</component>
</module>

12 changes: 12 additions & 0 deletions asual-lesscss-engine/pom.xml
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>asual-lesscss-engine</groupId>
<artifactId>asual-lesscss-engine</artifactId>
<version>1.0</version>


</project>
226 changes: 226 additions & 0 deletions asual-lesscss-engine/src/main/java/com/asual/lesscss/LessEngine.java
@@ -0,0 +1,226 @@
/*
* 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.asual.lesscss;

import com.asual.lesscss.loader.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mozilla.javascript.*;
import org.mozilla.javascript.tools.shell.Global;

import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
* @author Rostislav Hristov
* @author Uriah Carpenter
* @author Noah Sloan
*/
public class LessEngine {

private final Log logger = LogFactory.getLog(getClass());

private final LessOptions options;
private final ResourceLoader loader;

private Scriptable scope;
private Function compile;

public LessEngine() {
this(new LessOptions());
}

public LessEngine(LessOptions options) {
this(options, defaultResourceLoader(options));
}

private static ResourceLoader defaultResourceLoader(LessOptions options) {
ResourceLoader resourceLoader = new ChainedResourceLoader(
new FilesystemResourceLoader(), new ClasspathResourceLoader(
LessEngine.class.getClassLoader()),
new HTTPResourceLoader());
if(options.isCss()) {
return new CssProcessingResourceLoader(resourceLoader);
}
resourceLoader = new UnixNewlinesResourceLoader(resourceLoader);
return resourceLoader;
}

/**
*
* @param options
* @param loader
* @see <a href="http://www.envjs.com/doc/guides#running-embed">Embedding EnvJS</a>
*/
public LessEngine(LessOptions options, ResourceLoader loader) {
this.options = options;
this.loader = loader;
try {
logger.debug("Initializing LESS Engine.");

ClassLoader classLoader = getClass().getClassLoader();

URL less = options.getLess();
URL env = classLoader.getResource("env.js");
URL envjs = classLoader.getResource("env.rhino.1.2.js");
URL engine = classLoader.getResource("engine.js");
URL cssmin = classLoader.getResource("cssmin.js");

// Context cx = Context.enter();
Context cx = ContextFactory.getGlobal().enterContext();

// cx.setOptimizationLevel(9);
cx.setOptimizationLevel(-1);

cx.setLanguageVersion(Context.VERSION_1_5);

logger.debug("Using implementation version: " + cx.getImplementationVersion());

// Global global = Main.getGlobal();
Global global = new Global();

global.init(cx);

scope = cx.initStandardObjects(global);

cx.evaluateReader(scope, new InputStreamReader(env.openConnection().getInputStream()), envjs.getFile(), 1, null);
cx.evaluateReader(scope, new InputStreamReader(env.openConnection().getInputStream()), env.getFile(), 1, null);
Scriptable lessEnv = (Scriptable) scope.get("lessenv", scope);
lessEnv.put("charset", lessEnv, options.getCharset());
lessEnv.put("css", lessEnv, options.isCss());
lessEnv.put("loader", lessEnv, Context.javaToJS(loader, scope));
cx.evaluateReader(scope, new InputStreamReader(less.openConnection().getInputStream()), less.getFile(), 1, null);
cx.evaluateReader(scope, new InputStreamReader(cssmin.openConnection().getInputStream()), cssmin.getFile(), 1, null);
cx.evaluateReader(scope, new InputStreamReader(engine.openConnection().getInputStream()), engine.getFile(), 1, null);
compile = (Function) scope.get("compile", scope);
Context.exit();
} catch (Exception e) {
logger.error("LESS Engine intialization failed.", e);
}
}

public String compile(String input) throws LessException {
return compile(input, null, false);
}

public String compile(String input, String location) throws LessException {
return compile(input, location, false);
}

public String compile(String input, String location, boolean compress) throws LessException {
try {
long time = System.currentTimeMillis();
String result = call(compile, new Object[] { input,
location == null ? "" : location, compress });
logger.debug("The compilation of '" + input + "' took " + (System.currentTimeMillis () - time) + " ms.");
return result;
} catch (Exception e) {
throw parseLessException(e);
}
}

public String compile(URL input) throws LessException {
return compile(input, false);
}

public String compile(URL input, boolean compress) throws LessException {
try {
long time = System.currentTimeMillis();
String location = input.toString();
logger.debug("Compiling URL: " + location);
String source = loader.load(location, options.getCharset());
String result = call(compile, new Object[] {source, location, compress});
logger.debug("The compilation of '" + input + "' took " + (System.currentTimeMillis () - time) + " ms.");
return result;
} catch (Exception e) {
throw parseLessException(e);
}
}

public String compile(File input) throws LessException {
return compile(input, false);
}

public String compile(File input, boolean compress) throws LessException {
try {
long time = System.currentTimeMillis();
String location = input.getAbsolutePath();
logger.debug("Compiling File: " + "file:" + location);
String source = loader.load(location, options.getCharset());
String result = call(compile, new Object[] {source, location, compress});
logger.debug("The compilation of '" + input + "' took " + (System.currentTimeMillis () - time) + " ms.");
return result;
} catch (Exception e) {
throw parseLessException(e);
}
}

public void compile(File input, File output) throws LessException, IOException {
compile(input, output, false);
}

public void compile(File input, File output, boolean compress) throws LessException, IOException {
try {
String content = compile(input, compress);
if (!output.exists()) {
output.createNewFile();
}
BufferedWriter bw = new BufferedWriter(new FileWriter(output));
bw.write(content);
bw.close();
} catch (Exception e) {
throw parseLessException(e);
}
}

private synchronized String call(Function fn, Object[] args) {
return (String) Context.call(null, fn, scope, scope, args);
}

private LessException parseLessException(Exception root) throws LessException {
logger.debug("Parsing LESS Exception", root);
if (root instanceof JavaScriptException) {
Scriptable value = (Scriptable) ((JavaScriptException) root).getValue();
String type = ScriptableObject.getProperty(value, "type").toString() + " Error";
String message = ScriptableObject.getProperty(value, "message").toString();
String filename = "";
if (ScriptableObject.getProperty(value, "filename") != null) {
filename = ScriptableObject.getProperty(value, "filename").toString();
}
int line = -1;
if (ScriptableObject.getProperty(value, "line") != null) {
line = ((Double) ScriptableObject.getProperty(value, "line")).intValue();
}
int column = -1;
if (ScriptableObject.getProperty(value, "column") != null) {
column = ((Double) ScriptableObject.getProperty(value, "column")).intValue();
}
List<String> extractList = new ArrayList<String>();
if (ScriptableObject.getProperty(value, "extract") != null) {
NativeArray extract = (NativeArray) ScriptableObject.getProperty(value, "extract");
for (int i = 0; i < extract.getLength(); i++) {
if (extract.get(i, extract) instanceof String) {
extractList.add(((String) extract.get(i, extract)).replace("\t", " "));
}
}
}
throw new LessException(message, type, filename, line, column, extractList);
}
throw new LessException(root);
}

}
@@ -0,0 +1,94 @@
/*
* 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.asual.lesscss;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.net.URISyntaxException;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;

/**
* @author Rostislav Hristov
*/
public class LessEngineCli {

public static void main(String[] args) throws LessException, URISyntaxException {
Options cmdOptions = new Options();
cmdOptions.addOption(LessOptions.CHARSET_OPTION, true, "Input file charset encoding. Defaults to UTF-8.");
cmdOptions.addOption(LessOptions.COMPRESS_OPTION, false, "Flag that enables compressed CSS output.");
cmdOptions.addOption(LessOptions.CSS_OPTION, false, "Flag that enables compilation of .css files.");
cmdOptions.addOption(LessOptions.LESS_OPTION, true, "Path to a custom less.js for Rhino version.");
try {
CommandLineParser cmdParser = new GnuParser();
CommandLine cmdLine = cmdParser.parse(cmdOptions, args);
LessOptions options = new LessOptions();
if (cmdLine.hasOption(LessOptions.CHARSET_OPTION)) {
options.setCharset(cmdLine.getOptionValue(LessOptions.CHARSET_OPTION));
}
if (cmdLine.hasOption(LessOptions.COMPRESS_OPTION)) {
options.setCompress(true);
}
if (cmdLine.hasOption(LessOptions.CSS_OPTION)) {
options.setCss(true);
}
if (cmdLine.hasOption(LessOptions.LESS_OPTION)) {
options.setLess(new File(cmdLine.getOptionValue(LessOptions.LESS_OPTION)).toURI().toURL());
}
LessEngine engine = new LessEngine(options);
if (System.in.available() != 0) {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
StringWriter sw = new StringWriter();
char[] buffer = new char[1024];
int n = 0;
while (-1 != (n = in.read(buffer))) {
sw.write(buffer, 0, n);
}
String src = sw.toString();
if (!src.isEmpty()) {
System.out.println(engine.compile(src, null, options.isCompress()));
System.exit(0);
}
}
String[] files = cmdLine.getArgs();
if (files.length == 1) {
System.out.println(engine.compile(new File(files[0]), options.isCompress()));
System.exit(0);
}
if (files.length == 2) {
engine.compile(new File(files[0]), new File(files[1]), options.isCompress());
System.exit(0);
}

} catch (IOException ioe) {
System.err.println("Error opening input file.");
} catch (ParseException pe) {
System.err.println("Error parsing arguments.");
}
String[] paths = LessEngine.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath().split(File.separator);
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("java -jar " + paths[paths.length - 1] + " input [output] [options]", cmdOptions);
System.exit(1);
}

}

0 comments on commit c6dfa45

Please sign in to comment.