From 9d6a8fb33e92bb40beeb5b55ac7880eebb02dda2 Mon Sep 17 00:00:00 2001 From: Liam Cain Date: Thu, 2 Aug 2012 10:16:48 -0400 Subject: [PATCH] Big improvements. Should work faster and feel more fluid. --- .gitignore | 3 +- Default.sublime-keymap | 16 +++- afn_img_utils.py | 63 ---------------- autofilename.py | 137 ++++++++++++++++++++-------------- autofilename.sublime-settings | 8 +- commit-and-trim.sublime-macro | 4 - getimageinfo.py | 61 +++++++++++++++ 7 files changed, 160 insertions(+), 132 deletions(-) delete mode 100644 afn_img_utils.py delete mode 100644 commit-and-trim.sublime-macro create mode 100644 getimageinfo.py diff --git a/.gitignore b/.gitignore index 0cb3eac..a0d5d0d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.pyc *.html *.css -*.png \ No newline at end of file +*.png +.DS_STORE \ No newline at end of file diff --git a/Default.sublime-keymap b/Default.sublime-keymap index f367513..6098f74 100644 --- a/Default.sublime-keymap +++ b/Default.sublime-keymap @@ -1,16 +1,24 @@ [ - { "keys": ["tab"], "command": "run_macro_file", "args": {"file": "Packages/AutoFileName/commit-and-trim.sublime-macro"}, + { "keys": ["tab"], "command": "insert_dimensions", "context": [ { "key": "setting.auto_complete_commit_on_tab" }, - { "operand": true, "operator": "equal", "match_all": true, "key": "afn_commit-n-trim" } + { "key": "auto_complete_visible", "operator": "equal", "operand": true }, + { "key": "afn_insert_dimensions", "operator": "equal", "operand": true } ] }, - { "keys": ["enter"], "command": "run_macro_file", "args": {"file": "Packages/AutoFileName/commit-and-trim.sublime-macro"}, + { "keys": ["enter"], "command": "insert_dimensions", "context": [ { "key": "setting.auto_complete_commit_on_tab", "operator": "equal", "operand": false }, - { "operand": true, "operator": "equal", "match_all": true, "key": "afn_commit-n-trim" } + { "key": "auto_complete_visible", "operator": "equal", "operand": true }, + { "key": "afn_insert_dimensions", "operator": "equal", "operand": true } + ] + }, + { "keys": ["backspace"], "command": "reload_auto_complete", + "context": + [ + { "key": "afn_deleting_slash", "operator": "equal", "operand": true } ] } ] \ No newline at end of file diff --git a/afn_img_utils.py b/afn_img_utils.py deleted file mode 100644 index df994ee..0000000 --- a/afn_img_utils.py +++ /dev/null @@ -1,63 +0,0 @@ -# -# These methods were taken from -# Zencoding utils (I don't own them) -# - -def char_at(text, pos): - """ - Returns character at specified index of text. - If index if out of range, returns empty string - """ - return text[pos] if pos < len(text) else '' - - -def get_image_size(stream): - """ - Gets image size from image byte stream. - @author http://romeda.org/rePublish/ - @param stream: Image byte stream (use zen_file.read()) - @type stream: str - @return: dict with width and height properties - """ - png_magic_num = "\211PNG\r\n\032\n" - jpg_magic_num = "\377\330" - gif_magic_num = "GIF8" - pos = [0] - - def next_byte(): - char = char_at(stream, pos[0]) - pos[0] += 1 - return ord(char) - - if stream.startswith(png_magic_num): - # PNG. Easy peasy. - pos[0] = stream.find('IHDR') + 4 - - return { - 'width': (next_byte() << 24) | (next_byte() << 16) | (next_byte() << 8) | next_byte(), - 'height': (next_byte() << 24) | (next_byte() << 16) | (next_byte() << 8) | next_byte() - } - - elif stream.startswith(gif_magic_num): - pos[0] = 6 - - return { - 'width': next_byte() | (next_byte() << 8), - 'height': next_byte() | (next_byte() << 8) - } - - elif stream.startswith(jpg_magic_num): - hex_list = ["%02X" % ord(ch) for ch in stream] - - for k in range(len(hex_list) - 1): - if hex_list[k] == 'FF' and (hex_list[k + 1] == 'C0' or hex_list[k + 1] == 'C2'): - #print k, hex(k) # test - return { - 'height': int(hex_list[k + 5], 16) * 256 + int(hex_list[k + 6], 16), - 'width': int(hex_list[k + 7], 16) * 256 + int(hex_list[k + 8], 16) - } - else: - return { - 'width': -1, - 'height': -1 - } \ No newline at end of file diff --git a/autofilename.py b/autofilename.py index 81a8268..7a285ae 100644 --- a/autofilename.py +++ b/autofilename.py @@ -1,10 +1,9 @@ import sublime import sublime_plugin import os -import glob -from afn_img_utils import get_image_size +from getimageinfo import getImageInfo -class AfnCommitCompCommand(sublime_plugin.TextCommand): +class InsertDimensionsCommand(sublime_plugin.TextCommand): this_dir = '' def insert_dimension(self,edit,dim,name,tag_scope): @@ -12,81 +11,115 @@ class AfnCommitCompCommand(sublime_plugin.TextCommand): sel = view.sel()[0].a if name in view.substr(tag_scope): reg = view.find('(?<='+name+'\=)\s*\"\d{1,5}', tag_scope.a) - view.replace(edit, reg, '"'+str(dim.get(name))) + view.replace(edit, reg, '"'+str(dim)) else: - dimension = str(dim.get(name)) + dimension = str(dim) view.insert(edit, sel+1, ' '+name+'="'+dimension+'"') def run(self, edit): view = self.view + view.run_command("commit_completion") sel = view.sel()[0].a - if not 'string' in view.scope_name(sel): return + if not 'html' in view.scope_name(sel): return scope = view.extract_scope(sel-1) tag_scope = view.extract_scope(scope.a-1) - region = sublime.Region(sel, scope.b-1) - view.erase(edit, region) - path = view.substr(view.extract_scope(sel-1)) + path = view.substr(scope) if path.startswith(("'","\"","(")): path = path[1:-1] - path = path[path.rfind('/'):] + path = path[path.rfind('/'):] if '/' in path else '' full_path = self.this_dir + path + + print full_path + if ' view.size(): + if self.at_path_end(view): + if view.substr(sel.a-1) == '/': + view.run_command("hide_auto_complete") + sublime.set_timeout(self.complete, 50) + + self.size = view.size() + + def complete(self): + self.view.run_command('auto_complete', + {'disable_auto_insert': True, + 'next_completion_if_showing': False}) def fix_dir(self,sdir,fn): if fn.endswith(('.png','.jpg','.jpeg','.gif')): - path = os.path.join(sdir + '/', fn) + path = os.path.join(sdir, fn) with open(path,'rb') as r: - read_data = r.read() - dim = get_image_size(read_data) - return fn + '\t' + 'w:'+str(dim.get('width'))+" h:"+str(dim.get('height')) + read_data = r.read() if path.endswith(('.jpg','.jpeg')) else r.read(24) + con_type, w, h = getImageInfo(read_data) + return fn+'\t'+'w:'+ str(w) +" h:" + str(h) return fn def get_cur_path(self,view,sel): scope_contents = view.substr(view.extract_scope(sel-1)) cur_path = scope_contents.replace('\r\n', '\n').split('\n')[0] if cur_path.startswith(("'","\"","(")): - return cur_path[1:-1] - return cur_path + cur_path = cur_path[1:-1] + + return cur_path[:cur_path.rfind('/')] if '/' in cur_path else '' def on_query_completions(self, view, prefix, locations): - SETTINGS = "autofilename.sublime-settings" - is_proj_rel = sublime.load_settings(SETTINGS).get("auto_file_name_use_project_root") - valid_scopes = ["string", "css", "sass", "scss", "less"] + settings = sublime.load_settings("autofilename.sublime-settings") + is_proj_rel = settings.get("afn_use_project_root") + valid_scopes = settings.get("afn_valid_scopes") sel = view.sel()[0].a completions = [] backup = [] @@ -99,20 +132,14 @@ class FileNameComplete(sublime_plugin.EventListener): cur_path = self.get_cur_path(view, sel) - if view.extract_scope(sel-1).b - sel > 1: # if the cursor is not at the end - wild_pos = sel - view.extract_scope(sel-1).a - 1 - cur_path = cur_path[:wild_pos] + '*' + cur_path[wild_pos:] + '*' - - if is_proj_rel and os.path.isabs(cur_path): - this_dir = sublime.load_settings(SETTINGS).get("afn_proj_root") - cur_path = cur_path[1:] + if is_proj_rel: + this_dir = settings.get("afn_proj_root") if len(this_dir) < 2: for f in sublime.active_window().folders(): if f in view.file_name(): this_dir = f else: if not view.file_name(): - print 'AutoFileName: File not saved.' backup.insert(0,('AutoFileName: File Not Saved','')) return backup this_dir = os.path.split(view.file_name())[0] @@ -120,21 +147,15 @@ class FileNameComplete(sublime_plugin.EventListener): this_dir = os.path.join(this_dir, cur_path) try: - dir_files = [] - for f in glob.glob(this_dir): - if os.path.isfile(f): - dir_files.append(os.path.basename(f)) - else: - dir_files.extend(os.listdir(f)) + dir_files = os.listdir(this_dir) - for d in list(set(dir_files)): + for d in dir_files: n = d.decode('utf-8') if n.startswith('.'): continue if not '.' in n: n += '/' completions.append((self.fix_dir(this_dir,n), n)) - if completions: - self.committing_filename = True - AfnCommitCompCommand.this_dir = this_dir + if completions: + InsertDimensionsCommand.this_dir = this_dir return completions except OSError: print "AutoFileName: could not find " + this_dir diff --git a/autofilename.sublime-settings b/autofilename.sublime-settings index 186d190..660e37b 100644 --- a/autofilename.sublime-settings +++ b/autofilename.sublime-settings @@ -3,9 +3,13 @@ //Changing this setting allows for absolute paths on a project level //This is useful for web designers and developers who want to use the //root of their site. - "auto_file_name_use_project_root": false, + "afn_use_project_root": true, // Override the project root. Will only work // if "auto_file_name_use_project_root" is true. - "afn_proj_root": "" + "afn_proj_root": "", + + "afn_valid_scopes":["string","css","sass","less","scss"], + + "afn_insert_dimensions": true } \ No newline at end of file diff --git a/commit-and-trim.sublime-macro b/commit-and-trim.sublime-macro deleted file mode 100644 index 3e1866b..0000000 --- a/commit-and-trim.sublime-macro +++ /dev/null @@ -1,4 +0,0 @@ -[ - {"command": "commit_completion"}, - {"command": "afn_commit_comp"} -] \ No newline at end of file diff --git a/getimageinfo.py b/getimageinfo.py new file mode 100644 index 0000000..c9741f8 --- /dev/null +++ b/getimageinfo.py @@ -0,0 +1,61 @@ +import StringIO +import struct + +def getImageInfo(data): + data = str(data) + size = len(data) + height = -1 + width = -1 + content_type = '' + + # handle GIFs + if (size >= 10) and data[:6] in ('GIF87a', 'GIF89a'): + # Check to see if content_type is correct + content_type = 'image/gif' + w, h = struct.unpack("= 24) and data.startswith('\211PNG\r\n\032\n') + and (data[12:16] == 'IHDR')): + content_type = 'image/png' + w, h = struct.unpack(">LL", data[16:24]) + width = int(w) + height = int(h) + + # Maybe this is for an older PNG version. + elif (size >= 16) and data.startswith('\211PNG\r\n\032\n'): + # Check to see if we have the right content type + content_type = 'image/png' + w, h = struct.unpack(">LL", data[8:16]) + width = int(w) + height = int(h) + + # handle JPEGs + elif (size >= 2) and data.startswith('\377\330'): + content_type = 'image/jpeg' + jpeg = StringIO.StringIO(data) + jpeg.read(2) + b = jpeg.read(1) + try: + while (b and ord(b) != 0xDA): + while (ord(b) != 0xFF): b = jpeg.read(1) + while (ord(b) == 0xFF): b = jpeg.read(1) + if (ord(b) >= 0xC0 and ord(b) <= 0xC3): + jpeg.read(3) + h, w = struct.unpack(">HH", jpeg.read(4)) + break + else: + jpeg.read(int(struct.unpack(">H", jpeg.read(2))[0])-2) + b = jpeg.read(1) + width = int(w) + height = int(h) + except struct.error: + pass + except ValueError: + pass + + return content_type, width, height \ No newline at end of file