feat(SublimeText2.EditorPackages): cache packages
This commit is contained in:
1
EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/.gitignore
vendored
Normal file
1
EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.DS_Store
|
@@ -0,0 +1,212 @@
|
||||
import sublime, sublime_plugin, re, sys
|
||||
from string import Template
|
||||
|
||||
class AbacusCommand(sublime_plugin.TextCommand):
|
||||
"""
|
||||
Main entry point. Find candidates for alignment,
|
||||
calculate appropriate column widths, and then
|
||||
perform a series of replacements.
|
||||
"""
|
||||
def run(self, edit):
|
||||
candidates = []
|
||||
separators = sublime.load_settings("Abacus.sublime-settings").get("com.khiltd.abacus.separators")
|
||||
indentor = Template("$indentation$left_col")
|
||||
lg_aligner = Template("$left_col$separator")
|
||||
rg_aligner = Template("$left_col$gutter$separator_padding$separator")
|
||||
|
||||
#Run through the separators accumulating alignment candidates
|
||||
#starting with the longest ones i.e. '==' before '='.
|
||||
longest_first = self.sort_separators(separators)
|
||||
|
||||
#Favor those that lean right so assignments with slice notation in them
|
||||
#get handled sanely
|
||||
for separator in [righty for righty in longest_first if righty["gravity"] == "right"]:
|
||||
self.find_candidates_for_separator(separator, candidates)
|
||||
|
||||
for separator in [lefty for lefty in longest_first if lefty["gravity"] == "left"]:
|
||||
self.find_candidates_for_separator(separator, candidates)
|
||||
|
||||
#After accumulation is done, figure out what the minimum required
|
||||
#indentation and column width is going to have to be to make every
|
||||
#candidate happy.
|
||||
max_indent, max_left_col_width = self.calc_left_col_width(candidates)
|
||||
|
||||
#Perform actual alignments based on gravitational affinity of separators
|
||||
for candidate in candidates:
|
||||
indent = 0
|
||||
if not candidate["preserve_indent"]:
|
||||
indent = max_indent
|
||||
else:
|
||||
indent = candidate["adjusted_indent"]
|
||||
|
||||
sep_width = len(candidate["separator"])
|
||||
right_col = candidate["right_col"].strip()
|
||||
left_col = indentor.substitute( indentation = " " * indent,
|
||||
left_col = candidate["left_col"] )
|
||||
#Marry the separator to the proper column
|
||||
if candidate["gravity"] == "left":
|
||||
#Separator sits flush left
|
||||
left_col = lg_aligner.substitute(left_col = left_col,
|
||||
separator = candidate["separator"] )
|
||||
elif candidate["gravity"] == "right":
|
||||
gutter_width = max_left_col_width + max_indent - len(left_col) - len(candidate["separator"])
|
||||
#Push the separator ONE separator's width over the tab boundary
|
||||
left_col = rg_aligner.substitute( left_col = left_col,
|
||||
gutter = " " * gutter_width,
|
||||
separator_padding = " " * sep_width,
|
||||
separator = candidate["separator"] )
|
||||
#Most sane people will want a space between the operator and the value.
|
||||
right_col = " %s" % right_col
|
||||
#Snap the left side together
|
||||
left_col = left_col.ljust(max_indent + max_left_col_width)
|
||||
candidate["replacement"] = "%s%s\n" % (left_col, right_col)
|
||||
|
||||
#Replace each line in its entirety
|
||||
full_line = self.region_from_line_number(candidate["line"])
|
||||
#sys.stdout.write(candidate["replacement"])
|
||||
self.view.replace(edit, full_line, candidate["replacement"])
|
||||
|
||||
#Scroll and muck with the selection
|
||||
if candidates:
|
||||
self.view.sel().clear()
|
||||
for region in [self.region_from_line_number(changed["line"]) for changed in candidates]:
|
||||
start_of_right_col = region.begin() + max_indent + max_left_col_width
|
||||
insertion_point = sublime.Region(start_of_right_col, start_of_right_col)
|
||||
self.view.sel().add(insertion_point)
|
||||
#self.view.show_at_center(insertion_point)
|
||||
else:
|
||||
sublime.status_message('Abacus - no alignment token found on selected line(s)')
|
||||
|
||||
def sort_separators(self, separators):
|
||||
return sorted(separators, key=lambda sep: -len(sep["token"]))
|
||||
|
||||
def find_candidates_for_separator(self, separator, candidates):
|
||||
"""
|
||||
Given a particular separator, loop through every
|
||||
line in the current selection looking for it and
|
||||
add unique matches to a list.
|
||||
"""
|
||||
debug = sublime.load_settings("Abacus.sublime-settings").get("com.khiltd.abacus.debug")
|
||||
token = separator["token"]
|
||||
selection = self.view.sel()
|
||||
new_candidates = []
|
||||
for region in selection:
|
||||
for line in self.view.lines(region):
|
||||
line_no = self.view.rowcol(line.begin())[0]
|
||||
|
||||
#Never match a line more than once
|
||||
if len([match for match in candidates if match["line"] == line_no]):
|
||||
continue
|
||||
|
||||
#Collapse any string literals that might
|
||||
#also contain our separator token so that
|
||||
#we can reliably find the location of the
|
||||
#real McCoy.
|
||||
line_content = self.view.substr(line)
|
||||
collapsed = line_content
|
||||
|
||||
for match in re.finditer(r"(\"[^\"]*(?<!\\)\"|'[^']*(?<!\\)'|\%(q|Q)?\{.*\})", line_content):
|
||||
quoted_string = match.group(0)
|
||||
collapsed = collapsed.replace(quoted_string, "\007" * len(quoted_string))
|
||||
|
||||
#Look for ':' but not '::', '=' but not '=>'
|
||||
#And remember that quoted strings were collapsed
|
||||
#up above!
|
||||
token_pos = None
|
||||
safe_token = re.escape(token)
|
||||
token_matcher = re.compile(r"(?<![^a-zA-Z0-9_ \007])%s(?![^a-zA-Z0-9_# \007])" % (safe_token))
|
||||
potential_matches = [m for m in token_matcher.finditer(collapsed)]
|
||||
|
||||
if debug:
|
||||
print "Pattern:"
|
||||
print token_matcher.pattern
|
||||
print "Matches:"
|
||||
print potential_matches
|
||||
|
||||
if len(potential_matches):
|
||||
#Split on the first/last occurrence of the token
|
||||
if separator["gravity"] == "right":
|
||||
token_pos = potential_matches[-1].start()
|
||||
elif separator["gravity"] == "left":
|
||||
token_pos = potential_matches[0].start()
|
||||
|
||||
#Do you see what I see?
|
||||
if debug:
|
||||
sys.stdout.write("%s\n" % line_content.encode("ascii", "ignore"))
|
||||
sys.stdout.write(" " * token_pos)
|
||||
sys.stdout.write("^\n")
|
||||
|
||||
#Now we can slice
|
||||
left_col = self.detab(line_content[:token_pos]).rstrip()
|
||||
right_col = self.detab(line_content[token_pos + len(token):])
|
||||
sep = line_content[token_pos:token_pos + len(token)]
|
||||
initial_indent = re.match("\s+", left_col) or 0
|
||||
|
||||
if initial_indent:
|
||||
initial_indent = len(initial_indent.group(0))
|
||||
#Align to tab boundary
|
||||
if initial_indent % self.tab_width >= self.tab_width / 2:
|
||||
initial_indent = self.snap_to_next_boundary(initial_indent, self.tab_width)
|
||||
else:
|
||||
initial_indent -= initial_indent % self.tab_width
|
||||
candidate = { "line": line_no,
|
||||
"original": line_content,
|
||||
"separator": sep,
|
||||
"gravity": separator["gravity"],
|
||||
"adjusted_indent": initial_indent,
|
||||
"preserve_indent": separator.get("preserve_indentation", False),
|
||||
"left_col": left_col.lstrip(),
|
||||
"right_col": right_col.rstrip() }
|
||||
new_candidates.append(candidate)
|
||||
#Poke more stuff in the accumulator
|
||||
candidates.extend(new_candidates)
|
||||
|
||||
def calc_left_col_width(self, candidates):
|
||||
"""
|
||||
Given a list of lines we've already matched against
|
||||
one or more separators, loop through them all to
|
||||
normalize their indentation and determine the minimum
|
||||
possible column width that will accomodate them all
|
||||
when aligned to a tab stop boundary.
|
||||
"""
|
||||
max_width = 0
|
||||
max_indent = 0
|
||||
max_sep_width = 0
|
||||
|
||||
for candidate in candidates:
|
||||
max_indent = max([candidate["adjusted_indent"], max_indent])
|
||||
max_sep_width = max([len(candidate["separator"]), max_sep_width])
|
||||
max_width = max([len(candidate["left_col"].rstrip()), max_width])
|
||||
|
||||
max_width += max_sep_width
|
||||
|
||||
#Bump up to the next multiple of tab_width
|
||||
max_width = self.snap_to_next_boundary(max_width, self.tab_width)
|
||||
|
||||
return max_indent, max_width
|
||||
|
||||
@property
|
||||
def tab_width(self):
|
||||
"""
|
||||
Exceptionally inefficient
|
||||
"""
|
||||
return int(self.view.settings().get('tab_size', 4))
|
||||
|
||||
def detab(self, input):
|
||||
"""
|
||||
Goodbye tabs!
|
||||
"""
|
||||
return input.expandtabs(self.tab_width)
|
||||
|
||||
def region_from_line_number(self, line_number):
|
||||
"""
|
||||
Given a zero-based line number, return a region
|
||||
encompassing it (including the newline).
|
||||
"""
|
||||
return self.view.full_line(self.view.text_point(line_number, 0))
|
||||
|
||||
def snap_to_next_boundary(self, value, interval):
|
||||
"""
|
||||
Alignment voodoo
|
||||
"""
|
||||
return value + (interval - value % interval)
|
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"com.khiltd.abacus.debug": true,
|
||||
"com.khiltd.abacus.separators":
|
||||
[
|
||||
{
|
||||
"token": ":",
|
||||
"gravity": "left",
|
||||
"preserve_indentation": true
|
||||
},
|
||||
{
|
||||
"token": "=",
|
||||
"gravity": "right",
|
||||
"preserve_indentation": true
|
||||
},
|
||||
{
|
||||
"token": "+=",
|
||||
"gravity": "right",
|
||||
"preserve_indentation": true
|
||||
},
|
||||
{
|
||||
"token": "-=",
|
||||
"gravity": "right",
|
||||
"preserve_indentation": true
|
||||
},
|
||||
{
|
||||
"token": "*=",
|
||||
"gravity": "right",
|
||||
"preserve_indentation": true
|
||||
},
|
||||
{
|
||||
"token": "/=",
|
||||
"gravity": "right",
|
||||
"preserve_indentation": true
|
||||
},
|
||||
{
|
||||
"token": "?=",
|
||||
"gravity": "right",
|
||||
"preserve_indentation": true
|
||||
},
|
||||
{
|
||||
"token": "||=",
|
||||
"gravity": "right",
|
||||
"preserve_indentation": true
|
||||
},
|
||||
{
|
||||
"token": "%=",
|
||||
"gravity": "right",
|
||||
"preserve_indentation": true
|
||||
},
|
||||
{
|
||||
"token": "==",
|
||||
"gravity": "right",
|
||||
"preserve_indentation": true
|
||||
}
|
||||
]
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"com.khiltd.abacus.separators":
|
||||
[
|
||||
{
|
||||
"token": ":",
|
||||
"gravity": "left",
|
||||
"preserve_indentation": false
|
||||
}
|
||||
]
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"keys": ["ctrl+alt+]"], "command": "abacus"
|
||||
}
|
||||
]
|
||||
|
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"keys": ["super+ctrl+alt+]"], "command": "abacus"
|
||||
}
|
||||
]
|
||||
|
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"keys": ["ctrl+alt+]"], "command": "abacus"
|
||||
}
|
||||
]
|
||||
|
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"caption": "Abacus: align selection",
|
||||
"command": "abacus"
|
||||
}
|
||||
]
|
@@ -0,0 +1,97 @@
|
||||
[
|
||||
{
|
||||
"id": "selection",
|
||||
"children":
|
||||
[
|
||||
{ "id": "abacus" },
|
||||
{
|
||||
"command": "abacus",
|
||||
"caption": "Abacus Align"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"caption": "Preferences",
|
||||
"mnemonic": "n",
|
||||
"id": "preferences",
|
||||
"children":
|
||||
[
|
||||
{
|
||||
"caption": "Package Settings",
|
||||
"mnemonic": "P",
|
||||
"id": "package-settings",
|
||||
"children":
|
||||
[
|
||||
{
|
||||
"caption": "Abacus",
|
||||
"children":
|
||||
[
|
||||
{
|
||||
"command": "open_file",
|
||||
"args": { "file": "${packages}/Abacus/Abacus.sublime-settings" },
|
||||
"caption": "Settings - Default"
|
||||
},
|
||||
{
|
||||
"command": "open_file",
|
||||
"args": { "file": "${packages}/User/Abacus.sublime-settings" },
|
||||
"caption": "Settings - User"
|
||||
},
|
||||
{
|
||||
"command": "open_file_settings",
|
||||
"caption": "Settings - Syntax Specific - User"
|
||||
},
|
||||
{
|
||||
"command": "open_file",
|
||||
"args": {
|
||||
"file": "${packages}/Abacus/Default (Windows).sublime-keymap",
|
||||
"platform": "Windows"
|
||||
},
|
||||
"caption": "Key Bindings – Default"
|
||||
},
|
||||
{
|
||||
"command": "open_file",
|
||||
"args": {
|
||||
"file": "${packages}/Abacus/Default (OSX).sublime-keymap",
|
||||
"platform": "OSX"
|
||||
},
|
||||
"caption": "Key Bindings – Default"
|
||||
},
|
||||
{
|
||||
"command": "open_file",
|
||||
"args": {
|
||||
"file": "${packages}/Abacus/Default (Linux).sublime-keymap",
|
||||
"platform": "Linux"
|
||||
},
|
||||
"caption": "Key Bindings – Default"
|
||||
},
|
||||
{
|
||||
"command": "open_file",
|
||||
"args": {
|
||||
"file": "${packages}/User/Default (Windows).sublime-keymap",
|
||||
"platform": "Windows"
|
||||
},
|
||||
"caption": "Key Bindings – User"
|
||||
},
|
||||
{
|
||||
"command": "open_file",
|
||||
"args": {
|
||||
"file": "${packages}/User/Default (OSX).sublime-keymap",
|
||||
"platform": "OSX"
|
||||
},
|
||||
"caption": "Key Bindings – User"
|
||||
},
|
||||
{
|
||||
"command": "open_file",
|
||||
"args": {
|
||||
"file": "${packages}/User/Default (Linux).sublime-keymap",
|
||||
"platform": "Linux"
|
||||
},
|
||||
"caption": "Key Bindings – User"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
@@ -0,0 +1,59 @@
|
||||
Abacus Alignment Plugin for Sublime Text 2
|
||||
================
|
||||
|
||||

|
||||
|
||||
I'm pretty anal about aligning things in my code, but the alignment plugins I tried were more-or-less one-trick-ponies, and I didn't like any of their tricks, so I made my own.
|
||||
|
||||
My one anal pony trick involves allowing you to slide the operator like an abacus bead, toward either the left or the right hand side, by giving each possible token a `gravity` property like so:
|
||||
|
||||
``` json
|
||||
{
|
||||
"com.khiltd.abacus.separators":
|
||||
[
|
||||
{
|
||||
"token": ":",
|
||||
"gravity": "left",
|
||||
"preserve_indentation": true
|
||||
},
|
||||
{
|
||||
"token": "=",
|
||||
"gravity": "right",
|
||||
"preserve_indentation": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Abacus focuses on aligning assignments in as language-agnostic a manner as possible and strives to address most of the open issues in that other, more popular plugin (it won't even jack up your Backbone routes!). It is, however, an *alignment* tool and *not* a full-blown beautifier. It works best when there's one assignment per line; if you like shoving dozens of CSS or JSON declarations on a single line then you are an enemy of readability and this plugin will make every effort to hinder and harm your creature on Earth as far as it is able.
|
||||
|
||||
`preserve_indentation` is a tip that you might be working in a language where whitespace is significant, thereby suggesting that Abacus should make no effort to normalize indentation across lines. It's not foolproof, especially if you set your tab width really, really low, but it tries harder than Cory Doctorow ever has. OK, you're right... It would be impossible for anyone to try harder than that.
|
||||
|
||||
Usage
|
||||
============
|
||||
|
||||
Make a selection, then `command + option + control + ]`.
|
||||
|
||||
Think the plugin's crazy? Add the following to your config:
|
||||
|
||||
```
|
||||
"com.khiltd.abacus.debug": true
|
||||
```
|
||||
|
||||
and Abacus will dump its thoughts out to Sublime Text's console like so:
|
||||
|
||||
```
|
||||
margin:0;
|
||||
^
|
||||
padding:0;
|
||||
^
|
||||
border-style:none;
|
||||
^
|
||||
```
|
||||
|
||||
Caveats
|
||||
============
|
||||
|
||||
I've used nothing but Macs since 1984 and do absolutely **no** testing in Windows or Ububian's window manager of the minute. If something's broken in some OS I don't own, you'll need to have a suggestion as to how it can be fixed as I'm unlikely to have any idea what you're talking about.
|
||||
|
||||
I don't care if you like real tabs or Windows line endings and don't bother with handling them. Seriously, what year is this?
|
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"com.khiltd.abacus.separators":
|
||||
[
|
||||
{
|
||||
"token": "=>",
|
||||
"gravity": "right",
|
||||
"preserve_indentation": true
|
||||
}
|
||||
]
|
||||
}
|
@@ -0,0 +1 @@
|
||||
{"url": "https://github.com/khiltd/Abacus", "version": "2013.01.11.00.24.46", "description": "An Alignment Plugin for Sublime Text 2 that actually works `\u2318\u2325^ ]`"}
|
Reference in New Issue
Block a user