├── doc └── express.txt ├── plugin └── express.vim └── README.md /doc/express.txt: -------------------------------------------------------------------------------- 1 | *express.vim* An operator to change by an expression 2 | 3 | Author: Tom McDonald 4 | License: Same terms as Vim itself (see |license|) 5 | 6 | This plugin is only available if 'compatible' is not set. 7 | 8 | INTRODUCTION *express* 9 | 10 | express.vim defines an operator, `g=`, that allows you to change text 11 | according to a VimL |expression|. Once invoked, an expression will be 12 | prompted. In the expression, `v:val` will represent the text being operated 13 | on (similar to |map()|). 14 | 15 | COMMANDS *express-commands* 16 | 17 | *express-:MapExpress* 18 | :MapExpress {op} {expr} 19 | Create an operator which applies the VimL expression 20 | {expr} over the given text, and define |Normal| mode 21 | and |Visual| mode mappings using {op} as the left hand 22 | side of the mapping. 23 | See |eval.txt| for help on VimL expressions. 24 | 25 | *express-:MapSubpress* 26 | :MapSubpress {op} /{pattern}/{string}/[flags] 27 | Create an operator which substitutes replaces 28 | {pattern} with {string} over the given text, and 29 | define |Normal| mode and |Visual| mode mappings using 30 | {op} as the left hand side of the mapping. 31 | See |:substitute| for help on substitutions. 32 | 33 | MAPPINGS *express-mappings* 34 | 35 | *g=* 36 | g={motion} Replace the text defined by {motion} with the value of 37 | an expression. The expression is entered at the 38 | command-line (with an '=' prompt). The original text 39 | will populate the value of `v:val` within the 40 | expression. If expression is just a function name 41 | (with no parentheses), that function will be called 42 | using `v:val` as its argument. If the expression 43 | begins with '!', it will be treated as an external 44 | command, and passed to the |system()| function. To 45 | use an expression beginning with logical not 46 | (|expr-!|), include a space before the '!' character. 47 | 48 | *g==* 49 | g== Like |g=|, but for the current line. |linewise| 50 | 51 | *g:* 52 | g:{motion} Like |g=|, but input `pattern/replace/flags` similar to 53 | |:substitute|. Individual lines are filtered through 54 | the |substitute()| function, as if invoked by |g=| 55 | using 'substitute(v:val, ...)'. 56 | NOTE: Despite looking like a |:substitute| command, 57 | this is really a call to |substitute()|, so some 58 | behaviour may differ. 59 | 60 | *g::* 61 | g:: Like |g:|, but for the current line. |linewise| 62 | 63 | EXAMPLES *express-examples* 64 | 65 | Consider the following text: 66 | 67 | Hi, my name is "john jones" 68 | 69 | With your cursor inside the quoted string, type `g=i"` and then input the 70 | following expression on the prompt: 71 | 72 | substitute(v:val, '\<.', '\U\0', 'g') 73 | 74 | The resulting text will be: 75 | 76 | Hi, my name is "John Jones" 77 | 78 | SETTINGS *express-settings* 79 | 80 | To change the default mappings, simply provide your own mappings to the 81 | commands below. The default mappings are as follows: 82 | 83 | nmap g= (Express) 84 | xmap g= (Express) 85 | nmap g== (ExpressLine) 86 | 87 | For example, to change the Normal-mode mapping to `c=`, use the following: 88 | 89 | nmap c= (Express) 90 | 91 | This will use the mapping `c=`, and the default `g=` will not be mapped. 92 | 93 | *g:express_no_mappings* 94 | g:express_no_mappings ~ 95 | 96 | If this variable is defined, the default mappings will not be created. 97 | 98 | ISSUES AND TODO *express-issues* *express-todo* 99 | 100 | See https://github.com/tommcdo/vim-express/issues for bugs and issues. 101 | 102 | vim:tw=78:ts=8:ft=help:norl: 103 | -------------------------------------------------------------------------------- /plugin/express.vim: -------------------------------------------------------------------------------- 1 | function! s:express_base(expression, type, vis) 2 | let expression = a:expression 3 | if expression == '' 4 | return 5 | endif 6 | if expression =~? '^\([gswbv]:\)\?[a-z][a-z0-9#:_]\+$' 7 | let expression = expression.'(v:val)' 8 | elseif expression =~? '^!' 9 | let expression = 'system("'.escape(expression[1:], '"\').'", v:val)' 10 | endif 11 | let [value, regtype] = s:get(a:type, a:vis) 12 | call s:set(map([value], expression)[0], regtype) 13 | endfunction 14 | 15 | function! s:express(type, ...) 16 | call s:custom_setup(-1) 17 | let expression = input('=', '', 'expression') 18 | call s:express_base(expression, a:type, a:0) 19 | call s:repeat(expression."\".s:get_capture()) 20 | endfunction 21 | 22 | function! s:express_custom(type, ...) 23 | call s:express_base(s:express_custom[s:express_index], a:type, a:0) 24 | call s:repeat(s:get_capture()) 25 | endfunction 26 | 27 | function! s:subpress_base(input, type, vis) 28 | let input = a:input 29 | if input == '' 30 | return 31 | endif 32 | let args = split(input[1:], '\\\@".input."\".s:get_capture()) 46 | endfunction 47 | 48 | function! s:subpress_custom(type, ...) 49 | call s:subpress_base(s:express_custom[s:express_index], a:type, a:0) 50 | call s:repeat(s:get_capture()) 51 | endfunction 52 | 53 | function! express#capture(input, ...) 54 | let suffix = join(a:000, '') 55 | let s:express_capture .= a:input.suffix 56 | let s:express_captures += [a:input] 57 | return a:input 58 | endfunction 59 | 60 | function! express#recall(index) 61 | return get(s:express_captures, a:index, '') 62 | endfunction 63 | 64 | function! s:get_capture() 65 | return s:express_capture 66 | endfunction 67 | 68 | function! s:custom_setup(index) 69 | let s:express_capture = '' 70 | let s:express_index = a:index 71 | let s:express_captures = [''] 72 | endfunction 73 | 74 | function! s:custom_maps(op, mapping) 75 | let words = split(a:mapping) 76 | let lhs = words[0] 77 | let rhs = join(words[1:]) 78 | if !exists('s:express_custom') 79 | let s:express_custom = [] 80 | endif 81 | let s:express_custom += [rhs] 82 | execute 'nnoremap ' lhs ':call custom_setup('.(len(s:express_custom) - 1).'):set operatorfunc='.a:op.'_customg@' 83 | execute 'xnoremap ' lhs ':call custom_setup('.(len(s:express_custom) - 1).'):call '.a:op.'_custom(visualmode(), 1)g@' 84 | endfunction 85 | 86 | function! s:get(type, vis) 87 | let a_reg = s:getreg('a') 88 | let selection = &selection 89 | 90 | set selection=inclusive 91 | let selectcmd = "`[v`]" 92 | if a:vis 93 | if a:type ==# 'v' 94 | let selectcmd = "`" 95 | elseif a:type ==# 'V' 96 | let selectcmd = "'" 97 | elseif a:type ==# "\" 98 | let selectcmd = "`<\`>" 99 | endif 100 | else 101 | if a:type == 'line' 102 | let selectcmd = "'[V']" 103 | endif 104 | endif 105 | execute 'normal!'.selectcmd.'"ay' 106 | let value = s:getreg('a') 107 | let &selection = selection 108 | 109 | call s:setreg('a', a_reg) 110 | return value 111 | endfunction 112 | 113 | function! s:set(value, regtype) 114 | let a_reg = s:getreg('a') 115 | let selection = &selection 116 | 117 | set selection=inclusive 118 | call s:setreg('a', [a:value, a:regtype]) 119 | execute 'normal! gv"ap' 120 | 121 | let &selection = selection 122 | call s:setreg('a', a_reg) 123 | endfunction 124 | 125 | function! s:getreg(regname) 126 | return [getreg(a:regname), getregtype(a:regname)] 127 | endfunction 128 | 129 | function! s:setreg(regname, value) 130 | call setreg(a:regname, a:value[0], a:value[1]) 131 | endfunction 132 | 133 | function! s:repeat(input) 134 | silent! call repeat#set("\(ExpressRepeat)".a:input) 135 | endfunction 136 | 137 | function! s:create_map(mode, lhs, rhs) 138 | if !hasmapto(a:rhs, a:mode) 139 | execute a:mode.'map' a:lhs a:rhs 140 | endif 141 | endfunction 142 | 143 | nnoremap (ExpressRepeat) . 144 | 145 | nnoremap (Express) :set operatorfunc=expressg@ 146 | nnoremap (ExpressLine) :set operatorfunc=expressg@_ 147 | vnoremap (Express) :call express(visualmode(), 1) 148 | 149 | nnoremap (Subpress) :set operatorfunc=subpressg@ 150 | nnoremap (SubpressLine) :set operatorfunc=subpressg@_ 151 | vnoremap (Subpress) :call subpress(visualmode(), 1) 152 | 153 | command! -nargs=+ MapExpress call s:custom_maps('express', ) 154 | command! -nargs=+ MapSubpress call s:custom_maps('subpress', ) 155 | 156 | if exists('g:express_no_mappings') 157 | finish 158 | endif 159 | 160 | call s:create_map('n', 'g=', '(Express)') 161 | call s:create_map('n', 'g==', '(ExpressLine)') 162 | call s:create_map('x', 'g=', '(Express)') 163 | 164 | call s:create_map('n', 'g:', '(Subpress)') 165 | call s:create_map('n', 'g::', '(SubpressLine)') 166 | call s:create_map('x', 'g:', '(Subpress)') 167 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | express.vim 2 | =========== 3 | 4 | Express yourself with custom operators. Define your own operators that apply 5 | either a VimL expression or a substitution to any motion or text object. 6 | 7 | Custom operators 8 | ---------------- 9 | 10 | ### Express 11 | 12 | Expression opertors are created using the `:MapExpress` command. The expression 13 | makes use of the `v:val` placeholder, which contains the text covered by the 14 | given motion, and returns a string. 15 | 16 | For example, to create an operator `cd` that surrounds a motion in C-style 17 | comment delimiters, you can use the following command: 18 | 19 | :MapExpress cd '/* ' . v:val . ' */' 20 | 21 | Now you can use the new operator `cd` on any motion or text object, such as 22 | `cdiw` to comment out a word, or `cdi(` to comment out everything inside 23 | parentheses. 24 | 25 | ### Subpress 26 | 27 | Substitution operators are created using the `:MapSubpress` command. The 28 | substitution takes a form much like that of the `:substitute` command. It 29 | contains a search pattern, a replacement, and flags, each surrounded by some 30 | delimiter. 31 | 32 | As an example, to create an operator `yc` that capitalizes each word of a 33 | motion, you can use the following command: 34 | 35 | :MapSubpress yc /\<\w/\u\0/g 36 | 37 | Now you can use the `yc` operator on any motion or text object, such as `yc)` 38 | to capitalize from the cursor to the beginning of the next sentence, or `ycap` 39 | to capitalize every word in the paragraph. 40 | 41 | ### NOTE 42 | 43 | At the time of loading your `.vimrc` file, the commands `:MapExpress` and 44 | `:MapSubpress` will likely not have been defined yet. To create operators using 45 | these commands in your `.vimrc`, you can use the `VimEnter` event, for example: 46 | 47 | autocmd VimEnter * MapExpress cd '/*' . v:val . ' */' 48 | 49 | Ad-hoc operators 50 | ---------------- 51 | 52 | Sometimes you just want to do a one-off (but repeatable) operation using a VimL 53 | expression or substitution. The `g=` and `g:` operators will let you do just 54 | that. 55 | 56 | ### `g={motion}` 57 | 58 | Replace the text defined by {motion} with the value of an expression. The 59 | expression is entered at the command-line after a `=` prompt. The original text 60 | will populate the value of `v:val` within the expression. 61 | 62 | ### `g:{motion}` 63 | 64 | Filter the text defined by {motion} through a `:substitute`-like command. This 65 | is basically the same as using `g=` and entering `substitute(v:val, ...)`, but 66 | it's a bit easier (and more familiar) to type. A substitution is entered at the 67 | command-line after a `:s/` prompt. The `/` character in the prompt can be 68 | deleted and replaced with a different delimiter. 69 | 70 | ### Examples 71 | 72 | Here are some examples of using the `g=` and `g:` operators. In the examples, 73 | the prompt is included in the Expression column. For `g=` operations, the 74 | prompt is `=`; for `g:` operations, the prompt is `:s/`. The complete operation 75 | is performed by typing the Operator + Motion followed by the Expression 76 | (without the prompt), and then pressing `Enter`. 77 | 78 | Note that both operators are repeatable with `.` when [repeat.vim][1] is 79 | installed. 80 | 81 | Description | Operator + Motion | Expression | Before | After 82 | --- | --- | --- | --- | --- 83 | Change member names to getters (snake case) | `g=iw` | `='get_'.v:val.'()'` | `foo_bar` | `get_foo_bar()` 84 | Change member names to getters (camel case) | `g:iw` | `:s/.*/get\u\0()/` | `fooBar` | `getFooBar()` 85 | Sort elements of an array literal | `g=i[` | `=join(sort(split(v:val, ', ')), ', ')` | `[foo, bar, baz]` | `[bar, baz, foo]` 86 | Clean up whitespace around binary operators | `g::` (line) | `:s/\s*\([=+*\/-]\)\s*/ \1 /g` | `int x=foo + bar *baz` | `int x = foo + bar * baz` 87 | Comment out a block of code | `g=ip` | `='/* '.v:val.' */'` | `int x = 400;`
`int y = 5;` | `/* int x = 400;`
`int y = 5; */` 88 | 89 | Operators taking user input 90 | --------------------------- 91 | 92 | Some operators take input from the user to perform a change. Without some etra 93 | setup, repeating the operaton with `.` would prompt the user for input again. 94 | To help avoid this, express.vim offers an interface to use in operator 95 | definitions that hook into [repeat.vim][1]. 96 | 97 | ### Capturing user input 98 | 99 | To make user input automatically inserted upon repeat (using `.`), enclose the 100 | part of code that prompts for input in `express#capture()`. 101 | 102 | For example, if defining an operator to replace all characters with a character 103 | input by the user, you can capture the use input as follows: 104 | 105 | :MapSubpress cr /./\=express#capture(nr2char(getchar()))/g 106 | 107 | Some methods of user input require additional keystokes such as pressing Enter 108 | to terminate the input sequence. To accomodate for this, additional characters 109 | can be provided as a second argument to `express#capture()`. For example, 110 | appending a user-input string to the end of the motion: 111 | 112 | :MapExpress dc v:val . express#capture(input('Suffix: '), "\") 113 | 114 | Here, `"\"` is the carriage return typed after inputting a string with 115 | `input()`. The result of the function does not include the carriage return, but 116 | the user must type it to terminate input. 117 | 118 | ### Recalling captured input 119 | 120 | If you want a value taken from a single input prompt to be used in multiple 121 | places in your operator's expression, you can recall any portion captured by 122 | `express#capture()` using `express#recall(n)`, where `n` is the position of the 123 | call to `express#capture()` in the expression (similar to backreferences in 124 | regex). 125 | 126 | For example, to enclose a motion in an input string, we can do something like this: 127 | 128 | :MapExpress yp express#capture(input('Enclosing: '), "\") . v:val . express#recall(1) 129 | 130 | [1]: https://github.com/tpope/vim-repeat 131 | --------------------------------------------------------------------------------