├── .gitignore ├── README.md ├── doc └── textobjectify.txt └── plugin └── textobjectify.vim /.gitignore: -------------------------------------------------------------------------------- 1 | /doc/tags 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TextObjectify 2 | ============= 3 | 4 | Description 5 | ----------- 6 | 7 | Vim's text-objects are an extremely powerful tool. However, there are some 8 | peculiar lackings in how they operate out-of-the-box. The TextObjectify plugin 9 | attempts to rectify these peculiarities as well as add the ability to allow the 10 | user to create custom text-objects or even create text-objects on-the-fly 11 | if a object is provided that does not already exist. 12 | 13 | The peculiarities that TextObjectify attempts to resolve have to do with the 14 | mutually-exclusive functionality of the parenthesis-like text-objects and the 15 | quote-like text-objects. The parenthesis-like text-objects are all able to 16 | operate on objects that cover multiple lines; however, the cursor must be 17 | on/within the object for it to work. The quote-like text-objects only work 18 | on a single line. However, with the quote-like text-objects, the cursor will 19 | seek ahead and jump to a text object if the cursor is not already within one. 20 | 21 | For example, consider a buffer that contains nothing but the following: 22 | 23 | """ 24 | some example code. 25 | section is multiple lines 26 | """ 27 | 28 | if (getinput() == "yes") { 29 | print "this is an example" 30 | counter += 1 31 | } 32 | 33 | If the cursor is on the `p` in `print` and the user types 34 | 35 | vi{ 36 | 37 | The area between the { and } characters will be selected. However, if the 38 | cursor is on the `s` in `some` and the user types 39 | 40 | vi" 41 | 42 | nothing will happen, because quote-like text-objects do not work on multiple 43 | lines. With TextObjectify, the two lines between the triple quote sets will be 44 | selected - the parenthesis-style text-object functionality will apply. 45 | 46 | If the cursor is on the `p` in `print` and the user types 47 | 48 | vi" 49 | 50 | the string `this is an example` will be selected, because quote-style 51 | text-objects seek forward on the same line when looking for an object. 52 | However, if the cursor is on the `i` in `if` and the user types 53 | 54 | vi( 55 | 56 | nothing will happen, because parenthesis-like text-objects do not seek if 57 | they are not in already within a text-object. With TextObjectify, the region 58 | between the parenthesis will be selected. 59 | 60 | In addition to improving how existing text-objects function, TextObjectify 61 | allows user to create custom text-objects. To serve as examples, 62 | TextObjectify comes with two custom text objects: `` will operate over the 63 | entire buffer and `V` will operate over a block of viml. With default 64 | TextObjectify, if a user types 65 | 66 | "+ya 67 | 68 | the entire buffer will be stored within the quoteplus register. 69 | 70 | Consider a buffer that contains the following: 71 | 72 | if nr2char(getchar()) == g:quitmap 73 | echo "Quitting example" 74 | return 0 75 | endif 76 | 77 | If the cursor is on any of the lines shown and the user enters `viV`, the 78 | `echo` and `return` lines will be selected. 79 | 80 | Moreover, TextObjectify has the ability to create text-objects on-the-fly if 81 | an object is requested that is not provided by Vim out of the box and is not 82 | one of TextObjectify's custom text objects. Whatever character is provided 83 | becomes the delimiter for either side. For example, if the buffer contains the 84 | following: 85 | 86 | 87 | LaTeX documents can show colors such as red, blue, green, and yellow. 88 | Additionally, they can show pretty mathematics such as $\int x^2dx$. 89 | 90 | If the user would like to select an area between commas, there is no need to 91 | plan ahead and create such a custom text-object. The user can simply enter 92 | "vi," and TextObjectify will create a text-object with commas as delimiters 93 | on either side. Similarly, the user could type "ci$" to change the area 94 | between the dollar signs without having to create a custom text-object ahead 95 | of time. 96 | 97 | Setup 98 | ----- 99 | 100 | TextObjectify can be installed like most other Vim plugins. On a Unixy system 101 | without a plugin manager, the textobjectify.vim file should be located at: 102 | 103 | ~/.vim/plugin/textobjectify.vim 104 | 105 | On a Unixy system with pathogen, the textobjectify.vim file should be located at: 106 | 107 | ~/.vim/bundle/textobjectify/plugin/textobjectify.vim 108 | 109 | On a Windows system without a plugin manager, the textobjectify.vim file should be located at: 110 | 111 | %USERPROFILE%\vimfiles\plugin\textobjectify.vim 112 | 113 | On a Windows system with pathogen, the textobjectify.vim file should be located at: 114 | 115 | %USERPROFILE%\vimfiles\bundle\textobjectify\plugin\textobjectify.vim 116 | 117 | If you are using a plugin manager other than pathogen, see its documentation 118 | for how to install TextObjectify - it should be comparable to other plugins. 119 | 120 | If you would like the documentation to also be installed, include textobjectify.txt 121 | into the relevant directory described above, replacing `plugin` with `doc`. 122 | 123 | TextObjectify should have same defaults and be useful without any additional 124 | configuration. However, to get the most out of TextObjectify, it is 125 | recommended that you configure it to your own tastes. 126 | 127 | Configuration 128 | ------------- 129 | 130 | There are a handful of global variables which can be set to tweak how 131 | TextObjectify operates. The main one is g:textobjectify which contains 132 | information on how to treat all custom text-objects or modifications of 133 | existing text-objects. The others tweak how on-the-fly text-objects 134 | function. 135 | 136 | All TextObjectify objects have the following attributes: 137 | 138 | - 'left': Regex for the left delimiter 139 | - 'right': Regex for the right delimiter 140 | - 'same': Set to 1 to have object prioritize same-line objects over multi-line 141 | objects. That is, if the situation is ambiguous, act like quote-like 142 | text-objects normally do. Otherwise, set to 0 to have the object act like 143 | parenthesis-like text-objects normally do. 144 | - 'seek': Sets whether or not to search for a text-object if the cursor is 145 | not already within one. if 'seek' is 0, no seeking is done, i.e., if the 146 | cursor is not already within the text-object abort. if 'seek' is 1, search 147 | forward for a text-object if the cursor is not already in one. If 'seek' 148 | is 2, search backward for a text-object if the cursor is not already in 149 | one. The parenthesis-style text-objects come in pairs - TextObjectify 150 | defaults to having the left item of the pairs search forward and the right 151 | item search backwards. 152 | - 'line': If set to '1', it will force the object to act as though it is 153 | selected linewise. The `V` text-object which TextObjectify comes with acts 154 | this way. 155 | 156 | g:textobjectify is a Dictionary. Each key is the character which is used 157 | to select the object. The values are Dictionaries with the above five 158 | attributes. For example, the default g:textobjectify is: 159 | 160 | let g:textobjectify = { 161 | \'(': {'left': '(', 'right': ')', 'same': 0, 'seek': 1, 'line': 0}, 162 | \')': {'left': '(', 'right': ')', 'same': 0, 'seek': 2, 'line': 0}, 163 | \'{': {'left': '{', 'right': '}', 'same': 0, 'seek': 1, 'line': 0}, 164 | \'}': {'left': '{', 'right': '}', 'same': 0, 'seek': 2, 'line': 0}, 165 | \'[': {'left': '\[', 'right': '\]', 'same': 0, 'seek': 1, 'line': 0}, 166 | \']': {'left': '\[', 'right': '\]', 'same': 0, 'seek': 2, 'line': 0}, 167 | \'<': {'left': '<', 'right': '>', 'same': 0, 'seek': 1, 'line': 0}, 168 | \'>': {'left': '<', 'right': '>', 'same': 0, 'seek': 2, 'line': 0}, 169 | \'"': {'left': '"', 'right': '"', 'same': 1, 'seek': 1, 'line': 0}, 170 | \"'": {'left': "'", 'right': "'", 'same': 1, 'seek': 1, 'line': 0}, 171 | \'`': {'left': '`', 'right': '`', 'same': 1, 'seek': 1, 'line': 0}, 172 | \'V': {'left': '^\s*\(if\|for\|function\|try\|while\)\>', 173 | \'right': '^\s*end', 'same': 0, 'seek': 1, 'line': 1}, 174 | \"\": {'left': '\%^', 'right': '\%$', 'same': 0, 'seek': 0, 175 | \'line': 0}, 176 | \} 177 | 178 | This sets how the parenthesis-like text-objects, the quote-like 179 | text-objects, and the two new text-objects operate. This is a good 180 | reference if you want to make your own. 181 | 182 | To create modify or create a new text-object, copy the above lines into your 183 | vimrc and adjust accordingly. Note that if you create a g:textobjectify in 184 | your vimrc it overwrites all of TextObjectify's default values. Thus if you 185 | create an empty g:textobjectify all objects default will act as they would 186 | without TextObjectify installed - quote-like text-objects will not act on 187 | multiple lines, etc. 188 | 189 | By default, TextObjectify will create new text-objects on-the-fly if a 190 | mapping is called that does not correspond to an existing text-object (either 191 | one of Vim's defaults or one provided by TextObjectify). To disable this, set 192 | g:textobjectify_onthefly to 0. 193 | 194 | The 'same', 'seek' and 'line' attributes of on-the-fly text-objects can be 195 | customized by setting the g:textobjectify_onthefly_same, 196 | g:textobjectify_onthefly_seek, and g:textobjectify_onthefly_line variables 197 | to the desired value. Otherwise, they default to 0, 1, and 0, respectively. 198 | 199 | Changelog 200 | --------- 201 | 202 | 0.1 (2013-04-06): 203 | - initial release 204 | -------------------------------------------------------------------------------- /doc/textobjectify.txt: -------------------------------------------------------------------------------- 1 | *textobjectify.txt* Vim plugin to improve text objects 2 | 3 | TextObjectify USER MANUAL - by Daniel Thau 4 | 5 | TextObjectify 6 | 7 | 1. Description |textobjectify-intro| 8 | 2. Setup |textobjectify-setup| 9 | 3. Configuration |textobjectify-configuration| 10 | 4. Changelog |textobjectify-changelog| 11 | 12 | ============================================================================== 13 | 1. Description *textobjectify-intro* 14 | 15 | Vim's |text-objects| are an extremely powerful tool. However, there are some 16 | peculiar lackings in how they operate out-of-the-box. The TextObjectify plugin 17 | attempts to rectify these peculiarities as well as add the ability to allow the 18 | user to create custom |text-objects| or even create |text-objects| on-the-fly 19 | if a object is provided that does not already exist. 20 | 21 | *textobjectify-peculiarities* 22 | The peculiarities that TextObjectify attempts to resolve have to do with the 23 | mutually-exclusive functionality of the parenthesis-like |text-objects| and the 24 | quote-like |text-objects|. The parenthesis-like |text-objects| are all able to 25 | operate on objects that cover multiple lines; however, the cursor must be 26 | on/within the object for it to work. The quote-like |text-objects| only work 27 | on a single line. However, with the quote-like text-objects, the cursor will 28 | seek ahead and jump to a text object if the cursor is not already within one. 29 | 30 | For example, consider a buffer that contains nothing but the following: 31 | 32 | """ 33 | some example code. 34 | section is multiple lines 35 | """ 36 | 37 | if (getinput() == "yes") { 38 | print "this is an example" 39 | counter += 1 40 | } 41 | 42 | If the cursor is on the "p" in "print" and the user types 43 | 44 | vi{ 45 | 46 | The area between the { and } characters will be selected. However, if the 47 | cursor is on the "s" in "some" and the user types 48 | 49 | vi" 50 | 51 | nothing will happen, because quote-like |text-objects| do not work on multiple 52 | lines. With TextObjectify, the two lines between the triple quote sets will be 53 | selected - the parenthesis-style text-object functionality will apply. 54 | 55 | If the cursor is on the "p" in "print" and the user types 56 | 57 | vi" 58 | 59 | the string "this is an example" will be selected, because quote-style 60 | |text-objects| seek forward on the same line when looking for an object. 61 | However, if the cursor is on the "i" in "if" and the user types 62 | 63 | vi( 64 | 65 | nothing will happen, because parenthesis-like |text-objects| do not seek if 66 | they are not in already within a |text-object|. With TextObjectify, the region 67 | between the parenthesis will be selected. 68 | 69 | *textobjectify-custom* 70 | In addition to improving how existing |text-objects| function, TextObjectify 71 | allows user to create custom |text-objects|. To serve as examples, 72 | TextObjectify comes with two custom text objects: "" will operate over the 73 | entire buffer and "V" will operate over a block of viml. With default 74 | TextObjectify, if a user types 75 | 76 | "+ya 77 | 78 | the entire buffer will be stored within the |quoteplus| register. 79 | 80 | Consider a buffer that contains the following: 81 | 82 | if nr2char(getchar()) == g:quitmap 83 | echo "Quitting example" 84 | return 0 85 | endif 86 | 87 | If the cursor is on any of the lines shown and the user enters "viV", the 88 | "echo" and "return" lines will be selected. 89 | 90 | *textobjectify-onthefly* 91 | Moreover, TextObjectify has the ability to create |text-objects| on-the-fly if 92 | an object is requested that is not provided by Vim out of the box and is not 93 | one of TextObjectify's custom text objects. Whatever character is provided 94 | becomes the delimiter for either side. For example, if the buffer contains the 95 | following: 96 | 97 | 98 | LaTeX documents can show colors such as red, blue, green, and yellow. 99 | Additionally, they can show pretty mathematics such as $\int x^2dx$. 100 | 101 | If the user would like to select an area between commas, there is no need to 102 | plan ahead and create such a custom |text-object|. The user can simply enter 103 | "vi," and TextObjectify will create a |text-object| with commas as delimiters 104 | on either side. Similarly, the user could type "ci$" to change the area 105 | between the dollar signs without having to create a custom |text-object| ahead 106 | of time. 107 | 108 | ============================================================================== 109 | 2. Setup *textobjectify-setup* 110 | 111 | TextObjectify can be installed like most other Vim plugins. On a Unixy system 112 | without a plugin manager, the textobjectify.vim file should be located at: 113 | 114 | ~/.vim/plugin/textobjectify.vim 115 | 116 | On a Unixy system with pathogen, the textobjectify.vim file should be located at: 117 | 118 | ~/.vim/bundle/textobjectify/plugin/textobjectify.vim 119 | 120 | On a Windows system without a plugin manager, the textobjectify.vim file should be located at: 121 | 122 | %USERPROFILE%\vimfiles\plugin\textobjectify.vim 123 | 124 | On a Windows system with pathogen, the multicursor.vim file should be located at: 125 | 126 | %USERPROFILE%\vimfiles\bundle\textobjectify\plugin\textobjectify.vim 127 | 128 | If you are using a plugin manager other than pathogen, see its documentation 129 | for how to install TextObjectify - it should be comparable to other plugins. 130 | 131 | If you would like the documentation to also be installed, include textobjectify.txt 132 | into the relevant directory described above, replacing "plugin" with "doc". 133 | 134 | TextObjectify should have same defaults and be useful without any additional 135 | configuration. However, to get the most out of TextObjectify, it is 136 | recommended that you configure it to your own tastes. 137 | 138 | ============================================================================== 139 | 3. Configuration *textobjectify-configuration* 140 | 141 | There are a handful of global variables which can be set to tweak how 142 | TextObjectify operates. The main one is |g:textobjectify| which contains 143 | information on how to treat all custom |text-objects| or modifications of 144 | existing |text-objects|. The others tweak how on-the-fly |text-objects| 145 | function. 146 | 147 | All TextObjectify objects have the following attributes: 148 | 149 | - 'left': Regex for the left delimiter 150 | - 'right': Regex for the right delimiter 151 | - 'same': Set to 1 to have object prioritize same-line objects over multi-line 152 | objects. That is, if the situation is ambiguous, act like quote-like 153 | |text-objects| normally do. Otherwise, set to 0 to have the object act like 154 | parenthesis-like |text-objects| normally do. 155 | - 'seek': Sets whether or not to search for a |text-object| if the cursor is 156 | not already within one. if 'seek' is 0, no seeking is done, i.e., if the 157 | cursor is not already within the |text-object| abort. if 'seek' is 1, search 158 | forward for a |text-object| if the cursor is not already in one. If 'seek' 159 | is 2, search backward for a |text-object| if the cursor is not already in 160 | one. The parenthesis-style |text-objects| come in pairs - TextObjectify 161 | defaults to having the left item of the pairs search forward and the right 162 | item search backwards. 163 | - 'line': If set to '1', it will force the object to act as though it is 164 | selected linewise. The "V" |text-object| which TextObjectify comes with acts 165 | this way. 166 | 167 | 168 | *g:textobjectify* 169 | |g:textobjectify| is a |Dictionary|. Each key is the character which is used 170 | to select the object. The values are |Dictionaries| with the above five 171 | attributes. For example, the default g:textobjectify is: 172 | 173 | let g:textobjectify = { 174 | \'(': {'left': '(', 'right': ')', 'same': 0, 'seek': 1, 'line': 0}, 175 | \')': {'left': '(', 'right': ')', 'same': 0, 'seek': 2, 'line': 0}, 176 | \'{': {'left': '{', 'right': '}', 'same': 0, 'seek': 1, 'line': 0}, 177 | \'}': {'left': '{', 'right': '}', 'same': 0, 'seek': 2, 'line': 0}, 178 | \'[': {'left': '\[', 'right': '\]', 'same': 0, 'seek': 1, 'line': 0}, 179 | \']': {'left': '\[', 'right': '\]', 'same': 0, 'seek': 2, 'line': 0}, 180 | \'<': {'left': '<', 'right': '>', 'same': 0, 'seek': 1, 'line': 0}, 181 | \'>': {'left': '<', 'right': '>', 'same': 0, 'seek': 2, 'line': 0}, 182 | \'"': {'left': '"', 'right': '"', 'same': 1, 'seek': 1, 'line': 0}, 183 | \"'": {'left': "'", 'right': "'", 'same': 1, 'seek': 1, 'line': 0}, 184 | \'`': {'left': '`', 'right': '`', 'same': 1, 'seek': 1, 'line': 0}, 185 | \'V': {'left': '^\s*\(if\|for\|function\|try\|while\)\>', 186 | \'right': '^\s*end', 'same': 0, 'seek': 1, 'line': 1}, 187 | \"\": {'left': '\%^', 'right': '\%$', 'same': 0, 'seek': 0, 188 | \'line': 0}, 189 | \} 190 | 191 | This sets how the parenthesis-like |text-objects|, the quote-like 192 | |text-objects|, and the two new |text-objects| operate. This is a good 193 | reference if you want to make your own. 194 | 195 | To create modify or create a new |text-object|, copy the above lines into your 196 | |vimrc| and adjust accordingly. Note that if you create a |g:textobjectify| in 197 | your |vimrc| it overwrites all of TextObjectify's default values. Thus if you 198 | create an empty |g:textobjectify| all objects default will act as they would 199 | without TextObjectify installed - quote-like |text-objects| will not act on 200 | multiple lines, etc. 201 | 202 | *g:textobjectify_onthefly* 203 | By default, TextObjectify will create new |text-objects| on-the-fly if a 204 | mapping is called that does not correspond to an existing |text-object| (either 205 | one of Vim's defaults or one provided by TextObjectify). To disable this, set 206 | |g:textobjectify_onthefly| to 0. 207 | 208 | *g:textobjectify_onthefly_same* 209 | *g:textobjectify_onthefly_seek* 210 | *g:textobjectify_onthefly_line* 211 | The 'same', 'seek' and 'line' attributes of on-the-fly |text-objects| can be 212 | customized by setting the |g:textobjectify_onthefly_same|, 213 | |g:textobjectify_onthefly_seek|, and |g:textobjectify_onthefly_line| variables 214 | to the desired value. Otherwise, they default to 0, 1, and 0, respectively. 215 | 216 | ============================================================================== 217 | 4. Changelog *textobjectify-changelog* 218 | 219 | 0.1 (2013-04-06): 220 | - initial release 221 | 222 | ============================================================================== 223 | vim:tw=78:ts=8:ft=help:norl: 224 | -------------------------------------------------------------------------------- /plugin/textobjectify.vim: -------------------------------------------------------------------------------- 1 | " Vim plugin to improve text objects 2 | " Maintainer: Daniel Thau (paradigm@bedrocklinux.org) 3 | " Version: 0.1 4 | " Description: TextObjectify is a Vim plugin which improves text objects 5 | " Last Change: 2013-04-04 6 | " Location: plugin/textobjectify.vim 7 | " Website: https://github.com/paradigm/textobjectfy 8 | " 9 | " See textobjectify.txt for documentation. 10 | 11 | if exists('g:loaded_textobjectify') || &cp 12 | finish 13 | endif 14 | let g:loaded_textobjectify = 1 15 | 16 | " if the user did not configure textobjectify, use the following as a default 17 | if !exists("g:textobjectify") 18 | let g:textobjectify = { 19 | \'(': {'left': '(', 'right': ')', 'same': 0, 'seek': 1, 'line': 0}, 20 | \')': {'left': '(', 'right': ')', 'same': 0, 'seek': 2, 'line': 0}, 21 | \'{': {'left': '{', 'right': '}', 'same': 0, 'seek': 1, 'line': 0}, 22 | \'}': {'left': '{', 'right': '}', 'same': 0, 'seek': 2, 'line': 0}, 23 | \'[': {'left': '\[', 'right': '\]', 'same': 0, 'seek': 1, 'line': 0}, 24 | \']': {'left': '\[', 'right': '\]', 'same': 0, 'seek': 2, 'line': 0}, 25 | \'<': {'left': '<', 'right': '>', 'same': 0, 'seek': 1, 'line': 0}, 26 | \'>': {'left': '<', 'right': '>', 'same': 0, 'seek': 2, 'line': 0}, 27 | \'"': {'left': '"', 'right': '"', 'same': 1, 'seek': 1, 'line': 0}, 28 | \"'": {'left': "'", 'right': "'", 'same': 1, 'seek': 1, 'line': 0}, 29 | \'`': {'left': '`', 'right': '`', 'same': 1, 'seek': 1, 'line': 0}, 30 | \'V': {'left': '^\s*\(if\|for\|function\|try\|while\)\>', 31 | \'right': '^\s*end', 'same': 0, 'seek': 1, 'line': 1}, 32 | \"\": {'left': '\%^', 'right': '\%$', 'same': 0, 'seek': 0, 33 | \'line': 0}, 34 | \} 35 | endif 36 | 37 | " default text objects 38 | let s:defaults = ["w","W","s","p","[","]","(",")","b","<",">","t","}","{","B",'"',"'"] 39 | 40 | " mappings to call plugin rather than vim text objects 41 | onoremap i :call TextObjectify(v:operator,'i') 42 | onoremap a :call TextObjectify(v:operator,'a') 43 | xnoremap i :call TextObjectify(visualmode(),'i') 44 | xnoremap a :call TextObjectify(visualmode(),'a') 45 | 46 | 47 | function! TextObjectify(mode,ia) 48 | " general layout of this function: 49 | " - make arguments available script-scoped so I don't have to repeatedly 50 | " pass arguments along 51 | " - get the object from the user 52 | " - figure out if we should treat the object as a textobjecitfy-specific 53 | " item, a vim default, or use on-the-fly settings (or abort) 54 | " - store original cursor position so we can reset 55 | " - possibly move cursor if already visually selected region so user can 56 | " re-select a larger region 57 | " - set seek-directions-specific items. if seek is disabled, just check 58 | " if we're currently in a text object and either select or abort 59 | " - if same-line setting is set, search for object on same line as cursor. 60 | " if we find it, select and quit. otherwise, continue 61 | " - search for object irrelevant of line. if we find it, select and quit. 62 | " otherwise, abort 63 | 64 | " make arguments available script-scoped 65 | let s:ia = a:ia 66 | let s:mode = a:mode 67 | 68 | " get object 69 | let l:object = nr2char(getchar()) 70 | 71 | " figure out how to treat object. four possible situations. use the 72 | " first we run into. 73 | " - object is in g:textobjectify 74 | " - object is vim default 75 | " - g:textobjectify_onlythefly is set, then 76 | " - use the object as delimiters 77 | " - abort 78 | 79 | " object is in g:textobjectify 80 | if has_key(g:textobjectify, l:object) 81 | let s:left = g:textobjectify[l:object]['left'] 82 | let s:right = g:textobjectify[l:object]['right'] 83 | let s:same = g:textobjectify[l:object]['same'] 84 | let s:seek = g:textobjectify[l:object]['seek'] 85 | if g:textobjectify[l:object]['line'] == 1 86 | let s:mode = "V" 87 | endif 88 | elseif count(s:defaults, l:object) > 0 89 | " object is vim default - no need for any more plugin 90 | execute "normal! v".a:ia. l:object 91 | return 92 | elseif !exists("g:textobjectify_onthefly") || g:textobjectify_onthefly == 1 93 | " create object on-the-fly 94 | let s:left = '\V'.l:object 95 | let s:right = '\V'.l:object 96 | " use onthefly settings if set; otherwise, try sane defaults 97 | if exists("g:textobjectify_onthefly_same") 98 | let s:same = g:textobjectify_onthefly_same 99 | else 100 | let s:same = 0 101 | endif 102 | if exists("g:textobjectify_onthefly_seek") 103 | let s:seek = g:textobjectify_onthefly_seek 104 | else 105 | let s:seek = 1 106 | endif 107 | if exists("g:textobjectify_onthefly_line") 108 | if g:textobjectify_onthefly_line == 1 109 | let s:mode = "V" 110 | endif 111 | endif 112 | else 113 | return 114 | endif 115 | 116 | " store the original cursor position either to reference or simply so we 117 | " can restore it if we don't find any text objects to use 118 | let s:origline = line('.') 119 | let s:origcol = col('.') 120 | " we may tweak s:orig* below. save the real original values in case we 121 | " do. 122 | let s:realorigline = line('.') 123 | let s:realorigcol = col('.') 124 | 125 | " note whether or not a region is already visually selected 126 | if (s:mode ==# "v" || s:mode ==# "V" || s:mode ==# "\") && 127 | \(col("'<") != col("'>") || line("'<") != line("'>")) 128 | let s:invisual = 1 129 | " if there is one and 'i' is used, expand selected area so we can 130 | " re-select a larger area 131 | if s:ia == 'i' 132 | if s:seek == 2 133 | if col(".") < col("$")-1 134 | execute "normal! \" 135 | else 136 | execute "normal! \$" 137 | endif 138 | else 139 | " if seeking backward, expand backward 140 | if col(".") > col("^")+1 141 | execute "normal! \" 142 | else 143 | execute "normal! \0" 144 | endif 145 | endif 146 | let s:origline = line('.') 147 | let s:origcol = col('.') 148 | endif 149 | else 150 | let s:invisual = 0 151 | endif 152 | 153 | " set s:seekdir to use as flags for search() and friends if we're seeking 154 | if(s:seek == 1) " seek forward 155 | let s:seekdir = '' 156 | elseif(s:seek == 2) " seek backward 157 | let s:seekdir = 'b' 158 | else 159 | " otherwise, do not seek. just check if we're in the region. if not, 160 | " abort 161 | if s:CursorInRange() == 1 162 | return s:SelectRange() 163 | else 164 | " abort 165 | call cursor(s:realorigline,s:realorigcol) 166 | " if was in visual, re-select area 167 | if s:invisual == 1 168 | normal gv 169 | endif 170 | return 0 171 | endif 172 | endif 173 | 174 | " if s:same is set, search on same line. will select range if it finds 175 | " something 176 | if s:same == 1 && s:SearchSameLine() == 1 177 | return 1 178 | endif 179 | 180 | " if s:same is not set, or s:same is set but didn't find anything on same 181 | " line, search without line constraint. will select range if it finds 182 | " something 183 | call cursor(s:origline,s:origcol) 184 | if s:SearchAnyLine() 185 | return 1 186 | else 187 | " did not find any text objects. really reset cursor and abort 188 | call cursor(s:realorigline,s:realorigcol) 189 | " if was in visual, re-select area 190 | if s:invisual == 1 191 | normal gv 192 | endif 193 | return 0 194 | endif 195 | endfunction 196 | 197 | "" returns whether or not cursor is in range 198 | "" if cursor is in range, also sets s:variables of bound locations 199 | function! s:CursorInRange() 200 | " find left/right bounds, if they exist. use a single searchpairpos() if 201 | " left/right bounds are different; otherwise, use searchpos() in both 202 | " directions. 203 | 204 | " we have to handle the situation where the cursor is on the left 205 | " delimiter OR the right delimiter, but we can't check just allow the 206 | " cursor to be under both delimiter at the same time or it will accept 207 | " just a single character as a "range". instead, check if we're in a 208 | " range where only the left bound is allowed under the cursor, and if that 209 | " fails, check if we're in a range where the right bound is allowed under 210 | " the cursor. 211 | 212 | if(s:left != s:right) 213 | let s:l1 = searchpairpos(s:left,'',s:right,'Wncb')[0] 214 | let s:c1 = searchpairpos(s:left,'',s:right,'Wncb')[1] 215 | let s:l2 = searchpairpos(s:left,'',s:right,'Wn')[0] 216 | let s:c2 = searchpairpos(s:left,'',s:right,'Wn')[1] 217 | else 218 | let s:l1 = searchpos(s:left, 'Wncb')[0] 219 | let s:c1 = searchpos(s:left, 'Wncb')[1] 220 | let s:l2 = searchpos(s:right,'Wn')[0] 221 | let s:c2 = searchpos(s:right,'Wn')[1] 222 | endif 223 | 224 | " if none of the bounds are set to 0, the cursor is in range 225 | if(s:l1 != 0 && s:c1 != 0 && s:l2 != 0 && s:c2 != 0) 226 | return 1 227 | endif 228 | 229 | " check if it is a valid range when cursor is under other delimiter 230 | if(s:left != s:right) 231 | let s:l1 = searchpairpos(s:left,'',s:right,'Wnb')[0] 232 | let s:c1 = searchpairpos(s:left,'',s:right,'Wnb')[1] 233 | let s:l2 = searchpairpos(s:left,'',s:right,'Wnc')[0] 234 | let s:c2 = searchpairpos(s:left,'',s:right,'Wnc')[1] 235 | else 236 | let s:l1 = searchpos(s:left, 'Wnb')[0] 237 | let s:c1 = searchpos(s:left, 'Wnb')[1] 238 | let s:l2 = searchpos(s:right,'Wnc')[0] 239 | let s:c2 = searchpos(s:right,'Wnc')[1] 240 | endif 241 | 242 | " if none of the bounds are set to 0, the cursor is in range (and under 243 | " right delimiter), otherwise, not in range. 244 | if(s:l1 != 0 && s:c1 != 0 && s:l2 != 0 && s:c2 != 0) 245 | return 1 246 | else 247 | return 0 248 | endif 249 | endfunction 250 | 251 | " selects the range so the operator will apply to it 252 | function! s:SelectRange() 253 | " modify range if 'i' was used instead of 'a' 254 | if(s:ia == 'i') 255 | if s:mode ==# "V" 256 | " visual-line mode 257 | " bring bounds in one line each 258 | let s:l1+=1 259 | let s:l2-=1 260 | else 261 | " move upper/left bound in one 262 | if(len(getline(s:l1)) > s:c1) 263 | let s:c1+=1 264 | else 265 | let s:l1+=1 266 | let s:c1=1 267 | endif 268 | " move lower/right bound in one 269 | if(s:c2 > 1) 270 | let s:c2-=1 271 | else 272 | let s:l2-=1 273 | let s:c2=len(getline(s:l2)) 274 | endif 275 | endif 276 | endif 277 | 278 | " using functions like cursor() to set cursor acts funny in this context. 279 | " Normal mode commands work fine. I'd like to use |, but this uses 280 | " virtual-column rather than byte-wise columns. Change c1/c2 to virtual 281 | " columns 282 | call cursor(s:l1,s:c1) 283 | let s:c1 = virtcol('.') 284 | call cursor(s:l2,s:c2) 285 | let s:c2 = virtcol('.') 286 | 287 | " select range 288 | if s:mode == "v" || s:mode == "V" || s:mode == "\" 289 | " if already in a visual mode, use that same mode 290 | execute 'normal! '.s:l1.'G'.s:c1.'|'.s:mode.s:l2.'G'.s:c2.'|' 291 | else 292 | " visually select range - operator will apply to this range 293 | execute 'normal! '.s:l1.'G'.s:c1.'|v'.s:l2.'G'.s:c2.'|' 294 | endif 295 | return 1 296 | endfunction 297 | 298 | " searches for a text object range on the same line as the cursor currently 299 | " is 300 | function! s:SearchSameLine() 301 | while 1 302 | " check if cursor is already within a range that is on the same line 303 | if s:CursorInRange() && s:l1 == s:l2 304 | return s:SelectRange() 305 | endif 306 | " check if we've exhausted the search range. if so, abort. 307 | " otherwise, move cursor and try again next loop 308 | if s:seek == 1 309 | " seek forward 310 | if col(".") >= col("$")-1 311 | return 0 312 | endif 313 | execute "normal! \" 314 | elseif s:seek == 2 315 | " seek backward 316 | if col(".") <= col("^")+1 317 | return 0 318 | endif 319 | execute "normal! \" 320 | else 321 | " don't seek 322 | return 0 323 | endif 324 | endwhile 325 | endfunction 326 | 327 | " searches for the text object in s:seek direction 328 | function! s:SearchAnyLine() 329 | while 1 330 | if s:CursorInRange() 331 | " we found a range - select it and quit 332 | return s:SelectRange() 333 | elseif search(s:left.'\|'.s:right,'W'.s:seekdir) == 0 334 | " we could not find any range - abort 335 | return 0 336 | endif 337 | endwhile 338 | endfunction 339 | 340 | " vim: noet sw=8 sts=8 341 | --------------------------------------------------------------------------------