├── README.md └── text-objects.kak /README.md: -------------------------------------------------------------------------------- 1 | # kakoune-text-objects 2 | 3 | [kakoune](http://kakoune.org) plugin providing extra text-objects 4 | 5 | ## Install 6 | 7 | Add `text-objects.kak` to your autoload dir: `~/.config/kak/autoload/`. 8 | 9 | Or via [plug.kak](https://github.com/andreyorst/plug.kak): 10 | 11 | ``` 12 | plug 'delapouite/kakoune-text-objects' 13 | ``` 14 | 15 | ## Why? 16 | 17 | How many modes are there in Kakoune? 8? 18 | `normal`, `insert`, `menu`, `prompt`, `goto`, `view`, `object` and `user`? 19 | 20 | Unfortunately *on-key* modes like `object` are in fact divided in *sub-modes*: 21 | `q`, `q` or `[q` (to name only a few of the possibilities) all 22 | offer a slightly different behavior. 23 | 24 | The problem occurs when you declare a new mapping like: 25 | 26 | ``` 27 | map global object f foo 28 | ``` 29 | 30 | There's currently no native way to differentiate between an object triggered by 31 | `f` or `f`. 32 | 33 | This plugin attempts to address this issue. 34 | 35 | ## Usage 36 | 37 | ### pairs 38 | 39 | This plugin enhances the *pairs* text-objects (`(`, `)`, `{`, `}`…). 40 | 41 | Let's illustrate with the `braces` text-object. 42 | If the cursor is in a C function simple body and press `{` it will select it. 43 | But what if the cursor is in the main scope, just few lines below the function? 44 | Normally, the `{` would result in a noop and nothing would be selected. 45 | 46 | With the enhanced behavior provided `{` will attempt to find the **previous** 47 | `{…}` in the code and select the inside of it. `{` would do the same but select 48 | the braces as well. With `}` it will search the **next** couple. 49 | 50 | See https://github.com/mawww/kakoune/issues/9 51 | 52 | ### line 53 | 54 | By default Kakoune does not provide a real *line* text-object. This *line* concept is 55 | scattered around different keys like `x`, ``, ``, `Gh`, `Gi`… 56 | 57 | This plugins tries to reunite them under the `x` text-object. 58 | For example `x` selects the *whole* line (EOL included), while `x`, only select 59 | *inside* the line (not leading spaces and EOL excluded). 60 | 61 | You may find this text-object redundant or even useless, but I found it nice to reinforce 62 | Kakoune's *orthogonality*. 63 | 64 | ### buffer 65 | 66 | By default Kakoune does not provide a real *buffer* text-object. This *buffer* concept is 67 | scattered around different keys like `%`, `Gj`, `Gk`, `Ge`… 68 | 69 | This plugins tries to reunite them under the `f` text-object. 70 | For example `f` selects the *whole* buffer, while `f`, only select 71 | *inside* the buffer (not leading or trailing spaces). 72 | 73 | You may find this text-object redundant or even useless, but I found it nice to reinforce 74 | Kakoune's *orthogonality*. 75 | 76 | ### vertical selection 77 | 78 | Depends on [kakoune-vertical-selection](https://github.com/occivink/kakoune-vertical-selection). 79 | 80 | It's a great companion to the builtin `C` key. `v` selects up and down, `[v` selects up 81 | and `]v` selects down. 82 | 83 | ### XML tags 84 | 85 | There's currently a `t` text-object but it's very much work in progress at this stage. 86 | Use it with caution. 87 | 88 | ### `selectors` mode 89 | 90 | A `selectors` user-mode is provided. 91 | 92 | Its goal is to avoid using the `alt` key as much as possible and the related acrobatic fingers chords over the keyboard. 93 | Moreover it does not really make sense to waste a modifier key for a one shot sequence (a.k.a. dead-key behavior). 94 | 95 | There's no default key to access this user-mode, but I personally map it to the `s` key: 96 | 97 | ``` 98 | map global normal s ': enter-user-mode selectors' -docstring 'selectors' 99 | ``` 100 | 101 | On a QWERTY layout, the `s` key is under the left hand. 102 | All the keys mapped to this user-mode, like `i`, `o`, `j`… are all under the right hand. 103 | 104 | I find this "left then right" hands sequence quite ergonomic. 105 | 106 | The mnemonic logic is defined like this: 107 | 108 | - `i` → inside, `o` → outside (around). Bonus, on a QWERTY layout there are next to each other. 109 | - `h` → outside left, `l` → outside right 110 | - `j` → inside left, `k` → inside right. Usually, `j` and `k` are for up and down moves. 111 | But again, on a QWERTY keyboard, `j` and `k` are located between the `h` and `l` pair ("inside" this pair) 112 | 113 | As usual, upper keys are used to extend the selection. 114 | 115 | Examples: 116 | 117 | - to extend the selections until the next square bracket. Before: ` ]` After: `s K ]` 118 | - to select around words. Before: ` w` After: `s o w`. 119 | 120 | Note: You are guided by auto-info boxes along the way. 121 | 122 | "But, by doing so, you're sacrificing the regular behavior of `s` in the normal mode!" 123 | 124 | That's correct, but I decided to map it back to `s s`, which is easy to type: 125 | 126 | ``` 127 | map global selectors s s -docstring 'sub-selection' 128 | ``` 129 | 130 | ## See Also 131 | 132 | - [kakoune-mirror](https://github.com/Delapouite/kakoune-mirror) 133 | - [kakoune-select-view](https://github.com/Delapouite/kakoune-select-view) 134 | - [kakoune-vertical-selection](https://github.com/occivink/kakoune-vertical-selection) 135 | - [objectify.kak](https://github.com/alexherbo2/objectify.kak) 136 | 137 | ## Licence 138 | 139 | Thanks a lot to [occivink](https://github.com/occivink) and 140 | [alex](https://github.com/alexherbo2) for inspiration. 141 | 142 | MIT 143 | -------------------------------------------------------------------------------- /text-objects.kak: -------------------------------------------------------------------------------- 1 | # extended behaviors for pairs 2 | map global object '(' ': text-object-block (' -docstring 'prev parenthesis block' 3 | map global object ')' ': text-object-block )' -docstring 'next parenthesis block' 4 | map global object '{' ': text-object-block {' -docstring 'prev braces block' 5 | map global object '}' ': text-object-block }' -docstring 'next braces block' 6 | map global object '[' ': text-object-block [' -docstring 'prev brackets block' 7 | map global object ']' ': text-object-block ]' -docstring 'next brackets block' 8 | map global object '' ': text-object-block ' -docstring 'prev angle block' 9 | map global object '' ': text-object-block ' -docstring 'next angle block' 10 | # additional text objects 11 | map global object 'x' ': text-object-line' -docstring 'line' 12 | map global object 't' ': text-object-tag' -docstring 'tag' 13 | map global object 'f' ': text-object-buffer' -docstring 'buffer' 14 | map global object '' ': text-object-indented-paragraph' -docstring 'indented paragraph' 15 | # depends on occivink/kakoune-vertical-selection 16 | map global object 'v' ': text-object-vertical' -docstring 'vertical selection' 17 | 18 | # alias to avoid shift 19 | map global object 'd' '"' -docstring 'double quote string' 20 | map global object 'o' 'B' -docstring 'braces' 21 | 22 | # see issue #9 23 | # first normal behavior, then fallback if it fails 24 | define-command -hidden text-object-block -params 1 %@ 25 | evaluate-commands %sh! 26 | # there may be clever way to do this, but I don't want to be clever in shell 27 | case "$1" in 28 | '(') t=')';dir=''; ;; 29 | '{') t='}';dir=''; ;; 30 | '[') t=']';dir=''; ;; 31 | '<') t='>';dir=''; ;; 32 | ')') t='(';dir='/'; ;; 33 | '}') t='{';dir='/'; ;; 34 | ']') t='[';dir='/'; ;; 35 | '>') t='<';dir='/'; ;; 36 | esac 37 | # $t is used instead of $1 to provide more 'intuitive' prev / next blocks when nested 38 | echo "try %| exec $kak_opt_objects_last_mode '$t' | catch %| exec $dir \Q '$t' \E ; exec $kak_opt_objects_last_mode '$t' |" 39 | ! 40 | @ 41 | 42 | # this line object may seem to repeat builtins, 43 | # it is mostly here to improve the orthogonality of kakoune design 44 | define-command -hidden text-object-line %{ 45 | evaluate-commands %sh{ 46 | case "$kak_opt_objects_last_mode" in 47 | # around 48 | '') k='x' ;; 49 | '[') k='' ;; 50 | ']') k='L' ;; 51 | '{') k='Gh' ;; 52 | '}') k='GlL' ;; 53 | # inside 54 | '') k='x_' ;; 55 | '') k='_' ;; 56 | '') k='' ;; 57 | '') k='Gi' ;; 58 | '') k='Gl' ;; 59 | esac 60 | [ -n "$k" ] && echo "execute-keys $k" 61 | } 62 | } 63 | 64 | # this buffer object may seem to repeat builtins, 65 | # it is mostly here to improve the orthogonality of kakoune design 66 | define-command -hidden text-object-buffer %{ 67 | evaluate-commands %sh{ 68 | case "$kak_opt_objects_last_mode" in 69 | # around 70 | '') k='\%' ;; 71 | '[') k='\;Gk' ;; 72 | ']') k='\;Ge' ;; 73 | '{') k='Gk' ;; 74 | '}') k='Ge' ;; 75 | # inside 76 | '') k='\%_' ;; 77 | '') k='\;Gk_' ;; 78 | '') k='\;Ge_' ;; 79 | '') k='Gk_' ;; 80 | '') k='Ge_' ;; 81 | esac 82 | [ -n "$k" ] && echo "execute-keys $k" 83 | } 84 | } 85 | 86 | # work in progress - very brittle for now 87 | define-command -hidden text-object-tag %{ 88 | evaluate-commands %sh{ 89 | case "$kak_opt_objects_last_mode" in 90 | '') k='2f' ;; 91 | '') k='c,' ;; 92 | esac 93 | [ -n "$k" ] && echo "execute-keys $k" 94 | } 95 | } 96 | 97 | # thanks occivink 98 | define-command -hidden text-object-indented-paragraph %{ 99 | execute-keys -draft -save-regs '' 'pZ' 100 | execute-keys 'ii' 101 | } 102 | 103 | # depends on occivink/kakoune-vertical-selection 104 | define-command -hidden text-object-vertical %{ 105 | try %{ 106 | evaluate-commands %sh{ 107 | case "$kak_opt_objects_last_mode" in 108 | '') k=':vertical-selection-up-and-down' ;; 109 | '') k='w:vertical-selection-up-and-down' ;; 110 | '[') k=':vertical-selection-up' ;; 111 | ']') k=':vertical-selection-down' ;; 112 | '{') k='w:vertical-selection-up' ;; 113 | '}') k='w:vertical-selection-down' ;; 114 | esac 115 | [ -n "$k" ] && echo "execute-keys $k" 116 | } 117 | } catch %{ 118 | fail "no selections remaining" 119 | } 120 | } 121 | 122 | # helpers 123 | 124 | # hack to know in which "submode" we are 125 | # gGvV are not used in the context of this plugin 126 | declare-option -hidden str objects_last_mode 127 | hook global NormalKey (g|G|v|V|||\[|\]|\{|\}||||) %{ 128 | set-option global objects_last_mode %val{hook_param} 129 | } 130 | 131 | # selectors user-mode, see README for usage 132 | declare-user-mode selectors 133 | 134 | map global selectors 'a' '*%s' -docstring 'select all' 135 | 136 | map global selectors 'i' '' -docstring 'select inside object ' 137 | map global selectors 'o' '' -docstring 'select outside object ' 138 | 139 | map global selectors 'j' '' -docstring 'select inner object start ' 140 | map global selectors 'k' '' -docstring 'select inner object end ' 141 | map global selectors 'J' '' -docstring 'extend inner object start ' 142 | map global selectors 'K' '' -docstring 'extend inner object end ' 143 | 144 | map global selectors 'h' '[' -docstring 'select object start [' 145 | map global selectors 'l' ']' -docstring 'select object end ]' 146 | map global selectors 'H' '{' -docstring 'extend object start {' 147 | map global selectors 'L' '}' -docstring 'extend object end }' 148 | --------------------------------------------------------------------------------