#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