├── 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 |
--------------------------------------------------------------------------------