├── .github └── workflows │ ├── reviewdog.yml │ ├── vader.yml │ └── vint.yml ├── .vintrc.yaml ├── LICENSE ├── README.markdown ├── addon-info.json ├── autoload └── textobj │ ├── quote.vim │ └── quote │ ├── educate.vim │ ├── replace.vim │ └── surround.vim ├── doc └── textobj-quote.txt ├── plugin └── textobj │ └── quote.vim ├── run_tests.sh └── spec ├── educate.vader ├── intl.vader ├── match.vader ├── replace.vader ├── select.vader └── surround.vader /.github/workflows/reviewdog.yml: -------------------------------------------------------------------------------- 1 | name: Reviewdog 2 | on: [pull_request] 3 | jobs: 4 | vint: 5 | name: vint 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | - name: vint 10 | uses: reviewdog/action-vint@v1 11 | with: 12 | github_token: ${{ secrets.github_token }} 13 | reporter: github-pr-review 14 | -------------------------------------------------------------------------------- /.github/workflows/vader.yml: -------------------------------------------------------------------------------- 1 | name: Vader 2 | on: [push, pull_request] 3 | jobs: 4 | vader: 5 | name: vader 6 | runs-on: ubuntu-latest 7 | strategy: 8 | fail-fast: false 9 | matrix: 10 | vimFlavor: ["vim", "nvim"] 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | - name: Enable Universe package repository 15 | run: | 16 | sudo add-apt-repository universe 17 | sudo apt-get update 18 | - name: Install ${{ matrix.vimFlavor }} 19 | run: | 20 | sudo apt-get install ${{ matrix.vimFlavor == 'nvim' && 'neovim' || 'vim' }} 21 | - name: Review versions 22 | run: | 23 | ${{ matrix.vimFlavor }} --version 24 | - name: Fetch Vader and other dependencies 25 | run: | 26 | git clone --depth 1 https://github.com/junegunn/vader.vim.git 27 | git clone --depth 1 https://github.com/kana/vim-textobj-user.git 28 | git clone --depth 1 https://github.com/tpope/vim-surround.git 29 | - name: Create minimal vimrc file 30 | run: | 31 | cat << EOF > .minrc 32 | set nocompatible 33 | filetype off 34 | set rtp+=vader.vim 35 | set rtp+=vim-textobj-user 36 | set rtp+=vim-surround 37 | set rtp+=. 38 | filetype plugin indent on 39 | syntax enable 40 | set shortmess+=I 41 | EOF 42 | - name: Run test suite 43 | run: | 44 | ${{ matrix.vimFlavor == 'nvim' && 'nvim --headless' || 'vim -N' }} \ 45 | -i NONE -u .minrc "+Vader! spec/*" 46 | -------------------------------------------------------------------------------- /.github/workflows/vint.yml: -------------------------------------------------------------------------------- 1 | name: Vint 2 | on: [push] 3 | jobs: 4 | vint: 5 | name: vint 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout 9 | uses: actions/checkout@v2 10 | - name: Set up Python 11 | uses: actions/setup-python@v2 12 | - name: Setup dependencies 13 | run: pip install vim-vint 14 | - name: Lint Vimscript 15 | run: vint . 16 | -------------------------------------------------------------------------------- /.vintrc.yaml: -------------------------------------------------------------------------------- 1 | cmdargs: 2 | severity: style_problem 3 | color: true 4 | env: 5 | neovim: false 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | License: The MIT License (MIT) 2 | 3 | Copyright (c) 2013,2014 Reed Esau 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | 24 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # vim-textobj-quote 2 | 3 | [![Vader](https://github.com/preservim/vim-textobj-quote/workflows/Vader/badge.svg)](https://github.com/preservim/vim-textobj-quote/actions?workflow=Vader) 4 | [![Vint](https://github.com/preservim/vim-textobj-quote/workflows/Vint/badge.svg)](https://github.com/preservim/vim-textobj-quote/actions?workflow=Vint) 5 | 6 | > “Extending Vim to better support typographic (‘curly’) quote characters.” 7 | 8 | While Vim is renowned for its text manipulation capabilities, it 9 | nevertheless retains a bias towards ASCII that stretches back to its vi 10 | roots on Unix. This can limit Vim’s appeal for those who prefer 11 | typographic characters like “curly quotes” over ASCII "straight quotes" in 12 | the prose and documentation they write. 13 | 14 | Core features of this plugin: 15 | 16 | * Text object supporting “typographic quotes”, incl. motion commands 17 | * Implemented with regular expressions via the [kana/vim-textobj-user][vt] plugin 18 | * Supports quoted strings containing contractions (`“don’t”`, e.g.) 19 | * Configurable to support [international variations in quotation marks][iq] 20 | 21 | [iq]: http://en.wikipedia.org/wiki/International_variation_in_quotation_marks 22 | 23 | Includes four additional features: 24 | 25 | * _educate_ - automatic entry of ‘typographic quotes’ from the 'straight quote' keys 26 | * _replace_ - transform quotes from straight to typographic, and vice versa 27 | * _matchit_ - `%` matching for typographic quote pairs 28 | * _surround_ - surround a word or visual selection with quotes 29 | 30 | ## Requirements 31 | 32 | Requires a recent version of Vim compiled with Unicode support. 33 | 34 | ## Installation 35 | 36 | Install using Pathogen, Vundle, Neobundle, or your favorite Vim package 37 | manager. 38 | 39 | This plugin has an essential dependency that you will need to install: 40 | 41 | * [kana/vim-textobj-user][vt] - a Vim plugin to create your own text objects without pain 42 | 43 | [vt]: https://github.com/kana/vim-textobj-user 44 | 45 | ## Configuration 46 | 47 | Because you won't want typographic quotes in your code, the behavior of 48 | this plugin can be configured per file type. For example, to enable 49 | typographic quote support in `markdown` and `textile` files, place in your 50 | `.vimrc`: 51 | 52 | ```vim 53 | set nocompatible 54 | filetype plugin on " may already be in your .vimrc 55 | 56 | augroup textobj_quote 57 | autocmd! 58 | autocmd FileType markdown call textobj#quote#init() 59 | autocmd FileType textile call textobj#quote#init() 60 | autocmd FileType text call textobj#quote#init({'educate': 0}) 61 | augroup END 62 | ``` 63 | 64 | The last `autocmd` statement initializes the plugin for buffers of `text` 65 | file type, but disables the ‘educate’ feature by default. More on that 66 | below. 67 | 68 | ## Motion commands 69 | 70 | Motion commands on text objects are a powerful feature of Vim. 71 | 72 | By default, for motion commands, `q` denotes an operation on “double” 73 | quotes. `Q` for ‘single’ quotes. For example, with the `c` change 74 | operator: 75 | 76 | * `caq` - change _around_ “double” quotes - includes quote chars 77 | * `ciq` - change _inside_ “double” quotes - excludes quote chars 78 | * `caQ` - change _around_ ‘single’ quotes - includes quote chars 79 | * `ciQ` - change _inside_ ‘single’ quotes - excludes quote chars 80 | 81 | Apart from `c` for change, you can `v` for visual selection, `d` for 82 | deletion, `y` for yanking to clipboard, etc. Note that count isn’t 83 | supported at present (due to limitations of the underlying 84 | vim-textobj-user) but repeat with `.` should work. 85 | 86 | _quote_’s motion command is smart too, able to distinguish between an 87 | apostrophe and single closing quote, even though both are represented by 88 | the same glyph. For example, try out `viQ` on the following sentence: 89 | 90 | ``` 91 | ‘Really, I’d rather not relive the ’70s,’ said zombie Elvis. 92 | ``` 93 | 94 | You can change these key mappings from their defaults in your `.vimrc`: 95 | 96 | ```vim 97 | let g:textobj#quote#doubleMotion = 'q' 98 | let g:textobj#quote#singleMotion = 'Q' 99 | ``` 100 | 101 | ## Additional features 102 | 103 | The four additional features of this plugin include: _educate_, _matchit_, 104 | _replace_, and _surround_. 105 | 106 | ### Educate 107 | 108 | This plugin will ‘educate’ quotes, meaning that while in _Insert_ mode, 109 | your straight quote key presses (`"` or `'`) will be dynamically 110 | transformed into the appropriate typographic quote characters. 111 | 112 | For example, entering the following sentence without the _educate_ feature 113 | using the straight quote keys: 114 | 115 | ``` 116 | "It's Dr. Evil. I didn't spend six years in Evil Medical 117 | School to be called 'mister,' thank you very much." 118 | ``` 119 | 120 | As expected all the quotes are straight ones. But with the _educate_ 121 | feature, the straight quotes are transformed into the typographic 122 | equivalent as you type: 123 | 124 | ``` 125 | “It’s Dr. Evil. I didn’t spend six years in Evil Medical 126 | School to be called ‘mister,’ thank you very much.” 127 | ``` 128 | 129 | You can configure the default settings for the _educate_ feature in your 130 | `.vimrc`: 131 | 132 | ```vim 133 | let g:textobj#quote#educate = 1 " 0=disable, 1=enable (def) 134 | ``` 135 | 136 | You can change educating behavior with the following commands: 137 | 138 | * `Educate` 139 | * `NoEducate` 140 | * `ToggleEducate` 141 | 142 | As seen above, educating behavior can be configured as a parameter in the 143 | `textobj#quote#init()` call. 144 | 145 | #### Entering straight quotes 146 | 147 | In some cases, straight (ASCII) quotes are needed, such as: 148 | 149 | ``` 150 | “print "Hello World!"” is a simple program you can write in Python. 151 | ``` 152 | 153 | To insert a straight quote while educating, enter `«Ctrl-V»` (mnemonic is 154 | _verbatim_) before the quote key: 155 | 156 | * `«Ctrl-V» "` - straight double quote 157 | * `«Ctrl-V» '` - straight single quote 158 | 159 | Note that for units of measurement you’ll want to use the prime symbol(s) 160 | rather than straight quotes, as in: 161 | 162 | ``` 163 | Standing at 7′3″ (2.21 m), Hasheem Thabeet of the Oklahoma City Thunder 164 | is the tallest player in the NBA. 165 | ``` 166 | 167 | ### Matchit support 168 | 169 | _matchit_ enables jumping to matching typographic quotes. 170 | 171 | * `%` - jump to the matching typographic (curly) quote character 172 | 173 | You can configure this feature in your `.vimrc`: 174 | 175 | ```vim 176 | let g:textobj#quote#matchit = 1 " 0=disable, 1=enable (def) 177 | ``` 178 | 179 | ### Replace support 180 | 181 | You can replace straight quotes in existing text with curly quotes, and 182 | visa versa. Add key mappings of your choice to your `.vimrc`: 183 | 184 | ```vim 185 | map qc ReplaceWithCurly 186 | map qs ReplaceWithStraight 187 | ``` 188 | 189 | Both _Normal_ and _Visual_ modes are supported by this feature. (In 190 | _Normal_ mode, quotes in the current paragraph are replaced.) 191 | 192 | To transform all quotes in a document, use _Visual_ mode to select the 193 | entire document. 194 | 195 | ### Surround support 196 | 197 | By default there are no key mappings for `surround` support. 198 | 199 | #### Basic support 200 | 201 | This feature supports basic surround capabilities. Add to your `.vimrc` 202 | key mappings of your choice: 203 | 204 | ```vim 205 | " NOTE: remove these mappings if using the tpope/vim-surround plugin! 206 | map Sq SurroundWithDouble 207 | map SQ SurroundWithSingle 208 | ``` 209 | 210 | Then you can use ‘motion commands’ to surround text with quotes: 211 | 212 | (an asterisk is used to denote the cursor position) 213 | 214 | * `visSq` - My senten\*ce. => “My sentence.” 215 | * `visSQ` - My senten\*ce. => ‘My sentence.’ 216 | 217 | #### Using Tim Pope’s vim-surround 218 | 219 | Using Tim Pope’s [vim-surround][] plugin your text object key mappings 220 | should be available. For example, 221 | 222 | * `cs'q` - 'Hello W\*orld' => “Hello World” 223 | * `cs"q` - "Hello W\*orld" => “Hello World” 224 | * `cs(q` - (Hello W\*orld) => “Hello World” 225 | * `cs(Q` - (Hello W\*orld) => ‘Hello World’ 226 | 227 | [vim-surround]: https://github.com/tpope/vim-surround 228 | 229 | ## Entering special characters 230 | 231 | Sometimes you must enter special characters (like typographic quotes) 232 | manually, such as in a search expression. You can do so through Vim’s digraphs 233 | or via your operating system’s keyboard shortcuts. 234 | 235 | | Glyph | Vim Digraph | OS X | Description 236 | | ----- | ----------- | ------------------ | ---------------------------- 237 | | `‘` | `'6` | `Opt-]` | left single quotation mark 238 | | `’` | `'9` | `Shift-Opt-]` | right single quotation mark 239 | | `“` | `"6` | `Opt-[` | left double quotation mark 240 | | `”` | `"9` | `Shift-Opt-[` | right double quotation mark 241 | | `‚` | `.9` | | single low-9 quote 242 | | `„` | `:9` | `Shift-Opt-w` | double low-9 quote 243 | | `‹` | `1<` | `Opt-\` | left pointing single quotation mark 244 | | `›` | `1>` | `Shift-Opt-\` | right pointing single quotation mark 245 | | `«` | `<<` | `Opt-\` | left pointing double quotation mark 246 | | `»` | `>>` | `Shift-Opt-\` | right pointing double quotation mark 247 | | `′` | `1'` | | single prime 248 | | `″` | `2'` | | double prime 249 | | `–` | `-N` | `Opt-hyphen` | en dash 250 | | `—` | `-M` | `Shift-Opt-hyphen` | em dash 251 | | `…` | `,.` | `Opt-;` | horizontal ellipsis 252 | | ` ` | `NS` | | non-breaking space 253 | | `ï` | `i:` | `Opt-U` `i` | lowercase i, umlaut 254 | | `æ` | `ae` | `Opt-'` | lowercase ae 255 | 256 | For example, to enter left double quotation mark `“`, precede the digraph code 257 | `"6` with `Ctrl-K`, like 258 | 259 | * `«Ctrl-K» "6` 260 | 261 | Alternatively, if you’re on OS X, you can use `Opt-[` to enter this 262 | character. 263 | 264 | For more details, see: 265 | 266 | * `:help digraphs` 267 | 268 | ## International support 269 | 270 | Many international keyboards feature keys to allow you to input 271 | typographic quote characters directly. In such cases, you won’t need to 272 | change the behavior of the straight quote keys. 273 | 274 | But if you do, you can override the defaults. For example, those users 275 | editing most of their prose in German could change those defaults to: 276 | 277 | ```vim 278 | let g:textobj#quote#doubleDefault = '„“' " „doppel“ 279 | let g:textobj#quote#singleDefault = '‚‘' " ‚einzel‘ 280 | ``` 281 | 282 | Or on a file type initialization... 283 | 284 | ```vim 285 | augroup textobj_quote 286 | autocmd! 287 | autocmd FileType markdown call textobj#quote#init({ 'double':'„“', 'single':'‚‘' }) 288 | ... 289 | augroup END 290 | ``` 291 | 292 | Or in a key mapping... 293 | 294 | ```vim 295 | nnoremap qd :call textobj#quote#init({ 'double':'„“', 'single':'‚‘' }) 296 | ``` 297 | 298 | ## See also 299 | 300 | If you find this plugin useful, check out these others originally by [@reedes][re]: 301 | 302 | * [vim-colors-pencil][cp] - color scheme for Vim inspired by IA Writer 303 | * [vim-lexical][lx] - building on Vim’s spell-check and thesaurus/dictionary completion 304 | * [vim-litecorrect][lc] - lightweight auto-correction for Vim 305 | * [vim-pencil][pn] - rethinking Vim as a tool for writers 306 | * [vim-textobj-sentence][ts] - improving on Vim's native sentence motion command 307 | * [vim-thematic][th] - modify Vim’s appearance to suit your task and environment 308 | * [vim-wheel][wh] - screen-anchored cursor movement for Vim 309 | * [vim-wordy][wo] - uncovering usage problems in writing 310 | * [vim-wordchipper][wc] - power tool for shredding text in Insert mode 311 | 312 | [re]: http://github.com/reedes 313 | [cp]: http://github.com/preservim/vim-colors-pencil 314 | [lc]: http://github.com/preservim/vim-litecorrect 315 | [lx]: http://github.com/preservim/vim-lexical 316 | [pn]: http://github.com/preservim/vim-pencil 317 | [th]: http://github.com/preservim/vim-thematic 318 | [ts]: http://github.com/preservim/vim-textobj-sentence 319 | [wc]: http://github.com/preservim/vim-wordchipper 320 | [wh]: http://github.com/preservim/vim-wheel 321 | [wo]: http://github.com/preservim/vim-wordy 322 | 323 | ## Future development 324 | 325 | If you’ve spotted a problem or have an idea on improving this plugin, 326 | please post it to the [GitHub project issue page][issues]. 327 | 328 | [issues]: https://github.com/preservim/vim-textobj-quote/issues 329 | 330 | 331 | -------------------------------------------------------------------------------- /addon-info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vim-textobj-quote", 3 | "description": "Use ‘curly’ quote characters in Vim", 4 | "version": "1.0", 5 | "repository": { 6 | "type": "git", "url": "git://github.com/preservim/vim-textobj-quote.git", 7 | "vim_script_nr": 4811, 8 | "script-type": "utility" 9 | }, 10 | "dependencies": { 11 | "textobj-user": {} 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /autoload/textobj/quote.vim: -------------------------------------------------------------------------------- 1 | " ============================================================================ 2 | " File: quote.vim 3 | " Description: autoload functions for vim-textobj-quote plugin 4 | " Maintainer: preservim 5 | " Created: February 6, 2013 6 | " License: The MIT License (MIT) 7 | " ============================================================================ 8 | 9 | scriptencoding utf-8 10 | 11 | if &compatible || ( exists('g:autoloaded_textobj_quote') && 12 | \ !exists('g:force_reload_textobj_quote')) 13 | finish 14 | endif 15 | 16 | function! s:unicode_enabled() abort 17 | return &encoding ==# 'utf-8' 18 | endfunction 19 | 20 | function! textobj#quote#getPrevCharRE(mode) abort 21 | " regex to match previous character 22 | " mode=1 is double; mode=0 is single 23 | return '\v(^|[[({& ' . 24 | \ (a:mode 25 | \ ? (b:textobj_quote_sl . '''])') 26 | \ : (b:textobj_quote_dl . '"])')) 27 | endfunction 28 | 29 | " set up mappings for current buffer only 30 | " initialize buffer-scoped variables 31 | " args: { 'double':'“”', 'single':'‘’',} 32 | function! textobj#quote#init(...) abort 33 | if !s:unicode_enabled() | return | endif 34 | 35 | let l:args = a:0 ? a:1 : {} 36 | let l:double_pair = get(l:args, 'double', g:textobj#quote#doubleDefault) 37 | let l:single_pair = get(l:args, 'single', g:textobj#quote#singleDefault) 38 | 39 | " obtain the individual quote characters 40 | let l:d_arg = split(l:double_pair, '\zs') 41 | let l:s_arg = split(l:single_pair, '\zs') 42 | let b:textobj_quote_dl = l:d_arg[0] 43 | let b:textobj_quote_dr = l:d_arg[1] 44 | let b:textobj_quote_sl = l:s_arg[0] 45 | let b:textobj_quote_sr = l:s_arg[1] 46 | 47 | call textobj#user#plugin('quote', { 48 | \ 'select-d': { 49 | \ 'select-a': 'a' . g:textobj#quote#doubleMotion, 50 | \ 'select-i': 'i' . g:textobj#quote#doubleMotion, 51 | \ 'pattern': [ b:textobj_quote_dl, b:textobj_quote_dr ] 52 | \ }, 53 | \ 'select-s': { 54 | \ 'select-a': 'a' . g:textobj#quote#singleMotion, 55 | \ 'select-i': 'i' . g:textobj#quote#singleMotion, 56 | \ 'pattern': [ b:textobj_quote_sl, 57 | \ (b:textobj_quote_sr ==# '’' ? b:textobj_quote_sr . 58 | \ '\(\w\)\@!' : b:textobj_quote_sr) ] 59 | \ }, 60 | \}) 61 | 62 | " initialize extensions 63 | 64 | if get(l:args, 'matchit', g:textobj#quote#matchit) && 65 | \ exists('b:match_words') 66 | " support '%' navigation of textobj_quote pairs 67 | if b:textobj_quote_dl != b:textobj_quote_dr 68 | " specialized closing pattern to ignore use of quote in contractions 69 | let b:match_words .= ',' . b:textobj_quote_dl . 70 | \':' . b:textobj_quote_dr . 71 | \ (b:textobj_quote_dr ==# '’' 72 | \ ? '\(\W\|$\)' 73 | \ : '') 74 | endif 75 | if b:textobj_quote_sl != b:textobj_quote_sr 76 | " specialized closing pattern to ignore use of quote in contractions 77 | let b:match_words .= ',' . b:textobj_quote_sl . 78 | \':' . b:textobj_quote_sr . 79 | \ (b:textobj_quote_sr ==# '’' 80 | \ ? '\(\W\|$\)' 81 | \ : '') 82 | endif 83 | endif 84 | 85 | if get(l:args, 'educate', g:textobj#quote#educate) 86 | call textobj#quote#educate#mapKeys(1) 87 | endif 88 | 89 | " q/Q support for tpope/vim-surround 90 | let l:char = g:textobj#quote#doubleMotion 91 | let l:nr = char2nr(l:char) 92 | exe 'let b:surround_' . l:nr . ' = b:textobj_quote_dl . "\r" . b:textobj_quote_dr' 93 | let l:char = g:textobj#quote#singleMotion 94 | let l:nr = char2nr(l:char) 95 | exe 'let b:surround_' . l:nr . ' = b:textobj_quote_sl . "\r" . b:textobj_quote_sr' 96 | 97 | endfunction 98 | 99 | let g:autoloaded_textobj_quote = 1 100 | -------------------------------------------------------------------------------- /autoload/textobj/quote/educate.vim: -------------------------------------------------------------------------------- 1 | " ============================================================================ 2 | " File: educate.vim 3 | " Description: autoload functions for educate feature 4 | " Maintainer: preservim 5 | " Created: February 6, 2013 6 | " License: The MIT License (MIT) 7 | " ============================================================================ 8 | " 9 | if exists('g:autoloaded_textobj_quote_educate') && 10 | \ g:autoloaded_textobj_quote_educate 11 | fini 12 | en 13 | 14 | function! s:unicode_enabled() abort 15 | return &encoding ==# 'utf-8' 16 | endfunction 17 | 18 | function! s:educateQuotes(mode) abort 19 | " intelligently insert curly quotes 20 | " mode=1 is double; mode=0 is single 21 | return search( 22 | \ textobj#quote#getPrevCharRE(a:mode) . '%#', 23 | \ 'n') 24 | \ ? (a:mode ? b:textobj_quote_dl : b:textobj_quote_sl) 25 | \ : (a:mode ? b:textobj_quote_dr : b:textobj_quote_sr) 26 | endfunction 27 | 28 | function! textobj#quote#educate#mapKeys(...) abort 29 | " Un/Map keys to un/educate quotes for current buffer 30 | " 1=map, 0=unmap 31 | let b:textobj_quote_educate_mapped = a:0 ? !!a:1 : 1 32 | if !exists('b:textobj_quote_dl') 33 | call textobj#quote#init() 34 | return 35 | endif 36 | if b:textobj_quote_educate_mapped 37 | " For details on the leading , see :help ins-special-special 38 | " TODO use instead? 39 | inoremap " =educateQuotes(1) 40 | inoremap ' =educateQuotes(0) 41 | else 42 | silent! iunmap " 43 | silent! iunmap ' 44 | endif 45 | endfunction 46 | 47 | function! textobj#quote#educate#toggleMappings() abort 48 | " Toggle mapped keys for current buffer 49 | let l:educate = 50 | \ !exists('b:textobj_quote_educate_mapped') 51 | \ ? 1 52 | \ : !b:textobj_quote_educate_mapped 53 | call textobj#quote#educate#mapKeys(l:educate) 54 | endfunction 55 | 56 | let g:autoloaded_textobj_quote_educate = 1 57 | -------------------------------------------------------------------------------- /autoload/textobj/quote/replace.vim: -------------------------------------------------------------------------------- 1 | " ============================================================================ 2 | " File: replace.vim 3 | " Description: autoload functions for replace feature 4 | " Maintainer: preservim 5 | " Created: February 6, 2013 6 | " License: The MIT License (MIT) 7 | " ============================================================================ 8 | " 9 | if exists('g:autoloaded_textobj_quote_replace') && 10 | \ g:autoloaded_textobj_quote_replace 11 | fini 12 | en 13 | 14 | " Copied from vim-yoink 15 | function! textobj#quote#replace#getDefaultReg() abort 16 | let clipboardFlags = split(&clipboard, ',') 17 | if index(clipboardFlags, 'unnamedplus') >= 0 18 | return '+' 19 | elseif index(clipboardFlags, 'unnamed') >= 0 20 | return '*' 21 | else 22 | return '"' 23 | endif 24 | endfunction 25 | 26 | function! textobj#quote#replace#replace(mode, visual) abort 27 | " 0=C->S 1=S->C 28 | if !exists('b:textobj_quote_dl') | return | endif 29 | " Extract the target text... 30 | if len(a:visual) > 0 31 | execute 'normal! gv"' . textobj#quote#replace#getDefaultReg() . 'y' 32 | else 33 | execute 'normal! vip"' . textobj#quote#replace#getDefaultReg() . 'y' 34 | endif 35 | let l:text = getreg(textobj#quote#replace#getDefaultReg()) 36 | 37 | if a:mode ==# 0 " replace curly with straight 38 | let l:text = substitute(l:text, '[' . b:textobj_quote_sl . b:textobj_quote_sr . ']', "'", 'g') 39 | let l:text = substitute(l:text, '[' . b:textobj_quote_dl . b:textobj_quote_dr . ']', '"', 'g') 40 | else " replace straight with curly 41 | let l:text = substitute(l:text, textobj#quote#getPrevCharRE(0) . '\zs''', b:textobj_quote_sl, 'g') 42 | let l:text = substitute(l:text, textobj#quote#getPrevCharRE(1) . '\zs"' , b:textobj_quote_dl, 'g') 43 | let l:text = substitute(l:text, "'", b:textobj_quote_sr, 'g') 44 | let l:text = substitute(l:text, '"', b:textobj_quote_dr, 'g') 45 | endif 46 | 47 | " Paste back into buffer in place of original... 48 | call setreg(textobj#quote#replace#getDefaultReg(), l:text, mode()) 49 | execute 'normal! gv"' . textobj#quote#replace#getDefaultReg() . 'p' 50 | endfunction 51 | 52 | let g:autoloaded_textobj_quote_replace = 1 53 | -------------------------------------------------------------------------------- /autoload/textobj/quote/surround.vim: -------------------------------------------------------------------------------- 1 | " ============================================================================ 2 | " File: surround.vim 3 | " Description: autoload functions for surround feature 4 | " Maintainer: preservim 5 | " Created: February 6, 2013 6 | " License: The MIT License (MIT) 7 | " ============================================================================ 8 | " 9 | if exists('g:autoloaded_textobj_quote_surround') && 10 | \ g:autoloaded_textobj_quote_surround 11 | fini 12 | en 13 | 14 | function! textobj#quote#surround#surround(mode, visual) abort 15 | " A simple alternative to Tim Pope's vim-surround 16 | " wrap word/selection in curly quotes 17 | " mode=1 is double; mode=0 is single 18 | if !exists('b:textobj_quote_dl') | return | endif 19 | if a:mode 20 | let l:l = b:textobj_quote_dl 21 | let l:r = b:textobj_quote_dr 22 | else 23 | let l:l = b:textobj_quote_sl 24 | let l:r = b:textobj_quote_sr 25 | endif 26 | if a:visual ==# 'v' 27 | " note: the gv re-establishes the visual selection that removed 28 | execute 'normal! gvc' . l:l . "\\"" . l:r ."\" 29 | elseif a:visual ==# '' 30 | execute 'normal! ciw' . l:l . "\\"" . l:r . "\" 31 | endif 32 | endfunction 33 | 34 | let g:autoloaded_textobj_quote_surround = 1 35 | -------------------------------------------------------------------------------- /doc/textobj-quote.txt: -------------------------------------------------------------------------------- 1 | *textobj-quote.txt* Support typographic quote characters. 2 | 3 | ============================================================================== 4 | CONTENTS *textobj-quote-contents* 5 | 6 | INTRODUCTION |textobj-quote-introduction| 7 | USAGE |textobj-quote-usage| 8 | INITIALIZATION |textobj-quote-initialization| 9 | MOTION COMMANDS |textobj-quote-motion-commands| 10 | EDUCATE |textobj-quote-educate| 11 | MATCHIT |textobj-quote-matchit| 12 | REPLACE |textobj-quote-replace| 13 | SURROUND |textobj-quote-surround| 14 | VIM-SURROUND |textobj-quote-vim-surround| 15 | CONFIGURATION |textobj-quote-configuration| 16 | EDUCATE CONFIGURATION |textobj-quote-config-educate| 17 | MATCHIT CONFIGURATION |textobj-quote-config-matchit| 18 | MOTION CONFIGURATION |textobj-quote-config-motion| 19 | TIPS |textobj-quote-tips| 20 | STRAIGHT QUOTES |textobj-quote-straight-quotes| 21 | DIGRAPHS |textobj-quote-digraphs| 22 | INTERNATIONAL USE |textobj-quote-international-use| 23 | SPELL CHECKING |textobj-quote-spell-checking| 24 | SEE ALSO |textobj-quote-see-also| 25 | CONTRIBUTING |textobj-quote-contributing| 26 | LICENSE |textobj-quote-license| 27 | 28 | 29 | ============================================================================== 30 | INTRODUCTION *textobj-quote-introduction* 31 | 32 | While Vim is renowned for its text manipulation capabilities, it nevertheless 33 | retains a bias towards ASCII that stretches back to vi’s roots on Unix. This 34 | can limit Vim’s appeal for those who prefer typographic characters like “curly 35 | quotes” over ASCII "straight quotes" for prose and documentation. By extending 36 | Vim’s |text-objects|, this plugin offers full and configurable support for 37 | typographic quote characters. 38 | 39 | Note: this plugin requires Kana Natsuno’s vim-textobj-user. Be sure to install 40 | that if you don’t already have it. 41 | 42 | https://github.com/kana/vim-textobj-user 43 | 44 | ============================================================================== 45 | USAGE *textobj-quote-usage* 46 | 47 | ------------------------------------------------------------------------------ 48 | INITIALIZATION *textobj-quote-initialization* 49 | 50 | You must explicitly initialize the plugin. That is, even after you install it, 51 | textobj-quote does nothing by default when you start Vim. This makes sense 52 | because you will usually write code with Vim, and typographic quotes rarely 53 | appear in code. 54 | 55 | You can initialize the plugin in several ways, depending on your needs: > 56 | " During an editing session, enter this to initialize the plugin: 57 | call textobj#quote#init() 58 | 59 | " Tell Vim when to initialize the plugin in your .vimrc: 60 | " - globally (most people wont’t want this) 61 | call textobj#quote#init() 62 | 63 | " - by |filetype| (requires 'filetype plugin on'): 64 | augroup textobj_quote 65 | autocmd! 66 | autocmd FileType markdown call textobj#quote#init() 67 | autocmd FileType textile call textobj#quote#init() 68 | autocmd FileType text call textobj#quote#init({'educate': 0}) 69 | augroup END 70 | < 71 | 72 | The last |autocmd| statement initializes the plugin for buffers of text file 73 | type, but disables the ‘educate’ feature. More on that below, in the section 74 | on configuration (|textobj-quote-config-educate|). 75 | 76 | ------------------------------------------------------------------------------ 77 | MOTION COMMANDS *textobj-quote-motion-commands* 78 | 79 | The plugin provides two text objects that work with Vim’s built-in |operator| 80 | commands. The text objects are q, for text within double typographic quotes, 81 | and Q, for text within single typographic quotes. 82 | 83 | For example, with the |c| change operator: > 84 | caq - change around “double” quotes - includes quote chars 85 | ciq - change inside “double” quotes - excludes quote chars 86 | caQ - change around ‘single’ quotes - includes quote chars 87 | ciQ - change inside ‘single’ quotes - excludes quote chars 88 | 89 | " The asterisk denotes the cursor’s position. 90 | “foo*bar” " ciq -> “*” 91 | “foo*bar” " caq -> * 92 | < 93 | 94 | In addition to |c| for change, the plugin supports |v| for visual selection, 95 | |d| for deletion, |y| for yanking to clipboard, etc. Note that |count| isn’t 96 | supported (due to limitations of the underlying vim-textobj-user plugin), but 97 | repeat with |.| should work. 98 | 99 | The plugin’s motion command is smart enough to distinguish between an 100 | apostrophe and single closing quote, even though both are represented by the 101 | same glyph. For example, try out viQ on the following sentence: 102 | 103 | ‘Really, I’d rather not relive the ’70s,’ said zombie Elvis. 104 | 105 | ------------------------------------------------------------------------------ 106 | EDUCATE *textobj-quote-educate* 107 | 108 | After the plugin is initialized your straight quote key presses (" or ') will 109 | be dynamically transformed into the appropriate typographic quote characters. 110 | 111 | For example, consider the following sentence: 112 | 113 | "It's Dr. Evil. I didn't spend six years in Evil Medical 114 | School to be called 'mister,' thank you very much." 115 | 116 | If textobj-quote is not initialized, you get straight quotes and apostrophes. 117 | 118 | Buf if textobj-quote is initialized, the straight quotes are transformed into 119 | the typographic equivalent as you type: 120 | 121 | “It’s Dr. Evil. I didn’t spend six years in Evil Medical 122 | School to be called ‘mister,’ thank you very much.” 123 | 124 | ------------------------------------------------------------------------------ 125 | MATCHIT *textobj-quote-matchit* 126 | 127 | The plugin extends |matchit| to recognize typographic quotes. This enables 128 | you to jump from one end of a quote to the other with '%' (|matchit-%|). 129 | 130 | ------------------------------------------------------------------------------ 131 | REPLACE *textobj-quote-replace* 132 | 133 | You can replace straight quotes in existing text with curly quotes, and vice 134 | versa. Add key mappings of your choice to your .vimrc: > 135 | map qc ReplaceWithCurly 136 | map qs ReplaceWithStraight 137 | < 138 | 139 | This feature supports both Normal and Visual modes. 140 | 141 | To transform all quotes in a document, use Visual mode to select all the text 142 | in the document. 143 | 144 | ------------------------------------------------------------------------------ 145 | SURROUND *textobj-quote-surround* 146 | 147 | The plugin provides an optional surround feature. Add to your .vimrc key 148 | mappings of your choice: > 149 | " NOTE: these mappings clash with the vim-surround plugin! 150 | map Sq SurroundWithDouble 151 | map SQ SurroundWithSingle 152 | < 153 | 154 | Then you can use motion commands to surround text with typographic quotes. The 155 | asterisk denotes the cursor’s position. > 156 | visSq - My senten*ce. => “My sentence.” 157 | visSQ - My senten*ce. => ‘My sentence.’ 158 | < 159 | 160 | ------------------------------------------------------------------------------ 161 | VIM-SURROUND *textobj-quote-vim-surround* 162 | 163 | If you already use Tim Pope’s vim-surround plugin, your text object key 164 | mappings should be available. For example, > 165 | cs'q - 'Hello W*orld' => “Hello World” 166 | cs"q - "Hello W*orld" => “Hello World” 167 | cs(q - (Hello W*orld) => “Hello World” 168 | cs(Q - (Hello W*orld) => ‘Hello World’ 169 | < 170 | 171 | ============================================================================== 172 | CONFIGURATION *textobj-quote-configuration* 173 | 174 | The educate, matchit, and motion features of textobj-quote have options that 175 | can be configured in initialization files or changed on the fly. In each 176 | section below, we will layout the defaults and then explain the different ways 177 | to change those defaults. 178 | 179 | ------------------------------------------------------------------------------ 180 | EDUCATE CONFIGURATION *textobj-quote-config-educate* 181 | 182 | By default the educate feature is on after you initialize the plugin. You can 183 | globally set the default to be the opposite, you can set or unset the educate 184 | feature by filetype, or you can change it on the fly. 185 | 186 | First, if you place one of the following in an initialization file, you 187 | explicitly set the default behavior for educate: > 188 | " 0 = disable: educate off by default if textobj#quote#init() is called 189 | let g:textobj#quote#educate = 0 190 | " 1 = enable: educate on by default if textobj#quote#init() is called 191 | let g:textobj#quote#educate = 1 192 | < 193 | 194 | If one of those is in your .vimrc and you call textobj#quote#init(), the 195 | educate feature will be on or off according to the explicit default. If you 196 | don’t set this variable explicitly, textobj-quote assumes you want the normal 197 | default, and educate is turned on at initialization. (Note that the variable 198 | by itself does not turn educate on or off. The user still must call the 199 | textobj#quote#init function.) 200 | 201 | You can also pass a parameter to textobj#quote#init() to customize its 202 | behavior: > 203 | autocmd FileType markdown call textobj#quote#init({'educate': 0}) 204 | autocmd FileType textile call textobj#quote#init() 205 | < 206 | 207 | Assuming the normal default, those settings turn on the educate feature for 208 | textile files but not markdown ones. 209 | 210 | You can also do the reverse: turn off educate by default, but turn it on for 211 | individual filetypes: > 212 | let g:textobj#quote#educate = 0 213 | autocmd FileType textile call textobj#quote#init({'educate': 1) 214 | < 215 | 216 | Finally, you can toggle the educate feature on the fly with the following 217 | commands: > 218 | Educate 219 | NoEducate 220 | ToggleEducate 221 | < 222 | 223 | These commands can be used during an editing session if you need to switch 224 | between modes. But they can also be used in autocommands. For example, imagine 225 | that you want typographic quotes in markdown files, but not within code 226 | sections of a markdown document. The following handles that automatically: > 227 | function! IsMarkdownCode() 228 | let line = line(".") 229 | let synstack = synstack(line, 1) 230 | let syn = empty(synstack) ? "" : synIDattr(synstack[0], "name") 231 | let is_markdown_code = syn =~# 'markdown\(Code\(Block\)\?\|Highlight*\)' 232 | return is_markdown_code 233 | endfunction 234 | 235 | augroup textobj_quote 236 | autocmd! 237 | autocmd FileType markdown call textobj#quote#init() 238 | autocmd CursorMoved,CursorMovedI,WinEnter *.md 239 | \ if IsMarkdownCode() | 240 | \ NoEducate | 241 | \ else | 242 | \ Educate | 243 | \ endif 244 | augroup END 245 | < 246 | 247 | ------------------------------------------------------------------------------ 248 | MATCHIT CONFIGURATION *textobj-quote-config-matchit* 249 | 250 | By default, the plugin provides |matchit| support, as described above in 251 | |textobj-quote-matchit|. However, you can turn this feature off globally, by 252 | filetype, or as a one off, just as with the educate feature. Since we have 253 | gone over the possibilities in detail for educate, we can be briefer here: > 254 | " Turn matchit off by default. 255 | let g:textobj#quote#matchit = 0 256 | " Turn it on in this initialization. 257 | call g:textobj#quote#init({'matchit': 1}) 258 | " Turn it on for all markdown files. 259 | autocmd FileType markdown call textobj#quote#init({'matchit': 1}) 260 | < 261 | 262 | ------------------------------------------------------------------------------ 263 | MOTION CONFIGURATION *textobj-quote-config-motion* 264 | 265 | The defaults for the typographic quote motions can also be changed in the same 266 | ways. Here are some examples: > 267 | " You can explicitly set the choices to the defaults. 268 | let g:textobj#quote#doubleMotion = 'q' 269 | let g:textobj#quote#singleMotion = 'Q' 270 | " You can reverse the defaults if you prefer, though why would you? 271 | let g:textobj#quote#doubleMotion = 'Q' 272 | let g:textobj#quote#singleMotion = 'q' 273 | " You can use the keyboard characters as defaults. 274 | let g:textobj#quote#doubleMotion = '"' 275 | let g:textobj#quote#singleMotion = "'" 276 | < 277 | 278 | ============================================================================== 279 | TIPS *textobj-quote-tips* 280 | 281 | The following sections discuss less common circumstances or requirements and 282 | how to handle them. 283 | 284 | ------------------------------------------------------------------------------ 285 | STRAIGHT QUOTES *textobj-quote-straight-quotes* 286 | 287 | In some cases, straight (ASCII) quotes are needed in a document that mostly 288 | uses typographic quotes. For example, in a book about programming: 289 | 290 | “print "Hello World!"” is a simple program you can write in Python. 291 | 292 | To insert a straight quote while educating, enter Ctrl-V (mnemonic is 293 | “verbatim”) before the quote key: 294 | 295 | Ctrl-V followed by " - straight double quote 296 | Ctrl-V followed by ' - straight single quote 297 | 298 | For more details, see |c_CTRL-V|. Also note that for units of measurement 299 | you’ll want to use the prime symbol(s) rather than straight quotes, as in: 300 | 301 | Standing at 7′3″ (2.21 m), Hasheem Thabeet of the Oklahoma City 302 | Thunder is the tallest player in the NBA. 303 | 304 | See below if you are unsure of how to enter prime symbols in Vim. 305 | 306 | ------------------------------------------------------------------------------ 307 | DIGRAPHS *textobj-quote-digraphs* 308 | 309 | Sometimes you must enter special characters (like typographic quotes) 310 | manually, such as in a search expression. You can do so through Vim’s digraphs 311 | or via your operating system’s keyboard shortcuts. 312 | 313 | | Glyph | Vim Digraph | OS X | Description 314 | | ----- | ----------- | ------------------ | ---------------------------- 315 | | ‘ | '6 | Opt-] | left single quote 316 | | ’ | '9 | Shift-Opt-] | right single quote 317 | | “ | "6 | Opt-[ | left double quote 318 | | ” | "9 | Shift-Opt-[ | right double quote 319 | | ‚ | .9 | | single low-9 quote 320 | | „ | :9 | Shift-Opt-w | double low-9 quote 321 | | ‹ | 1< | Opt-\ | left pointing single quote 322 | | › | 1> | Shift-Opt-\ | right pointing single quote 323 | | « | << | Opt-\ | left pointing double quote 324 | | » | >> | Shift-Opt-\ | right pointing double quote 325 | | ′ | 1' | | single prime 326 | | ″ | 2' | | double prime 327 | | – | -N | Opt-hyphen | en dash 328 | | — | -M | Shift-Opt-hyphen | em dash 329 | | … | .. | Opt-; | horizontal ellipsis 330 | |   | NS | | non-breaking space 331 | | ï | i: | Opt-U i | lowercase i, umlaut 332 | | æ | ae | Opt-' | lowercase ae 333 | 334 | To enter the Vim digraphs, you need to precede them with Ctrl-K. For example, 335 | Ctrl-K followed by "6 will enter ”. For more information, see |digraphs| and 336 | |c_CTRL-K|. 337 | 338 | ------------------------------------------------------------------------------ 339 | INTERNATIONAL USE *textobj-quote-international-use* 340 | 341 | Many international keyboards feature keys to allow you to input typographic 342 | quote characters directly. In such cases, you won’t need to change the 343 | behavior of the straight quote keys. 344 | 345 | But if you do, you can override the defaults. For example, those users editing 346 | most of their prose in German could change those defaults to: > 347 | let g:textobj#quote#doubleDefault = '„“' " „doppel“ 348 | let g:textobj#quote#singleDefault = '‚‘' " ‚einzel‘ 349 | < 350 | 351 | Or on a file type initialization... > 352 | augroup textobj_quote 353 | autocmd! 354 | autocmd FileType markdown call textobj#quote#init({ 'double':'„“', 'single':'‚‘' }) 355 | ... 356 | augroup END 357 | < 358 | 359 | Or in a key mapping... > 360 | nnoremap qd :call textobj#quote#init({ 'double':'„“', 'single':'‚‘' }) 361 | < 362 | 363 | ============================================================================== 364 | SPELL CHECKING *textobj-quote-spell-checking* 365 | 366 | Vim’s spell checking may not support words with typographical apostrophes. For 367 | example, Vim may consider “isn’t” to be misspelled, even though it isn’t. 368 | There are several ways to address this, but here is one that works well. (The 369 | example uses English spelling files, but you can substitute any language.) 370 | 371 | First, you need to get spelling files for vim to build a dictionary. (See 372 | |aff-dic-format|.) For example, you can find some at the following link. 373 | 374 | https://extensions.libreoffice.org/en/extensions/show/english-dictionaries 375 | 376 | Copy or unpack those files to ~/.vim/spell and run this perl one-liner: > 377 | perl -i.bak -ne "print; print if s/'/’/g" en_US.dic 378 | < 379 | 380 | Then open Vim and do the following: > 381 | :mkspell en en_US 382 | " Use mkspell! if you already have a dictionary with that name. 383 | " See |mkspell| for further details of this command. 384 | < 385 | 386 | This method creates two entries for all words with apostrophes in them: one 387 | with a straight apostrophe and one with a typographic apostrophe. Thus, Vim 388 | will consider both “isn't” and “isn’t” to be correctly spelled. Which is great 389 | because they both are! Now you can type one version in code comments and 390 | another in an email, and Vim won’t bother you. 391 | 392 | ============================================================================== 393 | SEE ALSO *textobj-quote-see-also* 394 | 395 | The following plugins have some overlap with textobj-quote: 396 | 397 | - vim-sandwich: operator and textobject plugins to add/delete/replace 398 | surroundings of a sandwiched textobject, like (foo), "bar", and 399 | “buzz”. 400 | https://github.com/machakann/vim-sandwich 401 | - vim-surround: quoting/parenthesizing made simple 402 | https://github.com/tpope/vim-surround 403 | 404 | ============================================================================== 405 | CONTRIBUTING *textobj-quote-contributing* 406 | 407 | If you’ve spotted a problem or have an idea to improve this plugin, please 408 | post on the project’s GitHub issue page. 409 | 410 | https://github.com/preservim/vim-textobj-quote/issues 411 | 412 | We would especially like to integrate this plugin better with existing 413 | surround plugins. 414 | 415 | ============================================================================== 416 | LICENSE *textobj-quote-license* 417 | 418 | The MIT License (MIT) 419 | 420 | Copyright (c) 2013-2020 Reed Esau 421 | 422 | Permission is hereby granted, free of charge, to any person obtaining a copy 423 | of this software and associated documentation files (the "Software"), to deal 424 | in the Software without restriction, including without limitation the rights 425 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 426 | copies of the Software, and to permit persons to whom the Software is 427 | furnished to do so, subject to the following conditions: 428 | 429 | The above copyright notice and this permission notice shall be included in all 430 | copies or substantial portions of the Software. 431 | 432 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 433 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 434 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 435 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 436 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 437 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 438 | SOFTWARE. 439 | 440 | vim:tw=78:ts=8:noet:ft=help:norl 441 | -------------------------------------------------------------------------------- /plugin/textobj/quote.vim: -------------------------------------------------------------------------------- 1 | " ============================================================================ 2 | " File: textobj_quote.vim 3 | " Description: load functions for vim-textobj-quote plugin 4 | " Maintainer: preservim 5 | " Created: February 6, 2013 6 | " License: The MIT License (MIT) 7 | " ============================================================================ 8 | 9 | scriptencoding utf-8 10 | 11 | if exists('g:loaded_textobj_quote') | finish | endif 12 | 13 | if !exists('g:textobj#quote#doubleMotion') 14 | let g:textobj#quote#doubleMotion = 'q' 15 | endif 16 | if !exists('g:textobj#quote#singleMotion') 17 | let g:textobj#quote#singleMotion = 'Q' 18 | endif 19 | 20 | if !exists('g:textobj#quote#move_p') 21 | let g:textobj#quote#move_p = '' 22 | endif 23 | 24 | if !exists('g:textobj#quote#move_n') 25 | let g:textobj#quote#move_n = '' 26 | endif 27 | 28 | if !exists('g:textobj#quote#move_P') 29 | let g:textobj#quote#move_P = '' 30 | endif 31 | 32 | if !exists('g:textobj#quote#move_N') 33 | let g:textobj#quote#move_N = '' 34 | endif 35 | 36 | let g:textobj#quote#doubleStandard = '“”' 37 | let g:textobj#quote#singleStandard = '‘’' 38 | 39 | if !exists('g:textobj#quote#doubleDefault') 40 | " “double” 41 | let g:textobj#quote#doubleDefault = g:textobj#quote#doubleStandard 42 | endif 43 | if !exists('g:textobj#quote#singleDefault') 44 | " ‘single’ 45 | let g:textobj#quote#singleDefault = g:textobj#quote#singleStandard 46 | endif 47 | 48 | " enable/disable features 49 | if !exists('g:textobj#quote#matchit') 50 | let g:textobj#quote#matchit = 1 51 | endif 52 | if !exists('g:textobj#quote#educate') 53 | let g:textobj#quote#educate = 1 54 | endif 55 | 56 | " needed to match pairs of quotes (via tpope/vim-sensible) 57 | if g:textobj#quote#matchit && 58 | \ !exists('g:loaded_matchit') && 59 | \ findfile('plugin/matchit.vim', &runtimepath) ==# '' 60 | runtime! macros/matchit.vim 61 | endif 62 | 63 | " commands to toggle key mappings 64 | command! -nargs=0 -bar Educate call textobj#quote#educate#mapKeys(1) 65 | command! -nargs=0 -bar NoEducate call textobj#quote#educate#mapKeys(0) 66 | command! -nargs=0 -bar ToggleEducate call textobj#quote#educate#toggleMappings() 67 | 68 | " replace quotes in bulk 69 | nnoremap ReplaceWithCurly :call textobj#quote#replace#replace(1, '') 70 | vnoremap ReplaceWithCurly :call textobj#quote#replace#replace(1, visualmode()) 71 | nnoremap ReplaceWithStraight :call textobj#quote#replace#replace(0, '') 72 | vnoremap ReplaceWithStraight :call textobj#quote#replace#replace(0, visualmode()) 73 | 74 | " a simple alterative to tpope/vim-surround 75 | nnoremap SurroundWithDouble :call textobj#quote#surround#surround(1, '') 76 | vnoremap SurroundWithDouble :call textobj#quote#surround#surround(1, visualmode()) 77 | nnoremap SurroundWithSingle :call textobj#quote#surround#surround(0, '') 78 | vnoremap SurroundWithSingle :call textobj#quote#surround#surround(0, visualmode()) 79 | 80 | let g:loaded_textobj_quote = 1 81 | -------------------------------------------------------------------------------- /run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | VIMRC="$TMPDIR/mini-vimrc" 4 | 5 | cat > $VIMRC << EOF 6 | set nocompatible 7 | syntax on 8 | set shortmess+=I 9 | 10 | for dep in ['vader.vim', 'vim-textobj-user'] 11 | execute 'set rtp+=' . finddir(dep, expand('~/.vim').'/**') 12 | endfor 13 | set rtp+=. 14 | EOF 15 | 16 | vim -u $VIMRC "+Vader spec/*" 17 | #vim -u $VIMRC '+Vader!*' && echo Success || echo Failure 18 | 19 | rm -f $VIMRC 20 | -------------------------------------------------------------------------------- /spec/educate.vader: -------------------------------------------------------------------------------- 1 | Execute (Clean up test environment): 2 | call textobj#quote#init({'educate':1}) 3 | 4 | ########################################################### 5 | 6 | Given: 7 | 8 | Do (basic educate): 9 | a"It's 'Dr.' Evil, thank you very much." 10 | 11 | Expect: 12 | “It’s ‘Dr.’ Evil, thank you very much.” 13 | 14 | ########################################################### 15 | 16 | Given: 17 | 18 | Do (basic educate on preservim/vim-textobj#quote/issues/11): 19 | ar' 20 | 21 | Expect: 22 | r’ 23 | 24 | ########################################################### 25 | 26 | Given: 27 | 28 | Do (basic educate on alpha chars): 29 | a'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z' 'A' 'B' 'C' 'D' 'E' 'F' 'G' 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O' 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' 'X' 'Y' 'Z' 30 | 31 | Expect: 32 | ‘a’ ‘b’ ‘c’ ‘d’ ‘e’ ‘f’ ‘g’ ‘h’ ‘i’ ‘j’ ‘k’ ‘l’ ‘m’ ‘n’ ‘o’ ‘p’ ‘q’ ‘r’ ‘s’ ‘t’ ‘u’ ‘v’ ‘w’ ‘x’ ‘y’ ‘z’ ‘A’ ‘B’ ‘C’ ‘D’ ‘E’ ‘F’ ‘G’ ‘H’ ‘I’ ‘J’ ‘K’ ‘L’ ‘M’ ‘N’ ‘O’ ‘P’ ‘Q’ ‘R’ ‘S’ ‘T’ ‘U’ ‘V’ ‘W’ ‘X’ ‘Y’ ‘Z’ 33 | 34 | ########################################################### 35 | 36 | Given: 37 | 38 | Do (educate double with various previous chars): 39 | a" (" )" {" [" ]" &" ." ?" !" ," ;" '" ‘" ’" “" ”" 40 | 41 | Expect: 42 | “ (“ )” {“ [“ ]” &“ .” ?” !” ,” ;” ‘“ ‘“ ’” “” ”” 43 | 44 | ########################################################### 45 | 46 | Given: 47 | 48 | Do (educate single quote with various previous chars): 49 | a' (' )' {' [' ]' &' .' ?' !' ,' ;' '' ‘' ’' “' ”' 50 | 51 | Expect: 52 | ‘ (‘ )’ {‘ [‘ ]’ &‘ .’ ?’ !’ ,’ ;’ ‘’ ‘’ ’’ “‘ ”’ 53 | 54 | ########################################################### 55 | 56 | -------------------------------------------------------------------------------- /spec/intl.vader: -------------------------------------------------------------------------------- 1 | Execute (Clean up test environment): 2 | call textobj#quote#init({'educate':1}) 3 | 4 | ########################################################### 5 | 6 | Given: 7 | 8 | Do (educate with alternative quote characters): 9 | :call textobj#quote#init({ 'double':'„“', 'single':'‚‘' })\ 10 | a"Es ist 'Doktor' Böse, ich danke Ihnen sehr." 11 | 12 | Expect: 13 | „Es ist ‚Doktor‘ Böse, ich danke Ihnen sehr.“ 14 | 15 | ########################################################### 16 | -------------------------------------------------------------------------------- /spec/match.vader: -------------------------------------------------------------------------------- 1 | Execute (Clean up test environment): 2 | let b:match_words='' 3 | call textobj#quote#init() 4 | 5 | ########################################################### 6 | 7 | Given: 8 | (Simple parentheses) 9 | 10 | Do (simple parentheses): 11 | %rX 12 | 13 | Expect: 14 | (Simple parenthesesX 15 | 16 | ########################################################### 17 | 18 | Given: 19 | “It’s ‘Dr.’ Evil, thank you very much.” 20 | 21 | Do (double match): 22 | %rX 23 | 24 | Expect: 25 | “It’s ‘Dr.’ Evil, thank you very much.X 26 | 27 | ########################################################### 28 | 29 | Given: 30 | ‘It’s “Dr.” Evil, thank you very much.’ 31 | 32 | Do (single match): 33 | %rX 34 | 35 | Expect: 36 | ‘It’s “Dr.” Evil, thank you very much.X 37 | 38 | ########################################################### 39 | 40 | Given: 41 | ‘Really, I’d rather not relive the ’70s,’ said zombie Elvis. 42 | 43 | Do (single match with contraction): 44 | %rX 45 | 46 | Expect: 47 | ‘Really, I’d rather not relive the ’70s,X said zombie Elvis. 48 | 49 | -------------------------------------------------------------------------------- /spec/replace.vader: -------------------------------------------------------------------------------- 1 | Execute (Clean up test environment): 2 | call textobj#quote#init({'replace':1}) 3 | map ; ReplaceWithCurly 4 | map \ ReplaceWithStraight 5 | 6 | ########################################################### 7 | Given: 8 | "It's 'Dr.' Evil, thank you very much," he said. Then I said, "What do you 9 | know about it anyhow. You aren't very nice, are you?" Then he stormed off, 10 | and I haven't seen him since. 11 | 12 | Do (Curl quotes in normal mode): 13 | ; 14 | 15 | Expect: 16 | “It’s ‘Dr.’ Evil, thank you very much,” he said. Then I said, “What do you 17 | know about it anyhow. You aren’t very nice, are you?” Then he stormed off, 18 | and I haven’t seen him since. 19 | 20 | ########################################################### 21 | Given: 22 | “It’s ‘Dr.’ Evil, thank you very much,” he said. Then I said, “What do you 23 | know about it anyhow. You aren’t very nice, are you?” Then he stormed off, 24 | and I haven’t seen him since. 25 | 26 | Do (Straighten quotes in normal mode): 27 | \ 28 | 29 | Expect: 30 | "It's 'Dr.' Evil, thank you very much," he said. Then I said, "What do you 31 | know about it anyhow. You aren't very nice, are you?" Then he stormed off, 32 | and I haven't seen him since. 33 | 34 | 35 | Given: 36 | "It's 'Dr.' Evil, thank you very much," he said. Then I said, "What do you 37 | know about it anyhow. You aren't very nice, are you?" Then he stormed off, 38 | and I haven't seen him since. 39 | 40 | Do (Curl quotes in visual mode): 41 | vip; 42 | 43 | Expect: 44 | “It’s ‘Dr.’ Evil, thank you very much,” he said. Then I said, “What do you 45 | know about it anyhow. You aren’t very nice, are you?” Then he stormed off, 46 | and I haven’t seen him since. 47 | 48 | 49 | Given: 50 | “It’s ‘Dr.’ Evil, thank you very much,” he said. Then I said, “What do you 51 | know about it anyhow. You aren’t very nice, are you?” Then he stormed off, 52 | and I haven’t seen him since. 53 | 54 | Do (Straighten quotes in visual mode): 55 | vip\ 56 | 57 | Expect: 58 | "It's 'Dr.' Evil, thank you very much," he said. Then I said, "What do you 59 | know about it anyhow. You aren't very nice, are you?" Then he stormed off, 60 | and I haven't seen him since. 61 | 62 | -------------------------------------------------------------------------------- /spec/select.vader: -------------------------------------------------------------------------------- 1 | Execute (Clean up test environment): 2 | call textobj#quote#init() 3 | 4 | ########################################################### 5 | 6 | Given: 7 | ‘It’s “Dr.” Evil, thank you very much.’ 8 | 9 | Do (select single): 10 | viQU 11 | 12 | Expect: 13 | ‘IT’S “DR.” EVIL, THANK YOU VERY MUCH.’ 14 | 15 | ########################################################### 16 | 17 | Given: 18 | ‘It’s “Dr.” Evil, thank you very much.’ 19 | 20 | Do (select after double quotes): 21 | /thank\viQU 22 | 23 | Expect: 24 | ‘IT’S “DR.” EVIL, THANK YOU VERY MUCH.’ 25 | 26 | ########################################################### 27 | 28 | Given: 29 | ‘It’s “Dr.” Evil, thank you very much.’ 30 | 31 | Do (select in double quotes): 32 | /Dr\viqU 33 | 34 | Expect: 35 | ‘It’s “DR.” Evil, thank you very much.’ 36 | 37 | ########################################################### 38 | 39 | Given: 40 | “It’s Dr. Evil. I didn’t spend six years in Evil Medical 41 | School to be called ‘mister,’ thank you very much.” 42 | 43 | Do (select over multiple lines): 44 | /thank\viqU 45 | 46 | Expect: 47 | “IT’S DR. EVIL. I DIDN’T SPEND SIX YEARS IN EVIL MEDICAL 48 | SCHOOL TO BE CALLED ‘MISTER,’ THANK YOU VERY MUCH.” 49 | 50 | ########################################################### 51 | 52 | Given: 53 | “It’s Dr. Evil. I didn’t spend six years in ‘Evil Medical 54 | School’ to be called ‘mister,’ thank you very much.” 55 | 56 | Do (select single over multiple lines): 57 | /Medical\viQU 58 | 59 | Expect: 60 | “It’s Dr. Evil. I didn’t spend six years in ‘EVIL MEDICAL 61 | SCHOOL’ to be called ‘mister,’ thank you very much.” 62 | 63 | ########################################################### 64 | 65 | Given: 66 | ‘Really, I’d rather not relive the ’70s,’ said zombie Elvis. 67 | 68 | Do (select single over multiple lines): 69 | viQU 70 | 71 | Expect: 72 | ‘REALLY, I’D RATHER NOT RELIVE THE ’70S,’ said zombie Elvis. 73 | 74 | ########################################################### 75 | 76 | Given: 77 | hello 78 | Do (select where there is no match): 79 | daq 80 | 81 | Expect: 82 | hello 83 | 84 | -------------------------------------------------------------------------------- /spec/surround.vader: -------------------------------------------------------------------------------- 1 | Execute (Clean up test environment): 2 | call textobj#quote#init({'surround':1}) 3 | map ; SurroundWithDouble 4 | map \ SurroundWithSingle 5 | 6 | ########################################################### 7 | 8 | Given: 9 | This is a test. Another. 10 | 11 | Do (Visual surround double): 12 | vis; 13 | 14 | Expect: 15 | “This is a test.” Another. 16 | 17 | ########################################################### 18 | 19 | Given: 20 | This is a test. Another. 21 | 22 | Do (Visual surround single): 23 | vis\ 24 | 25 | Expect: 26 | ‘This is a test.’ Another. 27 | 28 | ########################################################### 29 | 30 | Given: 31 | This is a test. 32 | 33 | Do (Non-visual surround double): 34 | w; 35 | 36 | Expect: 37 | This “is” a test. 38 | 39 | ########################################################### 40 | 41 | Given: 42 | This is a test. 43 | 44 | Do (Non-visual surround single): 45 | w\ 46 | 47 | Expect: 48 | This ‘is’ a test. 49 | 50 | --------------------------------------------------------------------------------