Files
2013-04-04 08:54:25 -04:00

463 lines
14 KiB
Python

#aponxi v0.6.0
import sublime
import sys
from os import path
import os
from subprocess import Popen, PIPE
from sublime_plugin import TextCommand
from sublime_plugin import WindowCommand
import sublime_plugin
import time
import functools
settings = sublime.load_settings('CoffeeScript.sublime-settings')
def run(cmd, args=[], source="", cwd=None, env=None):
if not type(args) is list:
args = [args]
if sys.platform == "win32":
proc = Popen([cmd] + args, env=env, cwd=cwd, stdout=PIPE, stdin=PIPE, stderr=PIPE, shell=True)
stat = proc.communicate(input=source.encode('utf-8'))
else:
if env is None:
env = {"PATH": settings.get('binDir', '/usr/local/bin')}
if source == "":
command = [cmd] + args
else:
command = [cmd] + args + [source]
# print "Debug - coffee command: "
# print command
proc = Popen(command, env=env, cwd=cwd, stdout=PIPE, stderr=PIPE)
stat = proc.communicate()
okay = proc.returncode == 0
return {"okay": okay, "out": stat[0].decode('utf-8'), "err": stat[1].decode('utf-8')}
def brew(args, source):
if sys.platform == "win32":
args.append("-s")
else:
args.append("-e")
return run("coffee", args=args, source=source)
def cake(task, cwd):
return run("cake", args=task, cwd=cwd)
def isCoffee(view=None):
if view is None:
view = sublime.active_window().active_view()
return 'source.coffee' in view.scope_name(0)
class Text():
@staticmethod
def all(view):
return view.substr(sublime.Region(0, view.size()))
@staticmethod
def sel(view):
text = []
for region in view.sel():
if region.empty():
continue
text.append(view.substr(region))
return "".join(text)
@staticmethod
def get(view):
text = Text.sel(view)
if len(text) > 0:
return text
return Text.all(view)
class CompileCommand(TextCommand):
def is_enabled(self):
return isCoffee(self.view)
def run(self, *args, **kwargs):
no_wrapper = settings.get('noWrapper', True)
compile_dir = settings.get('compileDir')
args = ['-c', self.view.file_name()]
# print self.view.file_name()
if no_wrapper:
args = ['-b'] + args
# print compile_dir
# print isinstance(compile_dir, unicode)
if compile_dir and isinstance(compile_dir, str) or isinstance(compile_dir, unicode):
print "Compile dir specified: " + compile_dir
if not os.path.exists(compile_dir):
os.makedirs(compile_dir)
print "Compile dir did not exist, created folder: " + compile_dir
folder, file_nm = os.path.split(self.view.file_name())
print folder
args = ['--output', compile_dir] + args
# print args
# print args
result = run("coffee", args=args)
if result['okay'] is True:
status = 'Compilation Succeeded'
else:
status = 'Compilation Failed'
sublime.status_message(status)
class CompileAndDisplayCommand(TextCommand):
def is_enabled(self):
return isCoffee(self.view)
def run(self, edit, **kwargs):
output = self.view.window().new_file()
output.set_scratch(True)
opt = kwargs["opt"]
if opt == '-p':
output.set_syntax_file('Packages/JavaScript/JavaScript.tmLanguage')
no_wrapper = settings.get('noWrapper', True)
args = [opt]
print args
if no_wrapper:
args = ['-b'] + args
res = brew(args, Text.get(self.view))
if res["okay"] is True:
output.insert(edit, 0, res["out"])
else:
output.insert(edit, 0, res["err"].split("\n")[0])
class CheckSyntaxCommand(TextCommand):
def is_enabled(self):
return isCoffee(self.view)
def run(self, edit):
res = brew(['-b', '-p'], Text.get(self.view))
if res["okay"] is True:
status = 'Valid'
else:
status = res["err"].split("\n")[0]
sublime.status_message('Syntax %s' % status)
class RunScriptCommand(WindowCommand):
def finish(self, text):
if text == '':
return
text = "{puts, print} = require 'util'\n" + text
res = brew(['-b'], text)
if res["okay"] is True:
output = self.window.new_file()
output.set_scratch(True)
edit = output.begin_edit()
output.insert(edit, 0, res["out"])
output.end_edit(edit)
else:
sublime.status_message('Syntax %s' % res["err"].split("\n")[0])
def run(self):
sel = Text.sel(sublime.active_window().active_view())
if len(sel) > 0:
if not isCoffee():
return
self.finish(sel)
else:
self.window.show_input_panel('Coffee >', '', self.finish, None, None)
class RunCakeTaskCommand(WindowCommand):
def finish(self, task):
if task == '':
return
if not self.window.folders():
cakepath = path.dirname(self.window.active_view().file_name())
else:
cakepath = path.join(self.window.folders()[0], 'Cakefile')
if not path.exists(cakepath):
cakepath = path.dirname(self.window.active_view().file_name())
if not path.exists(cakepath):
return sublime.status_message("Cakefile not found.")
res = cake(task, cakepath)
if res["okay"] is True:
if "No such task" in res["out"]:
msg = "doesn't exist"
else:
msg = "suceeded"
else:
msg = "failed"
sublime.status_message("Task %s - %s." % (task, msg))
def run(self):
self.window.show_input_panel('Cake >', '', self.finish, None, None)
# _
# __ _ _ __ ___ _ __ __ _(_)
# / _` | '_ \ / _ \| '_ \\ \/ / |
# | (_| | |_) | (_) | | | |> <| |
# \__,_| .__/ \___/|_| |_/_/\_\_|
# |_|
def watched_filename(view_id):
view = ToggleWatch.views[view_id]['input_obj']
if view.file_name() is not None:
filename = view.file_name().split('/')[-1]
else:
filename = "Unsaved File"
return filename
class ToggleWatch(TextCommand):
views = {}
outputs = {}
def is_enabled(self):
return isCoffee(self.view)
def run(self, edit):
myvid = self.view.id()
if not myvid in ToggleWatch.views:
views = ToggleWatch.views
views[myvid] = {'watched': True, 'modified': True, 'input_closed': False}
views[myvid]["input_obj"] = self.view
print "Now watching", watched_filename(myvid)
createOut(myvid)
else:
views = ToggleWatch.views
views[myvid]['watched'] = not views[myvid]['watched']
if not views[myvid]['watched']:
print "Stopped watching", watched_filename(myvid)
if views[myvid]['output_open'] is False:
print "Openning output and watching", watched_filename(myvid)
createOut(myvid)
elif views[myvid]['watched'] is True:
print "Resuming watching", watched_filename(myvid)
refreshOut(myvid)
def cleanUp(input_view_id):
del ToggleWatch.outputs[ToggleWatch.views[input_view_id]['output_id']]
del ToggleWatch.views[input_view_id]
return
def get_output_filename(input_view_id):
input_filename = watched_filename(input_view_id)
fileName, fileExtension = os.path.splitext(input_filename)
output_filename = fileName + '.js'
return output_filename
def createOut(input_view_id):
#create output panel and save
this_view = ToggleWatch.views[input_view_id]
outputs = ToggleWatch.outputs
#print this_view
input_filename = watched_filename(input_view_id)
print input_filename
output = this_view["input_obj"].window().new_file()
output.set_scratch(True)
output.set_syntax_file('Packages/JavaScript/JavaScript.tmLanguage')
this_view['output_id'] = output.id()
this_view["output_obj"] = output
this_view["output_open"] = True
# setting output filename
# print output.settings().set('filename', '[Compiled]' + input_filename)
# Getting file extension
output_filename = get_output_filename(input_view_id)
output.set_name(output_filename)
if not output.id() in outputs:
outputs[output.id()] = {'boundto': input_view_id}
refreshOut(input_view_id)
return output
def refreshOut(view_id):
this_view = ToggleWatch.views[view_id]
this_view['last_modified'] = time.mktime(time.gmtime())
#refresh the output view
no_wrapper = settings.get('noWrapper', True)
args = ['-p']
if no_wrapper:
args = ['-b'] + args
res = brew(args, Text.get(this_view['input_obj']))
output = this_view['output_obj']
this_view['modified'] = False
if res["okay"] is True:
edit = output.begin_edit()
output.erase(edit, sublime.Region(0, output.size()))
output.insert(edit, 0, res["out"])
output.end_edit(edit)
print "Refreshed"
else:
edit = output.begin_edit()
output.erase(edit, sublime.Region(0, output.size()))
output.insert(edit, 0, res["err"].split("\n")[0])
output.end_edit(edit)
return
def isView(view_id):
# are they modifying a view (rather than a panel, etc.)
if not view_id:
return False
window = sublime.active_window()
view = window.active_view() if window != None else None
return (view is not None and view.id() == view_id)
def close_output(input_id):
views = ToggleWatch.views
v = views[input_id]
output = v['output_obj']
# output_id = v['output_id']
# print "close output"
if v['output_open'] is True:
#print "the output is open so we should attempt to close it"
output.window().focus_view(output)
output.window().run_command("close")
print watched_filename(input_id), "was closed. Closing the Output"
#v['output_open'] = False
cleanUp(input_id)
return
class CaptureEditing(sublime_plugin.EventListener):
def handleTimeout(self, vid):
this_view = ToggleWatch.views[vid]
modified = this_view['modified']
if modified is True:
# been 1000ms since the last modification
#print "handling"
refreshOut(vid)
def on_modified(self, view):
vid = view.id()
watch_modified = settings.get('watchOnModified')
if watch_modified is not False and vid in ToggleWatch.views:
if watch_modified is True:
delay = 0.5
elif watch_modified < 0.5:
delay = 0.5
else:
delay = watch_modified
#then we have a watched input.
this_view = ToggleWatch.views[vid]
#print " this view is ", this_view
if this_view['modified'] is False:
this_view['modified'] = True
#print " trigger "
if this_view['watched'] is True:
sublime.set_timeout(functools.partial(self.handleTimeout, vid), int(delay * 1000))
return
def on_post_save(self, view):
# print "isCoffee " + str(isCoffee())
watch_save = settings.get('watchOnSave', True)
if watch_save:
save_id = view.id()
views = ToggleWatch.views
if save_id in views:
# getting view object
save_view = ToggleWatch.views[save_id]
# check if modified
if save_view['modified'] is True:
refreshOut(save_id)
compile_on_save = settings.get('compileOnSave', True)
if compile_on_save is True and isCoffee() is True:
print "Compiling on save..."
view.run_command("compile")
show_compile_output_on_save = settings.get('showOutputOnSave', True)
if show_compile_output_on_save is True and isCoffee() is True and CompileOutput.IS_OPEN is True:
print "Updating output panel..."
view.run_command("compile_output")
return
def on_close(self, view):
close_id = view.id()
views = ToggleWatch.views
if close_id in views:
#this is an input
#print "input was closed"
views[close_id]['input_closed'] = True
close_output(close_id)
if close_id in ToggleWatch.outputs and views[ToggleWatch.outputs[close_id]['boundto']]['input_closed'] is not True:
#this is an output
#print "an output was closed!"
boundview = ToggleWatch.outputs[close_id]['boundto']
thatview = views[boundview]
thatview['output_open'] = False
thatview['watched'] = False
filename = watched_filename(boundview)
print "The output was closed. No longer watching", filename
return
class CompileOutput(TextCommand):
PANEL_NAME = 'coffee_compile_output'
IS_OPEN = False
def is_enabled(self):
return isCoffee(self.view)
def run(self, edit):
window = self.view.window()
#refresh the output view
no_wrapper = settings.get('noWrapper', True)
# args = ['-p']
if no_wrapper:
args = ['-b']
res = brew(args, Text.get(self.view))
panel = window.get_output_panel(self.PANEL_NAME)
panel.set_syntax_file('Packages/JavaScript/JavaScript.tmLanguage')
panel.set_read_only(False)
output = panel
# print res["err"]
if res["okay"] is True:
edit = output.begin_edit()
output.erase(edit, sublime.Region(0, output.size()))
output.insert(edit, 0, res["out"])
output.end_edit(edit)
# print "Refreshed"
else:
edit = output.begin_edit()
output.erase(edit, sublime.Region(0, output.size()))
output.insert(edit, 0, res["err"])
output.end_edit(edit)
output.sel().clear()
output.set_read_only(True)
window.run_command('show_panel', {'panel': 'output.%s' % self.PANEL_NAME})
self.IS_OPEN = True
return