Big improvements. Should work faster and feel more fluid.

This commit is contained in:
Liam Cain
2012-08-02 10:16:48 -04:00
parent 2c35da21e9
commit 9d6a8fb33e
7 changed files with 160 additions and 132 deletions

3
.gitignore vendored
View File

@@ -1,4 +1,5 @@
*.pyc
*.html
*.css
*.png
*.png
.DS_STORE

View File

@@ -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 }
]
}
]

View File

@@ -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 <code>zen_file.read()</code>)
@type stream: str
@return: dict with <code>width</code> and <code>height</code> 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
}

View File

@@ -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 '<img' in view.substr(tag_scope) and path.endswith(('.png','.jpg','.jpeg','.gif')):
with open(full_path,'rb') as r:
read_data = r.read()
dim = get_image_size(read_data)
self.insert_dimension(edit,dim,'width',tag_scope)
self.insert_dimension(edit,dim,'height',tag_scope)
read_data = r.read() if path.endswith(('.jpg','.jpeg')) else r.read(24)
con_type, w, h = getImageInfo(read_data)
self.insert_dimension(edit,w,'width',tag_scope)
self.insert_dimension(edit,h,'height',tag_scope)
class ReloadAutoCompleteCommand(sublime_plugin.TextCommand):
def complete(self):
self.view.run_command('auto_complete',
{'disable_auto_insert': True,
'next_completion_if_showing': False})
def run(self,edit):
self.view.run_command('hide_auto_complete')
self.view.run_command('left_delete')
sublime.set_timeout(self.complete, 50)
class FileNameComplete(sublime_plugin.EventListener):
committing_filename = False
def on_query_context(self, view, key, operator, operand, match_all):
if key == "afn_commit-n-trim":
return self.will_commit(view) == operand
def on_activated(self,view):
self.size = view.size()
self.view = view
def scope(self,view,string):
sel = view.sel()[0].a
return string in view.scope_name(sel)
def on_query_context(self, view, key, operator, operand, match_all):
if key == "afn_insert_dimensions":
settings = sublime.load_settings("autofilename.sublime-settings")
return settings.get('afn_insert_dimensions') == operand
if key == "afn_deleting_slash":
sel = view.sel()[0]
valid = sel.empty() and view.substr(sel.a-1) == '/'
return valid == operand
def scope(self,string):
sel = self.view.sel()[0].a
return string in self.view.scope_name(sel)
def at_path_end(self,view):
sel = view.sel()[0]
return (sel.empty() and self.scope('string.end')) or (self.scope('.css') and view.substr(sel.a) == ')')
def on_selection_modified(self,view):
sel = view.sel()[0].a
v = view
if self.scope(v,'string.end') or (self.scope(v,'.css') and ')' in view.substr(sel)):
if view.substr(sel-1) == '/' or len(view.extract_scope(sel)) < 3:
view.run_command('auto_complete',
sel = view.sel()[0]
if self.at_path_end(view):
if view.substr(sel.a-1) == '/' or len(view.extract_scope(sel.a)) < 3:
view.run_command('auto_complete',
{'disable_auto_insert': True,
'next_completion_if_showing': False})
def will_commit(self, view):
if self.committing_filename:
self.committing_filename = False
return True
return False
def on_modified(self,view):
sel = view.sel()[0]
v = view
if self.size > 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

View File

@@ -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
}

View File

@@ -1,4 +0,0 @@
[
{"command": "commit_completion"},
{"command": "afn_commit_comp"}
]

61
getimageinfo.py Normal file
View File

@@ -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("<HH", data[6:10])
width = int(w)
height = int(h)
# See PNG 2. Edition spec (http://www.w3.org/TR/PNG/)
# Bytes 0-7 are below, 4-byte chunk length, then 'IHDR'
# and finally the 4-byte width, height
elif ((size >= 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