Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support for Jade and Sass #68

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
12 changes: 12 additions & 0 deletions Default.sublime-commands
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{
"caption": "AutoFileName: Default Settings",
"command": "open_file",
"args": {"file": "${packages}/AutoFileName/autofilename.sublime-settings"}
},
{
"caption": "AutoFileName: Quick Settings",
"command": "afn_settings_panel"
}

]
2 changes: 1 addition & 1 deletion Default.sublime-keymap
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[
{ "keys": ["tab"], "command": "insert_dimensions",
{ "keys": ["tab"], "command": "insert_dimensions",
"context":
[
{ "key": "setting.auto_complete_commit_on_tab" },
Expand Down
57 changes: 34 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,39 @@
AutoFileName: Autocomplete Filenames in Sublime Text
=====================================================
AutoFileName
============

Autocomplete Filenames in Sublime Text
--------------------------------------
Do you ever find yourself sifting through folders in the sidebar trying to remember what you named that file? Can't remember if it was a jpg or a png? Maybe you just wish you could type filenames faster. *No more.*

Whether your making a `img` tag in html, setting a background image in css, or linking a `.js` file to your html (or whatever else people use filename paths for these days...), you can now autocomplete the filename. Plus, it uses the built-in autocomplete, so no need to learn another *pesky* shortcut.
Whether you're making a `img` tag in html, setting a background image in css, or linking a `.js` file to your html (or whatever else people use filename paths for these days...), you can now autocomplete the filename. Plus, it uses the built-in autocomplete, so no need to learn another pesky shortcut.

Features
--------

- Display filenames and folders
- Show dimensions next to image files
- Autoinsert dimensions in img tags (can be disabled in settings)
- Support for both '/' and '\' for all you Windows hooligans

Usage
=====
-----
**Nothing!**

For example:

If you are looking to autocomplete an image path in an HTML `<img>` tag:
<img src="../|" />

Pressing control+space, will activate AutoFileName. I list of available files where be ready to select.

*Looking for an even more automatic and seemless completion?* Add the following to your User Settings file:

"auto_complete_triggers":
[
{
"characters": "<",
"selector": "text.html"
},
{
"characters": "/",
"selector": "string.quoted.double.html,string.quoted.single.html, source.css"
}
]

With this, there's no need to worry about pressing control+space, autocompletion with appear upon pressing /.
`<img src="../|" />`

AutoFileName will display a list of files without you having to do anything. As you type, the results will narrow. The list will automatically update as you enter a new directory so you don't have to do a thing.

Settings
--------
There are some options now.

Perhaps you're working on a website and all the image files are relative to the project root instead of the Computer's root directory. No worries. Just tell AutoFileName the project root. (More info in the settings file.)

Additionally, if you hate awesomeness, you can turn off some of the automagicalness and use a boring keybinding to activate AutoFileName.

How Can I help?
---------------
- **Got a feature request? Something bugging you and you're about to uninstall it?** Submit a bug report with all your fears, desires, and vulgarity. I'll heartily try to fix the plugin to your specifications... well, I'll consider it.
182 changes: 144 additions & 38 deletions autofilename.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,57 @@
import sublime
import sublime_plugin
import os
from getimageinfo import getImageInfo
from .getimageinfo import getImageInfo

class AfnShowFilenames(sublime_plugin.TextCommand):
def run(self, edit):
FileNameComplete.is_active = True
self.view.run_command('auto_complete',
{'disable_auto_insert': True,
'next_completion_if_showing': False})

class AfnSettingsPanel(sublime_plugin.WindowCommand):
def run(self):
use_pr = '✗ Stop using project root' if self.get_setting('afn_use_project_root') else '✓ Use Project Root'
use_dim = '✗ Disable HTML Image Dimension insertion' if self.get_setting('afn_insert_dimensions') else '✓ Auto-insert Image Dimensions in HTML'
p_root = self.get_setting('afn_proj_root')

menu = [
[use_pr, p_root],
[use_dim, '<img src="_path_" width = "x" height = "y" >']
]
self.window.show_quick_panel(menu, self.on_done)

def on_done(self, value):
settings = sublime.load_settings('autofilename.sublime-settings')
if value == 0:
use_pr = settings.get('afn_use_project_root')
settings.set('afn_use_project_root', not use_pr)
if value == 1:
use_dim = settings.get('afn_use_project_root')
settings.set('afn_use_project_root', not use_dim)

def get_setting(self,string,view=None):
if view and view.settings().get(string):
return view.settings().get(string)
else:
return sublime.load_settings('autofilename.sublime-settings').get(string)

# Used to remove the / or \ when autocompleting a Windows drive (eg. /C:/path)
class AfnDeletePrefixedSlash(sublime_plugin.TextCommand):
def run(self, edit):
sel = self.view.sel()[0].a
reg = sublime.Region(sel-4,sel-3)
self.view.erase(edit, reg)

# inserts width and height dimensions into img tags. HTML only
class InsertDimensionsCommand(sublime_plugin.TextCommand):
this_dir = ''

def insert_dimension(self,edit,dim,name,tag_scope):
view = self.view
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))
Expand All @@ -22,32 +65,54 @@ def get_setting(self,string,view=None):
else:
return sublime.load_settings('autofilename.sublime-settings').get(string)


def insert_dimensions(self, edit, scope, w, h):
view = self.view

if self.get_setting('afn_insert_width_first',view):
self.insert_dimension(edit,h,'height', scope)
self.insert_dimension(edit,w,'width', scope)
else:
self.insert_dimension(edit,w,'width', scope)
self.insert_dimension(edit,h,'height', scope)


# determines if there is a template tag in a given region. supports HTML and template languages.
def img_tag_in_region(self, region):
view = self.view

# handle template languages but template languages like slim may also contain HTML so
# we do a check for that as well
return view.substr(region).strip().startswith('img') | ('<img' in view.substr(region))


def run(self, edit):
view = self.view
view.run_command("commit_completion")
sel = view.sel()[0].a

if not 'html' in view.scope_name(sel): return
scope = view.extract_scope(sel-1)
tag_scope = view.extract_scope(scope.a-1)

# if using a template language, the scope is set to the current line
tag_scope = view.line(sel) if self.get_setting('afn_template_languages',view) else view.extract_scope(scope.a-1)

path = view.substr(scope)
if path.startswith(("'","\"","(")):
path = path[1:-1]

path = path[path.rfind('/'):] if '/' in path else ''
path = path[path.rfind(FileNameComplete.sep):] if FileNameComplete.sep in path else path
full_path = self.this_dir + path

if '<img' in view.substr(tag_scope) and path.endswith(('.png','.jpg','.jpeg','.gif')):
if self.img_tag_in_region(tag_scope) and path.endswith(('.png','.jpg','.jpeg','.gif')):
with open(full_path,'rb') as r:
read_data = r.read() if path.endswith(('.jpg','.jpeg')) else r.read(24)
con_type, w, h = getImageInfo(read_data)
if self.get_setting('afn_insert_width_first',view):
self.insert_dimension(edit,h,'height',tag_scope)
self.insert_dimension(edit,w,'width',tag_scope)
else:
self.insert_dimension(edit,w,'width',tag_scope)
self.insert_dimension(edit,h,'height',tag_scope)
w, h = getImageInfo(read_data)

self.insert_dimensions(edit, tag_scope, w, h)


# When backspacing through a path, selects the previous path component
class ReloadAutoCompleteCommand(sublime_plugin.TextCommand):
def run(self,edit):
view = self.view
Expand All @@ -57,23 +122,32 @@ def run(self,edit):

scope = view.extract_scope(sel-1)
scope_text = view.substr(scope)
slash_pos = scope_text[:sel - scope.a].rfind('/')
slash_pos = scope_text[:sel - scope.a].rfind(FileNameComplete.sep)
slash_pos += 1 if slash_pos < 0 else 0

region = sublime.Region(scope.a+slash_pos+1,sel)
view.sel().add(region)


class FileNameComplete(sublime_plugin.EventListener):
def on_activated(self,view):
self.size = view.size()
self.showing_win_drives = False
FileNameComplete.is_active = False
FileNameComplete.sep = '/'

def get_drives(self):
# Search through valid drive names and see if they exist. (stolen from Facelessuser)
return [[d+":"+FileNameComplete.sep, d+":"+FileNameComplete.sep] for d in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" if os.path.exists(d + ":")]

def on_query_context(self, view, key, operator, operand, match_all):
if key == "afn_insert_dimensions":
return self.get_setting('afn_insert_dimensions',view) == operand
if key == "afn_deleting_slash":
if key == "afn_deleting_slash": # for reloading autocomplete
sel = view.sel()[0]
valid = self.at_path_end(view) and sel.empty() and view.substr(sel.a-1) == '/'
valid = self.at_path_end(view) and sel.empty() and view.substr(sel.a-1) == FileNameComplete.sep
return valid == operand
if key == "afn_use_keybinding":
return self.get_setting('afn_use_keybinding',view) == operand

def at_path_end(self,view):
sel = view.sel()[0]
Expand All @@ -84,32 +158,47 @@ def at_path_end(self,view):
return True
return False

def on_selection_modified(self,view):
def on_modified(self, view):
sel = view.sel()[0].a
txt = view.substr(sublime.Region(sel-4,sel-3))
if (self.showing_win_drives and txt == FileNameComplete.sep):
self.showing_win_drives = False
view.run_command('afn_delete_prefixed_slash')

def on_selection_modified_async(self,view):
if not view.window():
return
sel = view.sel()[0]
if sel.empty() and self.at_path_end(view):
if view.substr(sel.a-1) == '/' or len(view.extract_scope(sel.a)) < 3:
scope_contents = view.substr(view.extract_scope(sel.a-1))
p = scope_contents.replace('\r\n', '\n').split('\n')[0]
if('\\' in p and not '/' in p):
FileNameComplete.sep = '\\'
else:
FileNameComplete.sep = '/'
if view.substr(sel.a-1) == FileNameComplete.sep or len(view.extract_scope(sel.a)) < 3:
view.run_command('auto_complete',
{'disable_auto_insert': True,
'next_completion_if_showing': False})
else:
FileNameComplete.is_active = False

def fix_dir(self,sdir,fn):
if fn.endswith(('.png','.jpg','.jpeg','.gif')):
path = os.path.join(sdir, fn)
with open(path,'rb') as r:
read_data = r.read() if path.endswith(('.jpg','.jpeg')) else r.read(24)
con_type, w, h = getImageInfo(read_data)
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))
scope_contents = view.substr(view.extract_scope(sel-1)).strip()
cur_path = scope_contents.replace('\r\n', '\n').split('\n')[0]
if cur_path.startswith(("'","\"","(")):
cur_path = cur_path[1:-1]

return cur_path[:cur_path.rfind('/')+1] if '/' in cur_path else ''
return cur_path[:cur_path.rfind(FileNameComplete.sep)+1] if FileNameComplete.sep in cur_path else ''

def get_setting(self,string,view=None):
if view and view.settings().get(string):
Expand All @@ -120,38 +209,55 @@ def get_setting(self,string,view=None):
def on_query_completions(self, view, prefix, locations):
is_proj_rel = self.get_setting('afn_use_project_root',view)
valid_scopes = self.get_setting('afn_valid_scopes',view)
blacklist = self.get_setting('afn_blacklist_scopes', view)
uses_keybinding = self.get_setting('afn_use_keybinding', view)

sel = view.sel()[0].a
this_dir = ""
completions = []

if uses_keybinding and not FileNameComplete.is_active:
return
if not any(s in view.scope_name(sel) for s in valid_scopes):
return []
return
if any(s in view.scope_name(sel) for s in blacklist):
return

cur_path = self.get_cur_path(view, sel)
cur_path = os.path.expanduser(self.get_cur_path(view, sel))

if is_proj_rel:
this_dir = self.get_setting('afn_proj_root',view)
if len(this_dir) < 2:
for f in sublime.active_window().folders():
if f in view.file_name():
this_dir = f

if cur_path.startswith('/') or cur_path.startswith('\\'):
if is_proj_rel:
proot = self.get_setting('afn_proj_root', view)
if proot:
if not view.file_name() and not os.path.isabs(proot):
proot = "/"
cur_path = os.path.join(proot, cur_path[1:])
else:
for f in sublime.active_window().folders():
if f in view.file_name():
cur_path = f
elif not view.file_name():
return
else:
if not view.file_name():
return
this_dir = os.path.split(view.file_name())[0]

this_dir = os.path.join(this_dir, cur_path)

try:
if sublime.platform() == "windows" and len(view.extract_scope(sel)) < 4 and os.path.isabs(cur_path):
self.showing_win_drives = True
return self.get_drives()
self.showing_win_drives = False
dir_files = os.listdir(this_dir)

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 d.startswith('.'): continue
if not '.' in d: d += FileNameComplete.sep
completions.append((self.fix_dir(this_dir,d), d))
if completions:
InsertDimensionsCommand.this_dir = this_dir
return completions
return completions
return
except OSError:
print "AutoFileName: could not find " + this_dir
return
print("AutoFileName: could not find " + this_dir)
return
Loading