├── LICENSE ├── README.md ├── autoload └── caser.vim ├── doc └── caser.txt └── plugin └── caser.vim /LICENSE: -------------------------------------------------------------------------------- 1 | vim-caser Handy commands for changing word casing 2 | Copyright (C) 2018 Arthur Xavier 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # caser.vim 2 | 3 | Easily change word casing with motions, text objects or visual mode. 4 | 5 | This plugin is largely inspired by Tim Pope's [vim-abolish](https://github.com/tpope/vim-abolish), and aims to improve its casing functionality to better blend with Vim's editing philosophy. 6 | 7 | ## Table of contents 8 | 9 | 1. [Installation](#installation) 10 | 1. [Usage](#installation) 11 | - [Limitations](#limitations) 12 | - [Global options](#global-options) 13 | 14 | ## Installation 15 | 16 | `vim-caser` may be installed by any of your favourite plugin managers. Be it Pathogen, Vundle or Plug, use whichever you prefer. 17 | 18 | For example, when using with [Plug](https://github.com/junegunn/vim-plug): 19 | 20 | ```vim 21 | Plug 'arthurxavierx/vim-caser' 22 | ``` 23 | 24 | ## Usage 25 | 26 | `vim-caser` makes it easy to convert between multiple word casings, from `camelCase` to `snake_case` to `Title Case`, it blends nicely with Vim's editing features such as _motions_, _text objects_ and _visual mode_. 27 | 28 | All mappings below must be followed by a motion or a text object, or be applied within visual mode. 29 | 30 | Case | Default Mapping | Plug Mapping (normal/visual) 31 | ------|-----------------|------------------------------ 32 | `MixedCase` or `PascalCase` | `gsm` or `gsp` | `CaserMixedCase`/`CaserVMixedCase` 33 | `camelCase` | `gsc` | `CaserCamelCase`/`CaserVCamelCase` 34 | `snake_case` | `gs_` | `CaserSnakeCase`/`CaserVSnakeCase` 35 | `UPPER_CASE` | `gsu` or `gsU` | `CaserUpperCase`/`CaserVUpperCase` 36 | `Title Case` | `gst` | `CaserTitleCase`/`CaserVTitleCase` 37 | `Sentence case` | `gss` | `CaserSentenceCase`/`CaserVSentenceCase` 38 | `space case` | `gs` | `CaserSpaceCase`/`CaserVSpaceCase` 39 | `dash-case` or `kebab-case` | `gs-` or `gsk` | `CaserKebabCase`/`CaserVKebabCase` 40 | `Title-Dash-Case` or `Title-Kebab-Case` | `gsK` | `CaserTitleKebabCase`/`CaserVTitleKebabCase` 41 | `dot.case` | `gs.` | `CaserDotCase`/`CaserVDotCase` 42 | 43 | The `gs` prefix can be changed through the option `g:caser_prefix`. Alternatively, the default mappings can be disabled by setting `g:caser_no_mappings`. 44 | 45 | ### Limitations 46 | Currently `vim-caser` only supports the casing options displayed in the table above. If you would like to have another casing option in `vim-caser`, please feel free to create an issue in this repository, or to submit a pull request with the feature properly documented. 47 | 48 | ### Global options 49 | Name | Default | Description 50 | ------|---------|------------- 51 | `g:caser_prefix` | `gs` | The prefix of all mappings created by `vim-caser`. E.g.: `gsc`, `gs-`, `gsu`, etc. 52 | `g:caser_no_mappings` | not set | Set to `1` to disable default mappings. Custom mappings can be set using the `` mappings. 53 | -------------------------------------------------------------------------------- /autoload/caser.vim: -------------------------------------------------------------------------------- 1 | " This has been adapted from Tim Pope's amazing abolish.vim 2 | " 3 | 4 | " Case Handlers {{{ 5 | function! caser#CamelCase(word) 6 | let word = substitute(a:word, '[ .-]', '_', 'g') 7 | if word !~# '_' && word =~# '\l' 8 | return substitute(word, '^.', '\l&', '') 9 | else 10 | return substitute(word, '\C\(_\)\=\(.\)', '\=submatch(1)==""?tolower(submatch(2)) : toupper(submatch(2))', 'g') 11 | endif 12 | endfunction 13 | 14 | function! caser#MixedCase(word) 15 | return substitute(caser#CamelCase(a:word), '^.', '\u&', '') 16 | endfunction 17 | 18 | function! caser#SnakeCase(word) 19 | let word = substitute(a:word, '::', '/', 'g') 20 | let word = substitute(word, '\(\u\+\)\(\u\l\)', '\1_\2', 'g') 21 | let word = substitute(word, '\(\l\|\d\)\(\u\)', '\1_\2', 'g') 22 | let word = substitute(word, '[ .-]', '_', 'g') 23 | let word = tolower(word) 24 | return word 25 | endfunction 26 | 27 | function! caser#UpperCase(word) 28 | return toupper(caser#SnakeCase(a:word)) 29 | endfunction 30 | 31 | function! caser#KebabCase(word) 32 | return substitute(caser#SnakeCase(a:word), '_', '-', 'g') 33 | endfunction 34 | 35 | function! caser#TitleKebabCase(word) 36 | return substitute(caser#TitleCase(a:word), ' ', '-', 'g') 37 | endfunction 38 | 39 | function! caser#SpaceCase(word) 40 | return substitute(caser#SnakeCase(a:word), '_', ' ', 'g') 41 | endfunction 42 | 43 | function! caser#TitleCase(word) 44 | return substitute(caser#SpaceCase(a:word), '\(\<\w\)', '\=toupper(submatch(1))', 'g') 45 | endfunction 46 | 47 | function! caser#SentenceCase(word) 48 | return substitute(caser#SpaceCase(a:word), '^\(\<\w\)', '\=toupper(submatch(1))', 'g') 49 | endfunction 50 | 51 | function! caser#DotCase(word) 52 | return substitute(caser#SnakeCase(a:word), '_', '.', 'g') 53 | endfunction 54 | "}}} 55 | 56 | " Setup {{{ 57 | 58 | " Adapted from unimpaired.vim by Tim Pope. 59 | function! caser#DoAction(fn, type) 60 | " backup settings that we will change 61 | let sel_save = &selection 62 | let cb_save = &clipboard 63 | 64 | " make selection and clipboard work the way we need 65 | set selection=inclusive clipboard-=unnamed clipboard-=unnamedplus 66 | 67 | " backup the unnamed register, which we will be yanking into 68 | let reg_save = @@ 69 | let sel = "" 70 | 71 | " yank the relevant text, and also set the visual selection (which will be reused if the text 72 | " needs to be replaced) 73 | if a:type =~ '^\d\+$' 74 | " if type is a number, then select that many lines 75 | let sel = 'V'.a:type.'$y' 76 | elseif a:type =~ '^.$' 77 | " if type is 'v', 'V', or '' (i.e. 0x16) then reselect the visual region 78 | let sel = "`<" . a:type . "`>y" 79 | elseif a:type == 'line' 80 | " line-based text motion 81 | let sel = "'[V']y" 82 | elseif a:type == 'block' 83 | " block-based text motion 84 | let sel = "`[\`]y" 85 | else 86 | " char-based text motion 87 | let sel = "`[v`]y" 88 | endif 89 | 90 | silent exe 'normal! ' . sel 91 | 92 | " call the user-defined function, passing it the contents of the unnamed register 93 | let repl = {"caser#".a:fn}(@@) 94 | 95 | " if the function returned a value, then replace the text 96 | if type(repl) == 1 97 | " put the replacement text into the unnamed register, and also set it to be a 98 | " characterwise, linewise, or blockwise selection, based upon the selection type of the 99 | " yank we did above 100 | call setreg('@', repl, getregtype('@')) 101 | 102 | " reselect the visual region and paste 103 | normal! gvp 104 | endif 105 | 106 | " restore saved settings and register value 107 | let @@ = reg_save 108 | let &selection = sel_save 109 | let &clipboard = cb_save 110 | endfunction 111 | 112 | function! caser#ActionOpfunc(type) 113 | return caser#DoAction(s:encode_fn, a:type) 114 | endfunction 115 | 116 | function! caser#ActionSetup(fn) 117 | let s:encode_fn = a:fn 118 | let &opfunc = matchstr(expand(''), '\d\+_').'caser#ActionOpfunc' 119 | endfunction 120 | 121 | function! caser#MapAction(fn, keys) 122 | exe 'nnoremap Caser'.a:fn.' :call caser#ActionSetup("'.a:fn.'")g@' 123 | exe 'xnoremap CaserV'.a:fn.' :call caser#DoAction("'.a:fn.'",visualmode())' 124 | 125 | if !exists('g:caser_no_mappings') || !g:caser_no_mappings 126 | for key in a:keys 127 | exe 'nmap '.g:caser_prefix.key.' Caser'.a:fn 128 | exe 'xmap '.g:caser_prefix.key.' CaserV'.a:fn 129 | endfor 130 | endif 131 | endfunction 132 | " }}} 133 | -------------------------------------------------------------------------------- /doc/caser.txt: -------------------------------------------------------------------------------- 1 | *vim-caser* Handy commands for changing word casing 2 | 3 | Author: Arthur Xavier (https://github.com/arthurxavierx) 4 | License: GNU GPL 3 5 | Repository: https://github.com/arthurxavierx/vim-caser 6 | 7 | ============================================================================== 8 | 9 | Change casing of words, sentences, paragraphs, selections, or anything vim 10 | allows you to select with a |movement|, a |text-object| or |Visual-mode|. 11 | 12 | ------------------------------------------------------------------------------ 13 | 14 | DEFAULT MAPPINGS *caser-defaults* 15 | 16 | gsp{motion} *gsp* *caser-pascal* 17 | gsm{motion} *gsm* *caser-mixed* 18 | {Visual}gsp Make {motion} or highlighted text `PascalCase`/`MixedCase` 19 | {Visual}gsm (for {Visual} see |Visual-mode|). 20 | 21 | gsc{motion} *gsc* *caser-camel* 22 | {Visual}gsc Make {motion} or highlighted text `camelCase` 23 | (for {Visual} see |Visual-mode|). 24 | 25 | gs_{motion} *gs_* *caser-snake* 26 | {Visual}gs_ Make {motion} or highlighted text `snake_case` 27 | (for {Visual} see |Visual-mode|). 28 | 29 | gsu{motion} *gsu* 30 | gsU{motion} *gsU* *caser-upper* 31 | {Visual}gsu Make {motion} or highlighted text `UPPER_CASE` 32 | {Visual}gsU (for {Visual} see |Visual-mode|). 33 | 34 | gst{motion} *gst* *caser-title* 35 | {Visual}gst Make {motion} or highlighted text `Title Case` 36 | (for {Visual} see |Visual-mode|). 37 | 38 | gss{motion} *gss* *caser-sentence* 39 | {Visual}gss Make {motion} or highlighted text `Sentence case` 40 | (for {Visual} see |Visual-mode|). 41 | 42 | gs{motion} *gs* *caser-space* 43 | {Visual}gs Make {motion} or highlighted text `space case` 44 | (for {Visual} see |Visual-mode|). 45 | 46 | gs-{motion} *gs-* *caser-dash* 47 | gsk{motion} *gsk* *caser-kebab* 48 | gsK{motion} *gsK* *caser-title-kebab* 49 | {Visual}gs- Make {motion} or highlighted text `dash-case`/`kebab-case` 50 | {Visual}gsk (for {Visual} see |Visual-mode|). 51 | {Visual}gsK 52 | 53 | gs.{motion} *gs.* *caser-dot* 54 | {Visual}gs. Make {motion} or highlighted text `dot.case` 55 | (for {Visual} see |Visual-mode|). 56 | 57 | ------------------------------------------------------------------------------ 58 | 59 | MAPPINGS *caser-mappings* 60 | 61 | Each default mapping corresponds to a || mapping which can be used to set 62 | custom mappings. For example |CaserCamelCase| corresponds to the 63 | |caser-mixed| default normal mode binding 'gsc{motion}', while 64 | |CaserVCamelCase| corresponds to the default visual mode binding 65 | '{Visual}gsc'. 66 | 67 | CaserMixedCase *CaserMixedCase* 68 | CaserVMixedCase *CaserVMixedCase* 69 | 70 | CaserCamelCase *CaserCamelCase* 71 | CaserVCamelCase *CaserVCamelCase* 72 | 73 | CaserSnakeCase *CaserSnakeCase* 74 | CaserVSnakeCase *CaserVSnakeCase* 75 | 76 | CaserUpperCase *CaserUpperCase* 77 | CaserVUpperCase *CaserVUpperCase* 78 | 79 | CaserTitleCase *CaserTitleCase* 80 | CaserVTitleCase *CaserVTitleCase* 81 | 82 | CaserSentenceCase *CaserSentenceCase* 83 | CaserVSentenceCase *CaserVSentenceCase* 84 | 85 | CaserSpaceCase *CaserSpaceCase* 86 | CaserVSpaceCase *CaserVSpaceCase* 87 | 88 | CaserKebabCase *CaserKebabCase* 89 | CaserVKebabCase *CaserVKebabCase* 90 | 91 | CaserTitleKebabCase *CaserTitleKebabCase* 92 | CaserVTitleKebabCase *CaserVTitleKebabCase* 93 | 94 | CaserDotCase *CaserDotCase* 95 | CaserVDotCase *CaserVDotCase* 96 | 97 | ------------------------------------------------------------------------------ 98 | 99 | OPTIONS *caser-options* 100 | 101 | *g:caser_prefix* 102 | g:caser_prefix Allows for customizing the prefix used in all 103 | |vim-caser| mappings. Example: to use 'gc' as the 104 | prefix -- making commands such as 'gcs' to convert text 105 | text to `snake_case`: > 106 | :let g:caser_prefix = 'gc' 107 | 108 | *g:caser_no_mappings* 109 | g:caser_no_mappings Disable default mappings. The value of |g:caser_prefix| 110 | is ignored. See |caser-mappings| for setting custom 111 | mappings. Example: to use 'c' for `camelCase` and 'C' 112 | for `MixedCase`, but not set any other mappings: > 113 | :let g:caser_no_mappings = 1 114 | :nmap gsc CaserCamelCase 115 | :xmap gsc CaserVCamelCase 116 | :nmap gsC CaserMixedCase 117 | :xmap gsC CaserVMixedCase 118 | 119 | ============================================================================== 120 | vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: 121 | -------------------------------------------------------------------------------- /plugin/caser.vim: -------------------------------------------------------------------------------- 1 | if exists('g:loaded_caser') 2 | finish 3 | endif 4 | let g:loaded_caser = 1 5 | 6 | " Defaults {{{ 7 | if !exists('g:caser_prefix') 8 | let g:caser_prefix = 'gs' 9 | endif 10 | 11 | call caser#MapAction('MixedCase', ['m', 'p']) 12 | call caser#MapAction('CamelCase', ['c']) 13 | call caser#MapAction('SnakeCase', ['_']) 14 | call caser#MapAction('UpperCase', ['u', 'U']) 15 | call caser#MapAction('TitleCase', ['t']) 16 | call caser#MapAction('SentenceCase', ['s']) 17 | call caser#MapAction('SpaceCase', ['']) 18 | call caser#MapAction('KebabCase', ['k', '-']) 19 | call caser#MapAction('TitleKebabCase', ['K']) 20 | call caser#MapAction('DotCase', ['.']) 21 | " }}} 22 | --------------------------------------------------------------------------------