├── Default (Linux).sublime-keymap ├── Default (OSX).sublime-keymap ├── Default (Windows).sublime-keymap ├── Main.sublime-menu ├── .gitignore ├── LICENSE ├── README.md └── select_quotes.py /Default (Linux).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | {"keys": ["ctrl+'"], "command": "select_quoted"}, 3 | {"keys": ["ctrl+shift+'"], "command": "select_quoted", "args": {"inside": false} } 4 | ] -------------------------------------------------------------------------------- /Default (OSX).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | {"keys": ["super+'"], "command": "select_quoted"}, 3 | {"keys": ["super+shift+'"], "command": "select_quoted", "args": {"around": true} } 4 | ] -------------------------------------------------------------------------------- /Default (Windows).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | {"keys": ["ctrl+'"], "command": "select_quoted"}, 3 | {"keys": ["ctrl+shift+'"], "command": "select_quoted", "args": {"inside": false} } 4 | ] -------------------------------------------------------------------------------- /Main.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Selection", 4 | "mnemonic": "S", 5 | "id": "selection", 6 | "children": 7 | [ 8 | { "command": "select_quoted", "caption": "Expand Selection to Quoted" } 9 | ] 10 | } 11 | ] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Matthew E. Torok 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Sublime Select Quoted 2 | ===================== 3 | 4 | This is a small plugin for Sublime Text 2 and 3 which adds 'select within quoted' and 'select around quoted' functionality. This is roughly equivalent to Vim's `vi"` and `va"` commands. 5 | 6 | 7 | Usage 8 | ----- 9 | 10 | To select text within quotation marks, place your cursor within some quoted text, or partially select some quoted text. Then either select "Expand Selection to Quoted" under the "Edit" menu, or press `command+'` (Mac)/`control+'` (Linux/Windows) on your keyboard. 11 | 12 | To select the text around the quote marks (that is, the quoted text and the quotes themselves,) use the keyboard shortcut `command+shift+'`/`control+shift+'`. Additionally, if the selection includes the starting or ending quote, or if the selection is surrounded by quotes on either side, the expanded selection will include the quotes automatically. 13 | 14 | 15 | Install 16 | ------- 17 | 18 | The easiest way to install this plugin is to use [package control](http://wbond.net/sublime_packages/package_control) (search for 'Select Quoted'.) 19 | 20 | Alternatively, you can `git clone` this repo into your Sublime packages directory manually. 21 | 22 | 23 | Additional Info 24 | --------------- 25 | 26 | While we refer to "quotes" above, this plugin should work with any string deliminator. The only requirement is that the syntax file for the language tag the string with the scope string.quoted, and it is deliminated by characters tagged with the scope punctuation.definition.string. -------------------------------------------------------------------------------- /select_quotes.py: -------------------------------------------------------------------------------- 1 | import sublime, sublime_plugin 2 | 3 | class SelectQuoted(sublime_plugin.TextCommand): 4 | def run(self, edit, around=False): 5 | selections = self.view.sel() 6 | newSel = [] 7 | 8 | for originalRegion in selections: 9 | # If point a proceeds point b, reverse it to make calculations easier 10 | region = originalRegion if originalRegion.a <= originalRegion.b else sublime.Region(originalRegion.b, originalRegion.a) 11 | 12 | newRegion = self.getStringRegion(region, around) 13 | 14 | if newRegion is None: 15 | newSel.append(originalRegion) 16 | else: 17 | # Un-reverse region if we reversed it at the start 18 | newRegion = newRegion if originalRegion.a <= originalRegion.b else sublime.Region(newRegion.b, newRegion.a) 19 | newSel.append(newRegion) 20 | 21 | selections.clear() 22 | for reg in newSel: 23 | selections.add(reg) 24 | 25 | self.getTemplateRegion(region, around) 26 | 27 | 28 | def getStringRegion(self, region, around=False): 29 | """ 30 | If region resides within a single string, return a Region which encompasses the whole 31 | string. Otherwise, returns the original region. 32 | """ 33 | # Shrink the end by one unit, keeping a <= b, because we really care about the character 34 | # right before the end cursor, not after. 35 | region = sublime.Region(region.a, max(region.a, region.b - 1)) 36 | 37 | # Mark sure the entire selected region lies within a quoted string 38 | for pos in range(region.a, region.b + 1): 39 | if self.view.score_selector(pos, "string.quoted") <= 0: 40 | return None 41 | 42 | # Turn on 'round' implicitly if... 43 | # The start of the selection is a quote 44 | if self.view.score_selector(region.a, "punctuation.definition.string") > 0: 45 | around = True 46 | # The end of the selection is a quote 47 | if self.view.score_selector(region.b, "punctuation.definition.string") > 0: 48 | around = False 49 | # The selected region is abutted by quotes on either side 50 | if (self.view.score_selector(region.a - 1, "punctuation.definition.string") > 0 and 51 | self.view.score_selector(region.b + 1, "punctuation.definition.string") > 0): 52 | around = True 53 | 54 | # Predicates for if we should continue expanding the selection foreward/back 55 | if around: 56 | # We should only stop when we're on a quote, but the next character isn't a quote 57 | shouldExpandA = lambda a:((not (self.view.score_selector(a, "punctuation.definition.string") > 0 and not self.view.score_selector(a - 1, "punctuation.definition.string") > 0)) and (self.view.score_selector(a - 1, "string.quoted") > 1)) 58 | shouldExpandB = lambda b:((not (self.view.score_selector(b - 1, "punctuation.definition.string") > 0 and not self.view.score_selector(b, "punctuation.definition.string") > 0)) and (self.view.score_selector(b, "string.quoted") > 1)) 59 | else: 60 | # We should only stop as soon as we see a quote 61 | shouldExpandA = lambda a:((not self.view.score_selector(a - 1, "punctuation.definition.string") > 0) and (self.view.score_selector(a - 1, "string.quoted") > 1)) 62 | shouldExpandB = lambda b:((not self.view.score_selector(b, "punctuation.definition.string") > 0) and (self.view.score_selector(b, "string.quoted") > 1)) 63 | 64 | # Expand the selection by expanding the start, then the end 65 | expandedRegion = sublime.Region(region.a, region.b) 66 | expandedA = region.a 67 | expandedB = region.b 68 | while shouldExpandA(expandedA): 69 | expandedA = expandedA - 1 70 | while shouldExpandB(expandedB): 71 | expandedB = expandedB + 1 72 | 73 | return sublime.Region(expandedA, expandedB) 74 | 75 | 76 | def getTemplateRegion(self, region, around=False): 77 | regions = self.view.find_by_selector('string.template') 78 | regions = regions + self.view.find_by_selector('meta.template.expression') 79 | 80 | for region in regions: 81 | for selection in self.view.sel(): 82 | if region.contains(selection): 83 | start = region.a 84 | stop = region.b 85 | while (start > 0 and not self.view.match_selector(start, 'punctuation.definition.string.template.begin')): 86 | start = start - 1 87 | while (stop < self.view.size() and not self.view.match_selector(stop, 'punctuation.definition.string.template.end')): 88 | stop = stop + 1 89 | 90 | self.view.sel().add(sublime.Region(start + 1, stop)) 91 | 92 | 93 | def description(self, around=False): 94 | if around: 95 | return "Expand Selection to Quotes" 96 | else: 97 | return "Expand Selection to Quoted" 98 | --------------------------------------------------------------------------------