├── .gitignore ├── Default (Linux).sublime-keymap ├── Default (OSX).sublime-keymap ├── Default (Windows).sublime-keymap ├── Default.sublime-commands ├── readme.creole ├── Main.sublime-menu └── Prefixr.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.sublime-project 3 | *.sublime-workspace -------------------------------------------------------------------------------- /Default (Linux).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { "keys": ["ctrl+alt+x"], "command": "prefixr" } 3 | ] -------------------------------------------------------------------------------- /Default (OSX).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { "keys": ["ctrl+super+x"], "command": "prefixr" } 3 | ] -------------------------------------------------------------------------------- /Default (Windows).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { "keys": ["ctrl+alt+x"], "command": "prefixr" } 3 | ] -------------------------------------------------------------------------------- /Default.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Preferences: Prefixr Key Bindings – Default", 4 | "command": "open_file", 5 | "args": { 6 | "file": "${packages}/Prefixr/Default (Windows).sublime-keymap", 7 | "platform": "Windows" 8 | } 9 | }, 10 | { 11 | "caption": "Preferences: Prefixr Key Bindings – Default", 12 | "command": "open_file", 13 | "args": { 14 | "file": "${packages}/Prefixr/Default (OSX).sublime-keymap", 15 | "platform": "OSX" 16 | } 17 | }, 18 | { 19 | "caption": "Preferences: Prefixr Key Bindings – Default", 20 | "command": "open_file", 21 | "args": { 22 | "file": "${packages}/Prefixr/Default (Linux).sublime-keymap", 23 | "platform": "Linux" 24 | } 25 | }, 26 | { 27 | "caption": "Preferences: Prefixr Key Bindings – User", 28 | "command": "open_file", 29 | "args": { 30 | "file": "${packages}/User/Default (Windows).sublime-keymap", 31 | "platform": "Windows" 32 | } 33 | }, 34 | { 35 | "caption": "Preferences: Prefixr Key Bindings – User", 36 | "command": "open_file", 37 | "args": { 38 | "file": "${packages}/User/Default (OSX).sublime-keymap", 39 | "platform": "OSX" 40 | } 41 | }, 42 | { 43 | "caption": "Preferences: Prefixr Key Bindings – User", 44 | "command": "open_file", 45 | "args": { 46 | "file": "${packages}/User/Default (Linux).sublime-keymap", 47 | "platform": "Linux" 48 | } 49 | } 50 | ] -------------------------------------------------------------------------------- /readme.creole: -------------------------------------------------------------------------------- 1 | = Discontinued Project 2 | 3 | Unfortunately as of 2014, the API for prefixr.com no longer works, which 4 | means the package no longer functions. The source code is maintained here 5 | for learning purposes only. 6 | 7 | = Sublime Prefixr 8 | 9 | A plugin that runs CSS through the [[http://prefixr.com|Prefixr]] API for 10 | [[http://sublimetext.com/2|Sublime Text 2]]. 11 | 12 | Please see http://wbond.net/sublime_packages/prefixr for install instructions, 13 | screenshots and documentation. 14 | 15 | == License 16 | 17 | All of Sublime Prefixr is licensed under the MIT license. 18 | 19 | Copyright (c) 2011 Will Bond 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in 29 | all copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 37 | THE SOFTWARE. 38 | -------------------------------------------------------------------------------- /Main.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "edit", 4 | "children": 5 | [ 6 | {"id": "wrap"}, 7 | { "command": "prefixr" } 8 | ] 9 | }, 10 | { 11 | "id": "preferences", 12 | "children": 13 | [ 14 | { 15 | "caption": "Package Settings", 16 | "mnemonic": "P", 17 | "id": "package-settings", 18 | "children": 19 | [ 20 | { 21 | "caption": "Prefixr", 22 | "children": 23 | [ 24 | { 25 | "command": "open_file", 26 | "args": { 27 | "file": "${packages}/Prefixr/Default (Windows).sublime-keymap", 28 | "platform": "Windows" 29 | }, 30 | "caption": "Key Bindings – Default" 31 | }, 32 | { 33 | "command": "open_file", 34 | "args": { 35 | "file": "${packages}/Prefixr/Default (OSX).sublime-keymap", 36 | "platform": "OSX" 37 | }, 38 | "caption": "Key Bindings – Default" 39 | }, 40 | { 41 | "command": "open_file", 42 | "args": { 43 | "file": "${packages}/Prefixr/Default (Linux).sublime-keymap", 44 | "platform": "Linux" 45 | }, 46 | "caption": "Key Bindings – Default" 47 | }, 48 | { 49 | "command": "open_file", 50 | "args": { 51 | "file": "${packages}/User/Default (Windows).sublime-keymap", 52 | "platform": "Windows" 53 | }, 54 | "caption": "Key Bindings – User" 55 | }, 56 | { 57 | "command": "open_file", 58 | "args": { 59 | "file": "${packages}/User/Default (OSX).sublime-keymap", 60 | "platform": "OSX" 61 | }, 62 | "caption": "Key Bindings – User" 63 | }, 64 | { 65 | "command": "open_file", 66 | "args": { 67 | "file": "${packages}/User/Default (Linux).sublime-keymap", 68 | "platform": "Linux" 69 | }, 70 | "caption": "Key Bindings – User" 71 | }, 72 | { "caption": "-" } 73 | ] 74 | } 75 | ] 76 | } 77 | ] 78 | } 79 | ] 80 | -------------------------------------------------------------------------------- /Prefixr.py: -------------------------------------------------------------------------------- 1 | import sublime 2 | import sublime_plugin 3 | import urllib 4 | import urllib2 5 | import threading 6 | import re 7 | 8 | 9 | class PrefixrCommand(sublime_plugin.TextCommand): 10 | def run(self, edit): 11 | # We check for braces since we can do a better job of preserving 12 | # whitespace when braces are not present 13 | braces = False 14 | sels = self.view.sel() 15 | for sel in sels: 16 | if self.view.substr(sel).find('{') != -1: 17 | braces = True 18 | 19 | # Expand selection to braces, unfortunately this can't use the 20 | # built in move_to brackets since that matches parentheses also 21 | if not braces: 22 | new_sels = [] 23 | for sel in sels: 24 | new_sels.append(self.view.find('\}', sel.end())) 25 | sels.clear() 26 | for sel in new_sels: 27 | sels.add(sel) 28 | self.view.run_command("expand_selection", {"to": "brackets"}) 29 | 30 | # We start one thread per selection so we don't lock up the interface 31 | # while waiting for the response from the API 32 | threads = [] 33 | for sel in sels: 34 | string = self.view.substr(sel) 35 | thread = PrefixrApiCall(sel, string, 5) 36 | threads.append(thread) 37 | thread.start() 38 | 39 | # We clear all selection because we are going to manually set them 40 | self.view.sel().clear() 41 | 42 | # This creates an edit group so we can undo all changes in one go 43 | edit = self.view.begin_edit('prefixr') 44 | 45 | self.handle_threads(edit, threads, braces) 46 | 47 | def handle_threads(self, edit, threads, braces, offset=0, i=0, dir=1): 48 | next_threads = [] 49 | for thread in threads: 50 | if thread.is_alive(): 51 | next_threads.append(thread) 52 | continue 53 | if thread.result == False: 54 | continue 55 | offset = self.replace(edit, thread, braces, offset) 56 | threads = next_threads 57 | 58 | if len(threads): 59 | # This animates a little activity indicator in the status area 60 | before = i % 8 61 | after = (7) - before 62 | if not after: 63 | dir = -1 64 | if not before: 65 | dir = 1 66 | i += dir 67 | self.view.set_status('prefixr', 'Prefixr [%s=%s]' % \ 68 | (' ' * before, ' ' * after)) 69 | 70 | sublime.set_timeout(lambda: self.handle_threads(edit, threads, 71 | braces, offset, i, dir), 100) 72 | return 73 | 74 | self.view.end_edit(edit) 75 | 76 | self.view.erase_status('prefixr') 77 | selections = len(self.view.sel()) 78 | sublime.status_message('Prefixr successfully run on %s selection%s' % 79 | (selections, '' if selections == 1 else 's')) 80 | 81 | def replace(self, edit, thread, braces, offset): 82 | sel = thread.sel 83 | original = thread.original 84 | result = thread.result 85 | 86 | # Here we adjust each selection for any text we have already inserted 87 | if offset: 88 | sel = sublime.Region(sel.begin() + offset, 89 | sel.end() + offset) 90 | 91 | result = self.normalize_line_endings(result) 92 | (prefix, main, suffix) = self.fix_whitespace(original, result, sel, 93 | braces) 94 | self.view.replace(edit, sel, prefix + main + suffix) 95 | 96 | # We add the end of the new text to the selection 97 | end_point = sel.begin() + len(prefix) + len(main) 98 | self.view.sel().add(sublime.Region(end_point, end_point)) 99 | 100 | return offset + len(prefix + main + suffix) - len(original) 101 | 102 | def normalize_line_endings(self, string): 103 | string = string.replace('\r\n', '\n').replace('\r', '\n') 104 | line_endings = self.view.settings().get('default_line_ending') 105 | if line_endings == 'windows': 106 | string = string.replace('\n', '\r\n') 107 | elif line_endings == 'mac': 108 | string = string.replace('\n', '\r') 109 | return string 110 | 111 | def fix_whitespace(self, original, prefixed, sel, braces): 112 | # If braces are present we can do all of the whitespace magic 113 | if braces: 114 | return ('', prefixed, '') 115 | 116 | # Determine the indent of the CSS rule 117 | (row, col) = self.view.rowcol(sel.begin()) 118 | indent_region = self.view.find('^\s+', self.view.text_point(row, 0)) 119 | if indent_region and self.view.rowcol(indent_region.begin())[0] == row: 120 | indent = self.view.substr(indent_region) 121 | else: 122 | indent = '' 123 | 124 | # Strip whitespace from the prefixed version so we get it right 125 | prefixed = prefixed.strip() 126 | prefixed = re.sub(re.compile('^\s+', re.M), '', prefixed) 127 | 128 | # Indent the prefixed version to the right level 129 | settings = self.view.settings() 130 | use_spaces = settings.get('translate_tabs_to_spaces') 131 | tab_size = int(settings.get('tab_size', 8)) 132 | indent_characters = '\t' 133 | if use_spaces: 134 | indent_characters = ' ' * tab_size 135 | prefixed = prefixed.replace('\n', '\n' + indent + indent_characters) 136 | 137 | match = re.search('^(\s*)', original) 138 | prefix = match.groups()[0] 139 | match = re.search('(\s*)\Z', original) 140 | suffix = match.groups()[0] 141 | 142 | return (prefix, prefixed, suffix) 143 | 144 | 145 | class PrefixrApiCall(threading.Thread): 146 | def __init__(self, sel, string, timeout): 147 | self.sel = sel 148 | self.original = string 149 | self.timeout = timeout 150 | self.result = None 151 | threading.Thread.__init__(self) 152 | 153 | def run(self): 154 | try: 155 | data = urllib.urlencode({'css': self.original}) 156 | request = urllib2.Request('http://prefixr.com/api/index.php', data, 157 | headers={"User-Agent": "Sublime Prefixr"}) 158 | http_file = urllib2.urlopen(request, timeout=self.timeout) 159 | self.result = http_file.read() 160 | return 161 | 162 | except (urllib2.HTTPError) as (e): 163 | err = '%s: HTTP error %s contacting API' % (__name__, str(e.code)) 164 | except (urllib2.URLError) as (e): 165 | err = '%s: URL error %s contacting API' % (__name__, str(e.reason)) 166 | 167 | sublime.error_message(err) 168 | self.result = False 169 | --------------------------------------------------------------------------------