From b1752521e1f2057b7546f9a1789f13580f3bbc58 Mon Sep 17 00:00:00 2001 From: Jack Lukic Date: Fri, 17 Feb 2017 03:15:38 -0500 Subject: [PATCH] Initial commit --- Meteor Reload.sublime-keymap | 6 + Meteor Reload.sublime-settings | 27 +++ MeteorReload.py | 302 +++++++++++++++++++++++++++++++++ 3 files changed, 335 insertions(+) create mode 100755 Meteor Reload.sublime-keymap create mode 100755 Meteor Reload.sublime-settings create mode 100755 MeteorReload.py diff --git a/Meteor Reload.sublime-keymap b/Meteor Reload.sublime-keymap new file mode 100755 index 0000000..9434d7f --- /dev/null +++ b/Meteor Reload.sublime-keymap @@ -0,0 +1,6 @@ +[ + { + "caption": "Meteor Reload: Sync File", + "command": "meteor_reload" + } +] \ No newline at end of file diff --git a/Meteor Reload.sublime-settings b/Meteor Reload.sublime-settings new file mode 100755 index 0000000..e37f672 --- /dev/null +++ b/Meteor Reload.sublime-settings @@ -0,0 +1,27 @@ +{ + // Whether data should be transmitted when file is modified + "reload_on_modified": true, + + // The amount of time in MS to debounce when using reload_on_modified + "reload_debounce": 300, + + // The hostname for the machine that you are using + "hostname": "localhost", + + // The port your meteor server is running + "port": 3000, + + // The name of the server endpoint + "endpoint" : "/reval", + + // Path to the root of the meteor server (windows be sure to escape backslashes, i.e. "C:\\My\\Project") + "path": "", + + // Only files matching this local path will sync (windows paths require escaped backslashes) + // This can include partial paths, for example '/app', will match '/my/special/app' + "required_path": "", + + // Only files matching this regex will be required (used to specify file types or other conditions) + "required_regex": "\\.(html|less|css|js)$", + +} diff --git a/MeteorReload.py b/MeteorReload.py new file mode 100755 index 0000000..0199149 --- /dev/null +++ b/MeteorReload.py @@ -0,0 +1,302 @@ +import sublime +import functools +import sublime_plugin +import urllib.request +import re + + +settings = sublime.load_settings("Meteor Reload.sublime-settings") + +class meteorReload(sublime_plugin.EventListener): + pending = 0 + + def handleTimeout(self, view): + self.pending = self.pending - 1 + if self.pending == 0: + view.run_command("meteor_reload") + + def on_modified_async(self, view): + if settings.get('reload_on_modified') is True: + required_path = settings.get('required_path') + required_regex = settings.get('required_regex') + file_path = view.file_name() + if (file_path and file_path.find(required_path) >= 0 and re.search(required_regex, file_path)): + self.pending = self.pending + 1 + sublime.set_timeout(functools.partial(self.handleTimeout, view), settings.get('reload_debounce')) + +class meteorReloadCommand(sublime_plugin.TextCommand): + def run(self, view): + if (self.view.file_name()): + path = settings.get('path') + file_path = self.view.file_name().replace(path, '') + hostname = settings.get('hostname') + port = settings.get('port') + endpoint = settings.get('endpoint') + url = 'http://' + hostname + ':' + str(port) + endpoint + '?filePath=' + file_path + print (url) + data = self.view.substr(sublime.Region(0, self.view.size())) + request = HttpAsyncRequest(url) + request.set_content(str.encode(data)) + request.set_header('Content-type', 'text/plain') + request.send() + +# Library for doing async HTTP (threading yeah!) + +_user_agent = "qualia" + +import threading as _threading +_is_old = 3 / 2 == 1 # Yeah, I'm sure there's a better way. Deal with it. +if _is_old: + import urllib as _urllib + import urllib2 as _urllib2 + import urlparse as _urlparse +else: + import urllib as _urllib + import urllib.parse as _urllib_parse + import urllib.request as _urllib_request + +def _parse_url(url): + return _urlparse.urlparse(url) + +def set_user_agent(value): + global _user_agent + _user_agent = value + +def decode_url_value(value): + if _is_old: + return _urllib.unquote(value).decode('utf8') + else: + return _urllib_parse.unquote(value) + +def encode_url_value(value): + if _is_old: + return _urllib2.quote(value.encode('utf8')) + else: + return _urllib_parse.quote(value) + +def _send_impl(req_obj, method, url, headers, content): + if _is_old: + opener = _urllib2.build_opener(_urllib2.HTTPHandler) + if content == None: + request = _urllib2.Request(url) + else: + request = _urllib2.Request(url, data=content) + else: + opener = _urllib_request.build_opener(_urllib_request.HTTPHandler) + if content == None: + request = _urllib_request.Request(url) + else: + request = _urllib_request.Request(url, data=content) + for header in headers: + request.add_header(header[0], header[1]) + request.get_method = lambda:method + output = opener.open(request) + content = output.read() + headers = {} + for header_key in output.headers.keys(): + headers[header_key] = output.headers[header_key] + response_message = output.msg + response_code = output.code + req_obj._set_result(response_code, response_message, content, headers) + +class HttpAsyncRequest: + def __init__(self, url): + bad_format = False + try: + if _is_old: + url_parts = _parse_url(url) + else: + url_parts = _urllib_parse.urlparse(url) + if url_parts.scheme == '' or url_parts.netloc == '': + bad_format = True + except: + bad_format = True + if bad_format: + raise Exception("Bad URL! Bad!") + + self.mutex = _threading.Lock() + self.method = 'GET' + self.scheme = url_parts.scheme + self.host = url_parts.hostname + self.port = url_parts.port + self.path = url_parts.path + self.fragment = url_parts.fragment + self.params = url_parts.params + self.original_query = url_parts.query # use this if query params are not modified + self.query = None # if modified, clear original_query and populate this with a dictionary lookup + self.header_formatting = {} # preserves the formatting of the header key + self.header_values = {} # canonical key of header with list of values of that header + self.content = None + self.set_header('User-Agent', _user_agent) + self.done = False + self.response_code = -1 + self.response_message = None + self.response_content = None + self.response_headers_values = None + self.response_headers_formatting = None + + def send(self): + url = self.scheme + '://' + self.host + + if self.port != None: + url += ':' + str(self.port) + + if self.path != None and self.path != '': + if self.path[0] != '/': + self.path = '/' + self.path + url += self.path + + if self.params != None and self.params != '': + url += ';' + self.params + + if self.query == None: + if self.original_query != '': + url += '?' + self.original_query + else: + queries = [] + keys = self.query.keys()[:] + keys.sort() # deterministic requests + for key in keys: + e_key = encode_url_value(key) + for value in self.query[key]: + e_value = encode_url_value(value) + queries.append(e_key + '=' + e_value) + url += '?' + '&'.join(queries) + + if self.fragment != '': + url += '#' + self.fragment + + headers = [] + keys = list(self.header_formatting.keys()) + keys.sort() + for key in keys: + f_key = self.header_formatting[key] + for value in self.header_values[key]: + headers.append((f_key, value)) + + + thread = _threading.Thread(target = _send_impl, args = (self, self.method, url, headers, self.content)) + thread.daemon = True + thread.start() + + def _set_result(self, code, message, content, headers): + self.mutex.acquire() + try: + self.response_code = code + self.response_message = message + self.response_content = content + self.response_headers_values = {} + self.response_headers_formatting = {} + for key in headers.keys(): + ckey = key.lower() + self.response_headers_values[ckey] = headers[key] + self.response_headers_formatting[ckey] = key + finally: + self.mutex.release() + + def is_complete(self): + self.mutex.acquire() + try: + return self.response_code != -1 + finally: + self.mutex.release() + + def _ensure_request_complete(self): + if not self.is_complete(): + raise Exception("Cannot access response until request is complete.") + + def get_response_code(self): + self._ensure_request_complete() + return self.response_code + + def get_response_message(self): + self._ensure_request_complete() + return self.response_message + + def get_response_header_names(self): + self._ensure_request_complete() + output = list(self.response_headers_formatting.values()) + output.sort() + return output + + def get_response_header(self, name): + self._ensure_request_complete() + return self.response_headers_values.get(name.lower(), None) + + def get_response_content(self, mode='t'): + self._ensure_request_complete() + output = self.response_content + if mode == 't': + return output.decode('utf-8') + else: + return output + + + def set_header(self, key, value): + self.header_formatting[key.lower()] = key + self.header_values[key.lower()] = [value] + + def add_header(self, key, value): + canonical_key = key.lower() + existing_headers = self.header_values.get(canonical_key, None) + if existing_headers == None: + self.set_header(key, value) + else: + existing_headers.append(value) + + def clear_header(self, key): + canonical_key = key.lower() + if self.header_values.get(canonical_key, None) != None: + self.header_values.pop(canonical_key) + self.header_formatting.pop(canonical_key) + + def set_method(self, method): + self.method = method + + def set_content(self, content): + self.content = content + + def _init_query(self): + if self.query == None: + query = [] if self.original_query != '' else self.original_query.split('&') + lookup_values = {} + for item in query: + parts = item.split('=') + if len(parts) >= 2: + item_key = decode_url_value(parts[0]) + item_value = decode_url_value('='.join(parts[1:])) + existing_values = lookup_values.get(item_key, None) + if existing_values == None: + existing_values = [] + lookup_values[item_key] = existing_values + existing_values.append(item_value) + self.query = lookup_values + + def set_query(self, key, value): + self._init_query() + self.query[key] = [value] + + def add_query(self, key, value): + self._init_query() + values = self.query.get(key, None) + if values != None: + values.append(value) + else: + self.query[key] = [value] + + def clear_query(self, key): + self._init_query() + if self.query.get(key, None) != None: + self.query.pop(key) + + def set_port(self, port): + self.port = port + + def set_fragment(self, fragment): + self.fragment = fragment + + def clear_fragment(self): + self.fragment = None + + def set_scehem(self, scheme): + self.scheme = scheme \ No newline at end of file