Skip to content

Instantly share code, notes, and snippets.

@jlieske
Last active May 19, 2016 06:47
Show Gist options
  • Save jlieske/49d91810cc9fc9670a1a1f145a056489 to your computer and use it in GitHub Desktop.
Save jlieske/49d91810cc9fc9670a1a1f145a056489 to your computer and use it in GitHub Desktop.
Script for BBEdit to perform check (but not compilation) of JavaScript program using Google's Closure Compiler.
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Check JavaScript with Closure Compiler:
# Script to perform check (but not compilation) of front window using
# Google's Closure Compiler.
# Place this script in the script menu and invoke it from BBEdit.
# Copyright Jay Lieske Jr.
# 11 May 2016
import sys, re, os, subprocess, struct
from Foundation import NSAppleEventDescriptor
from ScriptingBridge import SBApplication
def getFrontBBEditDocument():
BBEdit = SBApplication.applicationWithBundleIdentifier_(
"com.barebones.BBEdit")
document = BBEdit.documents().firstObject()
if document:
return document.file().path()
else:
return None
def invokeClosureCompiler(filePath):
"""Call the Closure Compiler to check the filePath, returning stderr
from the run."""
command = []
# Closure Compiler driver script can be installed by Homebrew.
# Annoyingly, the script menu does not respect the shell path.
command.append("/usr/local/bin/closure-compiler")
command.extend(["--checks-only", "--warning_level=VERBOSE",
"--process_common_js_modules"])
command.append(filePath)
# Run the compiler and capture the output.
try:
output = subprocess.check_output(command, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError, e:
output = e.output
return output
def fourCharCode(string):
"Convert a four-char code string into a 32-bit int."
return struct.unpack('>I', string)[0]
def processMessages(compilerOutput):
# Matches just the error messages from the compiler:
# a file name, followed by a colon and line number, an optional column number,
# and then the error message.
# Since BBEdit doesn't accept column numbers, it doesn't try to parse
# other positional feedback from the compiler.
# To avoid false matches, this version doesn't allow spaces in file names.
pat = re.compile(r"^([^: ]+):(\d+)(?:[.:-]\d+)*[:\s]+(.+)$")
# Distinguish warning from errors.
warning = re.compile(r"warning", re.IGNORECASE)
warning_kind = NSAppleEventDescriptor.descriptorWithEnumCode_(
fourCharCode('Wrng'))
error_kind = NSAppleEventDescriptor.descriptorWithEnumCode_(
fourCharCode('Err '))
# Accumulate the set of errors found on compiler output.
entries = []
for line in compilerOutput.splitlines():
match = pat.match(line)
if match:
file, line, message = match.groups()
kind = warning_kind if warning.match(message) else error_kind
file = os.path.abspath(file)
# BBEdit returns error -1701 for nonexistent files.
if os.path.exists(file):
entry = {
'result_kind': kind,
'result_file': file,
'result_line': int(line),
'message': message
}
entries.append(entry)
return entries
def showResultsBrowser(name, entries):
BBEdit = SBApplication.applicationWithBundleIdentifier_(
"com.barebones.BBEdit")
ResultsBrowser = BBEdit.classForScriptingClass_("results browser")
properties = {'name': name}
browserSpecifier = ResultsBrowser.alloc().initWithElementCode_properties_data_(
fourCharCode('RslW'), properties, entries)
BBEdit.windows().addObject_(browserSpecifier)
filePath = getFrontBBEditDocument()
print filePath
if filePath:
compilerOutput = invokeClosureCompiler(filePath)
entries = processMessages(compilerOutput)
if entries:
title = "Closure Compiler"
showResultsBrowser(title, entries)
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment