~telemachus/vim-textobj-curly

7c236f8f14c33269e1cfec1dc1af0914b47d155f — Peter Aronoff 1 year, 1 month ago
Initial upload
A  => .gitignore +2 -0
@@ 1,2 @@
_vader.vim
_vtou

A  => CHANGES.md +5 -0
@@ 1,5 @@
# vim-textobj-curly history

## 0.0.1 (June 2020)

+ First public upload

A  => LICENSE.txt +29 -0
@@ 1,29 @@
Copyright (c) 2020 Peter Aronoff

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the
   distribution.

3. Neither the name of the copyright holder nor the names of its
   contributors may be used to endorse or promote products derived
   from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

A  => README.md +149 -0
@@ 1,149 @@
# textobj-curly: text objects and movements for “curly quotes”

## Introduction

This plugin provides text objects for text inside “double curly
quotes” and ‘single curly quotes.’ It also makes it easier to move
through text that contains curly quotes. (The information in the
README may be abbreviated. See the documentation for fullest details.)

## Installation

This plugin relies on [vim-textobj-user][vtu]. You must also install
that plugin.

Neither [vim-textobj-user][vtu] nor this plugin should require any
special treatment. You can use your favorite Vim plugin manager. For
neovim and recent versions of vim, you should also be able to simply
clone the directories in your [pack path][pack].

[vtu]:  https://github.com/kana/vim-textobj-user
[pack]: https://vimhelp.org/usr_05.txt.html#05.5

## Text Objects

| lhs | rhs |
| :--- | ---: |
| `ic` | inside double curly quotes |
| `ac` | inside double curly quotes and the quotes themselves |
| `iC` | inside single curly quotes |
| `aC` | inside single curly quotes and the quotes themselves |

Examples:

```
" Cursor is at |
“This is not| a love song.” 
cic
“|”
“T|his is not a love song.” 
dac
|
‘This i|sn’t a love song either.’
diC
‘|’
```

In the last example, the text object looks past the apostrophe to find
the entire quotation.

## Movements

| lhs | rhs |
| :--- | ---: |
| `]c` | move to the next double curly quotes |
| `[c` | move to the previous double curly quotes |
| `]C` | move to the next single curly quotes |
| `[C` | move to the previous single curly quotes |

Examples:

```
" Cursor is at |
I| said, “This is not a love song.” But someone replied
angrily, “It is! It *is* a love song!”
]c
I said, “|This is not a love song.” But someone replied
angrily, “It is! It *is* a love song!”
]c
I said, “This is not a love song.” But someone replied
angrily, “|It is! It *is* a love song!”
[c
I said, “|This is not a love song.” But someone replied
angrily, “It is! It *is* a love song!”
```

## Configuration

If you don’t want the default mappings, add this to your vim
startup files: `let g:textobj_curly_no_default_key_mappings=1`.


Then you can create whatever mappings suit you. For example:

```
" If you like q (for ‘quote’) more than c (for ‘curly’):
omap aq <Plug>(textobj-curly-double-a)
omap iq <Plug>(textobj-curly-double-i)
xmap aQ <Plug>(textobj-curly-single-a)
xmap iQ <Plug>(textobj-curly-single-i)
map ]q <Plug>(textobj-curly-double-n)
map [q <Plug>(textobj-curly-double-p)
map ]Q <Plug>(textobj-curly-single-n)
map [Q <Plug>(textobj-curly-single-p)
```

## See Also

There are two plugins that do nearly all of what this one does, plus
a whole lot more. I wrote this to learn, and also because I wanted
something more minimal. But I encourage you to take a look at
[vim-textobj-quote][vtq] and [vim-sandwich][vs], especially if you
want further features.

[vtq]: https://github.com/reedes/vim-textobj-quote
[vs]: https://github.com/machakann/vim-sandwich

## Contributing

If you find a bug, I would love to hear about it. Please email me at
<peter@aronoff.org>. I would also love to hear from you if you have
a better way to do something that the plugin already does. (I’m no
expert at VimL, and I’m always glad to learn more.)

You’re welcome to contact me if you want a new feature, but I’m not
likely to say yes. My goal was to make this as minimal as possible.
The plugin has the features that I consider essential, and I’m
unlikely to add any others.


## Credits

I took a great deal of inspiration—as well as some code—from
[vim-textobj-quote][vtq] and [vim-sandwich][vs]. I thank Reed Es and
machakann for their work on those.

Kana Natsuno’s [vim-textobj-user][vtu] does almost all the heavy
lifting for this plugin. [vim-textobj-user][vtu] makes it extremely
simple to create new [text objects][to]. I encourage you to check it
out, as well as [the listing of all the new text objects that Kana and
others have built with it][vtuwiki]. I am very grateful to Kana for
his many plugins.

[vtq]: https://github.com/reedes/vim-textobj-quote/
[vs]: https://github.com/machakann/vim-sandwich/
[vtu]: https://github.com/kana/vim-textobj-user/
[to]: https://vimhelp.org/usr_04.txt.html#04.8
[vtuwiki]: https://github.com/kana/vim-textobj-user/wiki/

Thanks also to June Gunn for [vader.vim][vader]. He made it almost fun
to test VimL.

[vader]: https://github.com/junegunn/vader.vim/

## License

[BSD 3-Clause license][bsd3]: for details see LICENSE.txt in this repo
or the version online.

[bsd3]: https://opensource.org/licenses/BSD-3-Clause

A  => autoload/textobj/curly.vim +77 -0
@@ 1,77 @@
" textobj-curly - Text objects and movements for curly quotes
" Version: 0.0.1
" License: BSD 3-Clause License
" Copyright (c) 2020 Peter Aronoff
"
" Redistribution and use in source and binary forms, with or without
" modification, are permitted provided that the following conditions
" are met:
"
" 1. Redistributions of source code must retain the above copyright
"    notice, this list of conditions and the following disclaimer.
"
" 2. Redistributions in binary form must reproduce the above copyright
"    notice, this list of conditions and the following disclaimer in
"    the documentation and/or other materials provided with the
"    distribution.
"
" 3. Neither the name of the copyright holder nor the names of its
"    contributors may be used to endorse or promote products derived
"    from this software without specific prior written permission.
"
" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
" FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
" COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
" CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
" POSSIBILITY OF SUCH DAMAGE.
scriptencoding utf-8
function! textobj#curly#move_pd() abort
    let l:start = searchpos('\m\C“\_.\{-}”', 'bn')
    if l:start[0] == 0
        return 0
    else
        let l:end = searchpos('\m\C“\_.\{-}\zs”', 'bn')
        return ['v', [0, l:start[0], l:start[1], 0],
                    \ [0, l:end[0], l:end[1], 0 ] ]
    endif
endfunction

function! textobj#curly#move_nd() abort
    let l:start = searchpos('\m\C“\_.\{-}”', 'n')
    if l:start[0] == 0
        return 0
    else
        let l:end = searchpos('\m\C“\_.\{-}\zs”', 'n')
        return ['v', [0, l:start[0], l:start[1], 0],
                    \ [0, l:end[0], l:end[1], 0 ] ]
    endif
endfunction

function! textobj#curly#move_ps() abort
    let l:start = searchpos('\m\C‘\_.\{-}’\(\w\)\@!', 'bn')
    if l:start[0] == 0
        return 0
    else
        let l:end = searchpos('\m\C‘\_.\{-}\zs’\(\w\)\@!', 'bn')
        return ['v', [0, l:start[0], l:start[1], 0],
                    \ [0, l:end[0], l:end[1], 0 ] ]
    endif
endfunction

function! textobj#curly#move_ns() abort
    let l:start = searchpos('\m\C‘\_.\{-}’\(\w\)\@!', 'n')
    if l:start[0] == 0
        return 0
    else
        let l:end = searchpos('\m\C‘\_.\{-}\zs’\(\w\)\@!', 'n')
        return ['v', [0, l:start[0], l:start[1], 0],
                    \ [0, l:end[0], l:end[1], 0 ] ]
    endif
endfunction

A  => doc/textobj-curly.txt +213 -0
@@ 1,213 @@
*textobj-curly.txt*	text-obj-curly	Last change: 2020-06-06

Version 0.0.1
Copyright (c) 2020 Peter Aronoff <peter@aronoff.org>
License BSD 3-Clause (see below or LICENSE for details)

======================================================================
CONTENTS				*textobj-curly-contents*

	INTRODUCTION			|textobj-curly-introduction|
	TEXT OBJECTS			|textobj-curly-text-objects|
	MOVEMENTS			|textobj-curly-movements|
	CONFIGURATION			|textobj-curly-configuration|
	TIPS				|textobj-curly-tips|
	LIMITATIONS			|textobj-curly-limitations|
	SEE ALSO			|textobj-curly-see-also|
	CONTRIBUTING			|textobj-curly-contributing|
	THANKS				|textobj-curly-thanks|
	CHANGES				|textobj-curly-changes|
	LICENSE				|textobj-curly-license|


======================================================================
INTRODUCTION				*textobj-curly-introduction*

This plugin provides text objects for text inside “double curly
quotes” and ‘single curly quotes.’ It also makes it easier to move
through text with many curly quotes.

======================================================================
TEXT OBJECTS				*textobj-text-objects*

	{lhs}	{rhs}				~
	----	---------------------------------
	ic	inside double curly quotes
	ac	inside double curly quotes and the quotes themselves
	iC	inside single curly quotes
	aC	inside single curly quotes and the quotes themselves

Examples: >
	" Cursor is at |
	“This is not| a love song.” 
	cic
	“|”
	“T|his is not a love song.” 
	dac
	|
	‘This i|sn’t a love song either.’
	diC
	‘|’
<

In the last example, the text object looks past the apostrophe to find
the entire quotation.

======================================================================
MOVEMENTS				*textobj-curly-movements*

	{lhs}	{rhs}				~
	----	---------------------------------
	]c	move to the next double curly quotes
	[c	move to the previous double curly quotes
	]C	move to the next single curly quotes
	[C	move to the previous single curly quotes

Examples: >
	" Cursor is at |
	I| said, “This is not a love song.” But someone replied
	angrily, “It is! It *is* a love song!”
	]c
	I said, “|This is not a love song.” But someone replied
	angrily, “It is! It *is* a love song!”
	]c
	I said, “This is not a love song.” But someone replied
	angrily, “|It is! It *is* a love song!”
	[c
	I said, “|This is not a love song.” But someone replied
	angrily, “It is! It *is* a love song!”
<

======================================================================
CONFIGURATION				*textobj-curly-configuration*

If you don’t want the default mappings, add this to your Vim
configuration: >
	let g:textobj_curly_no_default_key_mappings=1
<

Then you can create whatever mappings suit you. For example: >
	" If you like q (for ‘quote’) more than c (for ‘curly’):
	omap aq <Plug>(textobj-curly-double-a)
	omap iq <Plug>(textobj-curly-double-i)
	xmap aQ <Plug>(textobj-curly-single-a)
	xmap iQ <Plug>(textobj-curly-single-i)
	map ]q <Plug>(textobj-curly-double-n)
	map [q <Plug>(textobj-curly-double-p)
	map ]Q <Plug>(textobj-curly-single-n)
	map [Q <Plug>(textobj-curly-single-p)
<

======================================================================
TIPS					*textobj-curly-tips*

If you want |matchit| support for curly quotes, you can add something
like the following to your startup files: >

	if exists('b:match_words')
	 	let b:match_words.=',“:”,‘:’\(\w\)\@!'
	else
		let b:match_words='“:”,‘:’\(\w\)\@!'
	endif
<

See |b:match_words| for more details.

======================================================================
LIMITATIONS				*textobj-curly-limitations*

Neither the text objects nor the movements support a |count|. (This is
a result of the same limitation in the plugin that underlies this one,
vim-textojb-user.)

======================================================================
SEE ALSO				*textobj-curly-see-also*

There are two plugins that do nearly all of what this one does, plus
a whole lot more. I wrote this to learn, and also because I wanted
something more minimal. But I encourage you to take a look at
vim-textobj-quote and vim-sandwich, especially if you want further
features.

	<https://github.com/reedes/vim-textobj-quote>
	<https://github.com/machakann/vim-sandwich>

======================================================================
CONTRIBUTING				*textobj-curly-contributing*

If you find a bug, I would love to hear about it. Please email me at
<peter@aronoff.org>. I would also love to learn if you have a better
way to do something. (I am no expert at VimL, but I hope to improve.)

You’re welcome to contact me if you want a new feature, but I’m not
likely to say yes. My goal was to make this as minimal as possible.
The plugin has the features that I consider essential, and I don’t
plan to add any others.

======================================================================
CREDITS					*textobj-curly-credits*

I took a great deal of inspiration—as well as some code—from
vim-textobj-quote and vim-sandwich. I thank Reed Es and machakann for
their work.

	<https://github.com/reedes/vim-textobj-quote>
	<https://github.com/machakann/vim-sandwich>

Kana Natsuno’s vim-textobj-user does almost all the heavy lifting
here. vim-textobj-user makes it extremely simple to create new text
objects.  I encourage you to check it out, as well as the listing of
all the text objects that Kana and others have built with it. I’m very
grateful to Kana for his many plugins.

	<https://github.com/kana/vim-textobj-user>
	<https://github.com/kana/vim-textobj-user/wiki>

Thanks also to June Gunn for vader.vim. He made it almost fun to test
VimL.

	<https://github.com/junegunn/vader.vim>

======================================================================
CHANGES					*textobj-curly-changes*

	0.0.1				2020-06-06

======================================================================
LICENSE					*textobj-curly-license*

BSD 3-Clause License {{{
	Redistribution and use in source and binary forms, with or
	without modification, are permitted provided that the
	following conditions are met:

	1. Redistributions of source code must retain the above
	   copyright notice, this list of conditions and the following
	   disclaimer.

	2. Redistributions in binary form must reproduce the above
	   copyright notice, this list of conditions and the following
	   disclaimer in the documentation and/or other materials
	   provided with the distribution.

	3. Neither the name of the copyright holder nor the names of
	   its contributors may be used to endorse or promote products
	   derived from this software without specific prior written
	   permission.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
	CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
	MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
	DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
	CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
	OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
	EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}}}

vim:tw=70:ts=8:noet:ft=help:norl

A  => no-mappings-vimrc +10 -0
@@ 1,10 @@
filetype off
set runtimepath+=_vader.vim
set runtimepath+=_vtou
set runtimepath+=.
filetype plugin indent on
syntax enable
set nomore
set noswapfile
set viminfo=
let g:textobj_curly_no_default_key_mappings=1

A  => nvim-test +240 -0
@@ 1,240 @@
NVIM v0.5.0-550-g39f802bef
Build type: Release
LuaJIT 2.1.0-beta3
Compilation: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -O2 -DNDEBUG -Wall -Wextra -pedantic -Wno-unused-parameter -Wstrict-prototypes -std=gnu99 -Wshadow -Wconversion -Wmissing-prototypes -Wimplicit-fallthrough -Wvla -fstack-protector-strong -fno-common -fdiagnostics-color=always -DINCLUDE_GENERATED_DECLARATIONS -D_GNU_SOURCE -DNVIM_MSGPACK_HAS_FLOAT32 -DNVIM_UNIBI_HAS_VAR_FROM -DMIN_LOG_LEVEL=3 -I/Users/telemachus/Downloads/src/neovim/build/config -I/Users/telemachus/Downloads/src/neovim/src -I/Users/telemachus/Downloads/src/neovim/.deps/usr/include -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include -I/usr/local/opt/gettext/include -I/Users/telemachus/Downloads/src/neovim/build/src/nvim/auto -I/Users/telemachus/Downloads/src/neovim/build/include
Compiled by telemachus@mbpr1315-3192.local

Features: +acl +iconv +tui
See ":help feature-compile"

   system vimrc file: "$VIM/sysinit.vim"
  fall-back for $VIM: "/Users/telemachus/local/neovim/share/nvim"

Run :checkhealth for more info

Starting Vader: 1 suite(s), 46 case(s)
  Starting Vader: /Users/telemachus/Documents/git-repos/vim-textobj-curly/test/basics.vader
    ( 1/46) [  GIVEN] a straightforward double curly quote
    ( 1/46) [     DO] select inside double curly quotes
    ( 1/46) [ EXPECT] an uppercase curly quote
    ( 2/46) [  GIVEN] a straightforward double curly quote
    ( 2/46) [     DO] change text inside the double curly quotes
    ( 2/46) [ EXPECT] the new text
    ( 3/46) [  GIVEN] a straightforward double curly quote
    ( 3/46) [     DO] delete text inside the double curly quotes
    ( 3/46) [ EXPECT] only the quotes to remain
    ( 4/46) [  GIVEN] a double curly quote with an apostrophe inside of it
    ( 4/46) [     DO] select inside double curly quote and make it uppercase
    ( 4/46) [ EXPECT] an uppercase curly quote
    ( 5/46) [  GIVEN] no curly quotes
    ( 5/46) [     DO] make the whole curly quote uppercase
    ( 5/46) [ EXPECT] no change
    ( 6/46) [  GIVEN] no curly quotes
    ( 6/46) [     DO] delete the quote
    ( 6/46) [ EXPECT] no change
    ( 7/46) [  GIVEN] a double curly quote inside of a single curly quote
    ( 7/46) [     DO] select the inner double curly quote and make it uppercase
    ( 7/46) [ EXPECT] an uppercase inner double curly quote
    ( 8/46) [  GIVEN] a double curly quote inside of a single curly quote
    ( 8/46) [     DO] change the inner double curly quote
    ( 8/46) [ EXPECT] an uppercase inner double curly quote
    ( 9/46) [  GIVEN] a double curly quote inside of a single curly quote
    ( 9/46) [     DO] delete the inner double curly quote
    ( 9/46) [ EXPECT] an empty inner double curly quote
    (10/46) [  GIVEN] a two-line double curly quote
    (10/46) [     DO] select the entire curly quote and make it uppercase
    (10/46) [ EXPECT] an uppercase curly quote
    (11/46) [  GIVEN] a two-line double curly quote
    (11/46) [     DO] change the whole curly quote
    (11/46) [ EXPECT] the new curly quote
    (12/46) [  GIVEN] a two-line double curly quote
    (12/46) [     DO] delete everything inside the curly quote
    (12/46) [ EXPECT] only the quotes to remain
    (13/46) [  GIVEN] a two-line double curly quote
    (13/46) [     DO] move to the second line, select the quote, and make it uppercase
    (13/46) [ EXPECT] an uppercase curly quote
    (14/46) [  GIVEN] a long double curly quote with a single curly quote inside it
    (14/46) [     DO] move to the second line, select the quote, and make it uppercase
    (14/46) [ EXPECT] an uppercase curly quote
    (15/46) [  GIVEN] a straightforward double curly quote
    (15/46) [     DO] select around double curly quotes and make it uppercase
    (15/46) [ EXPECT] an uppercase curly quote
    (16/46) [  GIVEN] a straightforward double curly quote
    (16/46) [     DO] change text including the double curly quotes
    (16/46) [ EXPECT] the new text
    (17/46) [  GIVEN] a straightforward double curly quote
    (17/46) [     DO] delete text including the double curly quotes
    (17/46) [ EXPECT] nothing left
    (18/46) [  GIVEN] a one character double curly quote
    (18/46) [     DO] select around double curly quotes and make it uppercase
    (18/46) [ EXPECT] an uppercase curly quote
    (19/46) [  GIVEN] a straightforward double curly quote
    (19/46) [     DO] change text including the double curly quotes
    (19/46) [ EXPECT] the new text
    (20/46) [  GIVEN] a straightforward double curly quote
    (20/46) [     DO] delete text including the double curly quotes
    (20/46) [ EXPECT] only the double curly quotes to remain
    (21/46) [  GIVEN] a straightforward single curly quote
    (21/46) [     DO] select inside single curly quotes
    (21/46) [ EXPECT] the curly quote in uppercase
    (22/46) [  GIVEN] a straightforward single curly quote
    (22/46) [     DO] change text inside the single curly quotes
    (22/46) [ EXPECT] the new text
    (23/46) [  GIVEN] a straightforward single curly quote
    (23/46) [     DO] delete text inside the single curly quotes
    (23/46) [ EXPECT] only the quotes to remain
    (24/46) [  GIVEN] a single curly quote with an apostrophe inside of it
    (24/46) [     DO] select single curly quotes
    (24/46) [ EXPECT] the curly quote in uppercase
    (25/46) [  GIVEN] no curly quotes
    (25/46) [     DO] select a single curly quote and make it uppercase
    (25/46) [ EXPECT] no change
    (26/46) [  GIVEN] single curly quotes inside of double curly quotes
    (26/46) [     DO] select the inner curly quote and make it uppercase
    (26/46) [ EXPECT] an uppercase inner curly quote
    (27/46) [  GIVEN] a two-line single curly quote
    (27/46) [     DO] select the whole curly quote and make it uppercase
    (27/46) [ EXPECT] an uppercase curly quote
    (28/46) [  GIVEN] a two-line single curly quote with an apostrophe in it
    (28/46) [     DO] select the whole curly quote and make it uppercase
    (28/46) [ EXPECT] an uppercase curly quote
    (29/46) [  GIVEN] a two-line single curly quote
    (29/46) [     DO] change the entire curly quote
    (29/46) [ EXPECT] an uppercase curly quote
    (30/46) [  GIVEN] a two-line single curly quote
    (30/46) [     DO] delete the whole curly quote
    (30/46) [ EXPECT] only the single quotes to remain
    (31/46) [  GIVEN] a two-line single curly quote
    (31/46) [     DO] move to the second line, select the quote, and make it uppercase
    (31/46) [ EXPECT] an uppercase curly quote
    (32/46) [  GIVEN] a long single curly quote with a double curly quote inside it
    (32/46) [     DO] move to the second line, select the quote, and make it uppercase
    (32/46) [ EXPECT] an uppercase curly quote
    (33/46) [  GIVEN] a straightforward single curly quote
    (33/46) [     DO] select around single curly quotes and make it uppercase
    (33/46) [ EXPECT] an uppercase curly quote
    (34/46) [  GIVEN] a straightforward single curly quote
    (34/46) [     DO] change text including the single curly quotes
    (34/46) [ EXPECT] the new text
    (35/46) [  GIVEN] a straightforward single curly quote
    (35/46) [     DO] delete text including the double curly quotes
    (35/46) [ EXPECT] an empty string
    (36/46) [  GIVEN] a one character single curly quote
    (36/46) [     DO] select around single curly quotes and make it uppercase
    (36/46) [ EXPECT] an uppercase curly quote
    (37/46) [  GIVEN] a straightforward single curly quote
    (37/46) [     DO] change text including the single curly quotes
    (37/46) [ EXPECT] the new text
    (38/46) [  GIVEN] a straightforward single curly quote
    (38/46) [     DO] delete text including the single curly quotes
    (38/46) [ EXPECT] only the single curly quotes to remain
    (39/46) [  GIVEN] a little document with some double curly quotes
    (39/46) [     DO] ]c to move forward to the next double curly quote
    (39/46) [   THEN] a test of where we are
    (40/46) [  GIVEN] a little document with some double curly quotes
    (40/46) [     DO] ]c]c to move forward to the second double curly quote
    (40/46) [   THEN] another test of where we are
    (41/46) [  GIVEN] a little document with some double curly quotes
    (41/46) [     DO] go to the end and then [c to go to the second double curly quote
    (41/46) [   THEN] yet another test of where we are
    (42/46) [  GIVEN] a little document with some single curly quotes
    (42/46) [     DO] ]C to move forward to the next single curly quote
    (42/46) [   THEN] a test of where we are
    (43/46) [  GIVEN] a little document with some single curly quotes
    (43/46) [     DO] ]C]C to move forward to the second single curly quote
    (43/46) [   THEN] another test of where we are
    (44/46) [  GIVEN] a little document with some single curly quotes
    (44/46) [     DO] go to the end and then [C to go to the second single curly quote
    (44/46) [   THEN] yet another test of where we are
    (45/46) [  GIVEN] a little document with some double curly quotes
    (45/46) [     DO] ]c]c]c to test whether nowrapscan is respected
    (45/46) [   THEN] test whether we are still on the second double curly quote
    (46/46) [  GIVEN] a little document with some double curly quotes
    (46/46) [     DO] ]c]c]c to test whether wrapscan is respected
    (46/46) [   THEN] test whether we are still on the second double curly quote
  Success/Total: 46/46
Success/Total: 46/46 (assertions: 8/8)
Elapsed time: 0.45 sec.
NVIM v0.5.0-550-g39f802bef
Build type: Release
LuaJIT 2.1.0-beta3
Compilation: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -O2 -DNDEBUG -Wall -Wextra -pedantic -Wno-unused-parameter -Wstrict-prototypes -std=gnu99 -Wshadow -Wconversion -Wmissing-prototypes -Wimplicit-fallthrough -Wvla -fstack-protector-strong -fno-common -fdiagnostics-color=always -DINCLUDE_GENERATED_DECLARATIONS -D_GNU_SOURCE -DNVIM_MSGPACK_HAS_FLOAT32 -DNVIM_UNIBI_HAS_VAR_FROM -DMIN_LOG_LEVEL=3 -I/Users/telemachus/Downloads/src/neovim/build/config -I/Users/telemachus/Downloads/src/neovim/src -I/Users/telemachus/Downloads/src/neovim/.deps/usr/include -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include -I/usr/local/opt/gettext/include -I/Users/telemachus/Downloads/src/neovim/build/src/nvim/auto -I/Users/telemachus/Downloads/src/neovim/build/include
Compiled by telemachus@mbpr1315-3192.local

Features: +acl +iconv +tui
See ":help feature-compile"

   system vimrc file: "$VIM/sysinit.vim"
  fall-back for $VIM: "/Users/telemachus/local/neovim/share/nvim"

Run :checkhealth for more info

Starting Vader: 1 suite(s), 23 case(s)
  Starting Vader: /Users/telemachus/Documents/git-repos/vim-textobj-curly/test/mappings.vader
    ( 1/23) [EXECUTE] a check of the global variable
    ( 2/23) [  GIVEN] a sentence with curly double quotes and a curly apostrophe
    ( 2/23) [     DO] select all text within the double quotes and uppercase it
    ( 2/23) [ EXPECT] the text still to be lowercase
    ( 3/23) [  GIVEN] a sentence with curly single quotes and a curly apostrophe
    ( 3/23) [     DO] try to select all text within the double quotes and uppercase it
    ( 3/23) [ EXPECT] the text still to be lowercase
    ( 4/23) [  GIVEN] a sentence with curly single quotes and a curly apostrophe
    ( 4/23) [EXECUTE] test if the global var is set
    ( 5/23) [  GIVEN] a sentence with curly quotes and a curly apostrophe
    ( 5/23) [     DO] select all text within double quotes and uppercase it
    ( 5/23) [ EXPECT] the new mappings to work
    ( 6/23) [  GIVEN] a sentence with curly quotes and a curly apostrophe
    ( 6/23) [     DO] delete all text within double quotes
    ( 6/23) [ EXPECT] the new mappings to work
    ( 7/23) [  GIVEN] a sentence with curly quotes and a curly apostrophe
    ( 7/23) [     DO] change all text within double quotes
    ( 7/23) [ EXPECT] the new mappings to work
    ( 8/23) [  GIVEN] a sentence with curly quotes and a curly apostrophe
    ( 8/23) [     DO] select all text including double quotes and uppercase it
    ( 8/23) [ EXPECT] the new mappings to work
    ( 9/23) [  GIVEN] a sentence with curly quotes and a curly apostrophe
    ( 9/23) [     DO] select all text within double quotes and uppercase it
    ( 9/23) [ EXPECT] the new mappings to work
    (10/23) [  GIVEN] a sentence with curly quotes and a curly apostrophe
    (10/23) [     DO] change all text including double quotes
    (10/23) [ EXPECT] the new mappings to work
    (11/23) [  GIVEN] a sentence with curly quotes and a curly apostrophe
    (11/23) [EXECUTE] test if the global var is set
    (12/23) [  GIVEN] a sentence with curly quotes and a curly apostrophe
    (12/23) [     DO] select all text within single quotes and make it uppercase
    (12/23) [ EXPECT] the new mappings to work
    (13/23) [  GIVEN] a sentence with curly quotes and a curly apostrophe
    (13/23) [     DO] delete all text within single quotes
    (13/23) [ EXPECT] the new mappings to work
    (14/23) [  GIVEN] a sentence with curly quotes and a curly apostrophe
    (14/23) [     DO] change all text within single quotes
    (14/23) [ EXPECT] the new mappings to work
    (15/23) [  GIVEN] a sentence with curly quotes and a curly apostrophe
    (15/23) [     DO] select all text including single quotes and make it uppercase
    (15/23) [ EXPECT] the new mappings to work
    (16/23) [  GIVEN] a sentence with curly quotes and a curly apostrophe
    (16/23) [     DO] select all text including single quotes and replace it
    (16/23) [ EXPECT] the new mappings to work
    (17/23) [  GIVEN] a sentence with curly quotes and a curly apostrophe
    (17/23) [     DO] change all text including single quotes
    (17/23) [ EXPECT] the new mappings to work
    (18/23) [  GIVEN] a sentence with curly quotes and a curly apostrophe
    (18/23) [EXECUTE] test if the global var is set
    (19/23) [  GIVEN] a sentence with a curly double quote
    (19/23) [     DO] move to the start of the quote
    (19/23) [   THEN] we should not have moved
    (20/23) [  GIVEN] a sentence with a curly double quote
    (20/23) [     DO] move to the start of the quote
    (20/23) [   THEN] we should have moved
    (21/23) [  GIVEN] a sentence with two curly double quotes
    (21/23) [     DO] go to end and then backwards to second quote
    (21/23) [   THEN] we should have moved
    (22/23) [  GIVEN] a sentence with a curly single quote
    (22/23) [     DO] move to the start of the quote
    (22/23) [   THEN] we should have moved
    (23/23) [  GIVEN] a sentence with two curly single quotes
    (23/23) [     DO] move to the end of the text and then to start of the second quote
    (23/23) [   THEN] we should have moved
  Success/Total: 23/23
Success/Total: 23/23 (assertions: 9/9)
Elapsed time: 0.28 sec.

A  => plugin/textobj/curly.vim +65 -0
@@ 1,65 @@
" textobj-curly - Text objects and movements for curly quotes
" Version: 0.0.1
" License: BSD 3-Clause License
" Copyright (c) 2020 Peter Aronoff
"
" Redistribution and use in source and binary forms, with or without
" modification, are permitted provided that the following conditions
" are met:
"
" 1. Redistributions of source code must retain the above copyright
"    notice, this list of conditions and the following disclaimer.
"
" 2. Redistributions in binary form must reproduce the above copyright
"    notice, this list of conditions and the following disclaimer in
"    the documentation and/or other materials provided with the
"    distribution.
"
" 3. Neither the name of the copyright holder nor the names of its
"    contributors may be used to endorse or promote products derived
"    from this software without specific prior written permission.
"
" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
" FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
" COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
" CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
" POSSIBILITY OF SUCH DAMAGE.
scriptencoding utf-8

if exists('g:loaded_textobj_curly')
    finish
endif
let g:loaded_textobj_curly = 1

let s:save_cpoptions = &cpoptions
set cpoptions&vim

call textobj#user#plugin('curly', {
            \ 'double': {
            \       'pattern': ['“', '”'],
            \       'select-a': 'ac',
            \       'select-i': 'ic',
            \       'move-p': '[c',
            \       'move-p-function': 'textobj#curly#move_pd',
            \       'move-n': ']c',
            \       'move-n-function': 'textobj#curly#move_nd',
            \   },
            \ 'single': {
            \       'pattern': ['‘', '’\(\w\)\@!'],
            \       'select-a': 'aC',
            \       'select-i': 'iC',
            \       'move-p': '[C',
            \       'move-p-function': 'textobj#curly#move_ps',
            \       'move-n': ']C',
            \       'move-n-function': 'textobj#curly#move_ns',
            \   },
            \ })

let &cpoptions = s:save_cpoptions

A  => test.sh +30 -0
@@ 1,30 @@
#!/usr/bin/env bash

# Use privileged mode and avoid $CDPATH
set -p
set -e

if [[ ! -d ./_vader.vim ]]; then
	git clone https://github.com/junegunn/vader.vim ./_vader.vim
# Uncomment the else clause sometimes to make sure I'm up to date.
# else
# 	cd "$(pwd)/_vader.vim"
# 	git remote update -p && git merge --ff-only "@{u}"
# 	cd -
fi

if [[ ! -d ./_vtou ]]; then
	git clone https://github.com/kana/vim-textobj-user ./_vtou
# Uncomment the else clause sometimes to make sure I'm up to date.
# else
# 	cd "$(pwd)/_vtou"
# 	git remote update -p && git merge --ff-only "@{u}"
# 	cd -
fi


: "${VADER_TEST_VIM:=vim}"
$VADER_TEST_VIM -es --clean -Nu vimrc -c '+Vader! test/basics.vader'

$VADER_TEST_VIM -es --clean -Nu no-mappings-vimrc \
	-c '+Vader! test/mappings.vader'

A  => test/basics.vader +7 -0
@@ 1,7 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Test the text objects
Include: double-curly.vader
Include: single-curly.vader

# Test the movements
Include: movement.vader

A  => test/double-curly.vader +191 -0
@@ 1,191 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (a straightforward double curly quote):
  “This is not a love song.”

Do (select inside double curly quotes):
  vicU

Expect (an uppercase curly quote):
  “THIS IS NOT A LOVE SONG.”

Do (change text inside the double curly quotes):
  cicThis is a love song.

Expect (the new text):
  “This is a love song.”

Do (delete text inside the double curly quotes):
  dic

Expect (only the quotes to remain):
  “”

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (a double curly quote with an apostrophe inside of it):
  “This isn’t a love song.”

Do (select inside double curly quote and make it uppercase):
  vicU

Expect (an uppercase curly quote):
  “THIS ISN’T A LOVE SONG.”

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (no curly quotes):
  This is not a love song.

Do (make the whole curly quote uppercase):
  vicU

Expect (no change):
  This is not a love song.

Do (delete the quote):
  dic

Expect (no change):
  This is not a love song.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (a double curly quote inside of a single curly quote):
  ‘This is “not” a love song.’

Do (select the inner double curly quote and make it uppercase):
  /not\<CR>vicU

Expect (an uppercase inner double curly quote):
  ‘This is “NOT” a love song.’

Given (a double curly quote inside of a single curly quote):
  ‘This is “not” a love song.’

Do (change the inner double curly quote):
  /not\<CR>cicdefinitely

Expect (an uppercase inner double curly quote):
  ‘This is “definitely” a love song.’

Given (a double curly quote inside of a single curly quote):
  ‘This is “not” a love song.’

Do (delete the inner double curly quote):
  /not\<CR>dac

Expect (an empty inner double curly quote):
# Note that the extra space is not a typo. I am not yet sure how I feel about
# this. In the future, maybe I should have the regex eat any following space?
  ‘This is  a love song.’

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (a two-line double curly quote):
  “This is not a love song. This is definitely
  not a love song.”

Do (select the entire curly quote and make it uppercase):
  vicU

Expect (an uppercase curly quote):
  “THIS IS NOT A LOVE SONG. THIS IS DEFINITELY
  NOT A LOVE SONG.”

Given (a two-line double curly quote):
  “This is not a love song. This is definitely
  not a love song.”

Do (change the whole curly quote):
  cicThis might be a love song.

Expect (the new curly quote):
  “This might be a love song.”

Given (a two-line double curly quote):
  “This is not a love song. This is definitely
  not a love song.”

Do (delete everything inside the curly quote):
  dic

Expect (only the quotes to remain):
  “”

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (a two-line double curly quote):
  “This is not a love song. This is definitely
  not a love song.”

Do (move to the second line, select the quote, and make it uppercase):
  jvicU

Expect (an uppercase curly quote):
  “THIS IS NOT A LOVE SONG. THIS IS DEFINITELY
  NOT A LOVE SONG.”

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (a long double curly quote with a single curly quote inside it):
  “This is not a love song. This is ‘definitely
  not’ a love song.”

Do (move to the second line, select the quote, and make it uppercase):
  jvicU

Expect (an uppercase curly quote):
  “THIS IS NOT A LOVE SONG. THIS IS ‘DEFINITELY
  NOT’ A LOVE SONG.”

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (a straightforward double curly quote):
  “This is not a love song.”

Do (select around double curly quotes and make it uppercase):
  vacU

Expect (an uppercase curly quote):
  “THIS IS NOT A LOVE SONG.”

Given (a straightforward double curly quote):
  “This is not a love song.”

Do (change text including the double curly quotes):
  cacThis is a love song.

Expect (the new text):
  This is a love song.

Given (a straightforward double curly quote):
  “This is not a love song.”

Do (delete text including the double curly quotes):
  dac

Expect (nothing left):
# Two spaces are necessary here in order to get vader to register this as an
# empty string. Do not remove or let tooling remove these two spaces!
  

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (a one character double curly quote):
  “t”

Do (select around double curly quotes and make it uppercase):
  vicU

Expect (an uppercase curly quote):
  “T”

Given (a straightforward double curly quote):
  “t”

Do (change text including the double curly quotes):
  cicThis is a love song.

Expect (the new text):
  “This is a love song.”

Given (a straightforward double curly quote):
  “t”

Do (delete text including the double curly quotes):
  dic

Expect (only the double curly quotes to remain):
  “”

A  => test/mappings.vader +6 -0
@@ 1,6 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Test the mappings
Include: no-mappings.vader
Include: new-double-mappings.vader
Include: new-single-mappings.vader
Include: new-movement-mappings.vader

A  => test/movement.vader +77 -0
@@ 1,77 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (a little document with some double curly quotes):
  I said,
  “This is not a love song.” But they said, “Yes, it is!”
  "Only straight quotes here!"

Do (]c to move forward to the next double curly quote):
  ]c

Then (a test of where we are):
  AssertEqual [0, 2, 1, 0], getpos('.')

Do (]c]c to move forward to the second double curly quote):
  ]c]c

Then (another test of where we are):
  AssertEqual [0, 2, 47, 0], getpos('.')

Do (go to the end and then [c to go to the second double curly quote):
  G[c

Then (yet another test of where we are):
  AssertEqual [0, 2, 47, 0], getpos('.')

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (a little document with some single curly quotes):
  I said,
  ‘This is not a love song.’ But they said, ‘Yes, it is!’
  "Only straight quotes here!"

Do (]C to move forward to the next single curly quote):
  ]C

Then (a test of where we are):
  AssertEqual [0, 2, 1, 0], getpos('.')

Do (]C]C to move forward to the second single curly quote):
  ]C]C

Then (another test of where we are):
  AssertEqual [0, 2, 47, 0], getpos('.')

Do (go to the end and then [C to go to the second single curly quote):
  G[C

Then (yet another test of where we are):
  AssertEqual [0, 2, 47, 0], getpos('.')

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before (set nowrapscan):
  :set nowrapscan

Given (a little document with some double curly quotes):
  I said,
  “This is not a love song.” But they said, “Yes, it is!”
  "Only straight quotes here!"

Do (]c]c]c to test whether nowrapscan is respected):
  ]c]c]c

Then (test whether we are still on the second double curly quote):
  AssertEqual [0, 2, 47, 0], getpos('.')

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before (set wrapscan):
  :set wrapscan

Given (a little document with some double curly quotes):
  I said,
  “This is not a love song.” But they said, “Yes, it is!”
  "Only straight quotes here!"

Do (]c]c]c to test whether wrapscan is respected):
  ]c]c]c

Then (test whether we are still on the second double curly quote):
  AssertEqual [0, 2, 1, 0], getpos('.')

A  => test/new-double-mappings.vader +67 -0
@@ 1,67 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Execute (test if the global var is set):
  AssertEqual 1, g:textobj_curly_no_default_key_mappings

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before (set new double-i xmapping):
  xmap iq <Plug>(textobj-curly-double-i)

Given (a sentence with curly quotes and a curly apostrophe):
  “This is fun, isn’t it?”

Do (select all text within double quotes and uppercase it):
  viqU

Expect (the new mappings to work):
  “THIS IS FUN, ISN’T IT?”

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before (set new double-i omapping):
  omap iq <Plug>(textobj-curly-double-i)

Given (a sentence with curly quotes and a curly apostrophe):
  “This is fun, isn’t it?”

Do (delete all text within double quotes):
  diq

Expect (the new mappings to work):
  “”

Do (change all text within double quotes):
  ciqFoo, foo, foo!

Expect (the new mappings to work):
  “Foo, foo, foo!”

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before (set new double-a xmapping):
  xmap aq <Plug>(textobj-curly-double-a)

Given (a sentence with curly quotes and a curly apostrophe):
  “This is fun, isn’t it?”

Do (select all text including double quotes and uppercase it):
  viqU

Expect (the new mappings to work):
  “THIS IS FUN, ISN’T IT?”

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before (set new double-i omapping):
  omap aq <Plug>(textobj-curly-double-a)

Given (a sentence with curly quotes and a curly apostrophe):
  “This is fun, isn’t it?”

Do (select all text within double quotes and uppercase it):
  daqiFoo

Expect (the new mappings to work):
  Foo

Do (change all text including double quotes):
  caqFoo, foo, foo!

Expect (the new mappings to work):
  Foo, foo, foo!

A  => test/new-movement-mappings.vader +67 -0
@@ 1,67 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Execute (test if the global var is set):
  AssertEqual 1, g:textobj_curly_no_default_key_mappings

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (a sentence with a curly double quote):
  I said, “This is fun, isn’t it?”

Do (move to the start of the quote):
  ]c

Then (we should not have moved):
  AssertEqual [0, 1, 1, 0], getpos('.')

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before (set new mapping for forward double curly movement):
  map ]q <Plug>(textobj-curly-double-n)

Given (a sentence with a curly double quote):
  I said, “This is fun, isn’t it?”

Do (move to the start of the quote):
  ]q

Then (we should have moved):
  AssertEqual [0, 1, 9, 0], getpos('.')

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before (set new mapping for backward double curly movement):
  map [q <Plug>(textobj-curly-double-p)

Given (a sentence with two curly double quotes):
  I said, “This is fun, isn’t it?”
  They replied, “It sure is!”

Do (go to end and then backwards to second quote):
  G$[q

Then (we should have moved):
  AssertEqual [0, 2, 15, 0], getpos('.')

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before (set new mapping for forward single curly movement):
  map ]Q <Plug>(textobj-curly-single-n)

Given (a sentence with a curly single quote):
  I said, ‘This is fun, isn’t it?’

Do (move to the start of the quote):
  ]Q

Then (we should have moved):
  AssertEqual [0, 1, 9, 0], getpos('.')

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before (set new mapping for backward single curly movement):
  map [Q <Plug>(textobj-curly-single-p)

Given (a sentence with two curly single quotes):
  I said, ‘This is fun, isn’t it?’
  They replied, ‘It sure is!’

Do (move to the end of the text and then to start of the second quote):
  G$[Q

Then (we should have moved):
  AssertEqual [0, 2, 15, 0], getpos('.')

A  => test/new-single-mappings.vader +67 -0
@@ 1,67 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Execute (test if the global var is set):
  AssertEqual 1, g:textobj_curly_no_default_key_mappings

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before (set new single-i xmapping):
  xmap iQ <Plug>(textobj-curly-single-i)

Given (a sentence with curly quotes and a curly apostrophe):
  ‘This is fun, isn’t it?’

Do (select all text within single quotes and make it uppercase):
  viQU

Expect (the new mappings to work):
  ‘THIS IS FUN, ISN’T IT?’

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before (set new single-i omapping):
  omap iQ <Plug>(textobj-curly-single-i)

Given (a sentence with curly quotes and a curly apostrophe):
  ‘This is fun, isn’t it?’

Do (delete all text within single quotes):
  diQ

Expect (the new mappings to work):
  ‘’

Do (change all text within single quotes):
  ciQFoo, foo, foo!

Expect (the new mappings to work):
  ‘Foo, foo, foo!’

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before (set new single-a xmapping):
  xmap aQ <Plug>(textobj-curly-single-a)

Given (a sentence with curly quotes and a curly apostrophe):
  ‘This is fun, isn’t it?’

Do (select all text including single quotes and make it uppercase):
  vaQU

Expect (the new mappings to work):
  ‘THIS IS FUN, ISN’T IT?’

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before (set new single-i omapping):
  omap aQ <Plug>(textobj-curly-single-a)

Given (a sentence with curly quotes and a curly apostrophe):
  ‘This is fun, isn’t it?’

Do (select all text including single quotes and replace it):
  daQiFoo

Expect (the new mappings to work):
  Foo

Do (change all text including single quotes):
  caQFoo, foo, foo!

Expect (the new mappings to work):
  Foo, foo, foo!

A  => test/no-mappings.vader +23 -0
@@ 1,23 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Execute (a check of the global variable):
  AssertEqual 1, g:textobj_curly_no_default_key_mappings

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (a sentence with curly double quotes and a curly apostrophe):
  “This is fun, isn’t it?”

Do (select all text within the double quotes and uppercase it):
  vicU

Expect (the text still to be lowercase):
  “This is fun, isn’t it?”

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (a sentence with curly single quotes and a curly apostrophe):
  ‘This is fun, isn’t it?’

Do (try to select all text within the double quotes and uppercase it):
  viCU

Expect (the text still to be lowercase):
  ‘This is fun, isn’t it?’

A  => test/single-curly.vader +182 -0
@@ 1,182 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (a straightforward single curly quote):
  ‘This is not a love song.’

Do (select inside single curly quotes):
  viCU

Expect (the curly quote in uppercase):
  ‘THIS IS NOT A LOVE SONG.’

Given (a straightforward single curly quote):
  ‘This is not a love song.’

Do (change text inside the single curly quotes):
  ciCThis is a love song.

Expect (the new text):
  ‘This is a love song.’

Given (a straightforward single curly quote):
  ‘This is not a love song.’

Do (delete text inside the single curly quotes):
  diC

Expect (only the quotes to remain):
  ‘’

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (a single curly quote with an apostrophe inside of it):
  ‘This isn’t a love song.’

Do (select single curly quotes):
  viCU

Expect (the curly quote in uppercase):
  ‘THIS ISN’T A LOVE SONG.’

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (no curly quotes):
  This is not a love song.

Do (select a single curly quote and make it uppercase):
  viCU

Expect (no change):
  This is not a love song.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (single curly quotes inside of double curly quotes):
  “This is ‘not’ a love song.”

Do (select the inner curly quote and make it uppercase):
  /not\<CR>viCU

Expect (an uppercase inner curly quote):
  “This is ‘NOT’ a love song.”

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (a two-line single curly quote):
  ‘This is not a love song. This is definitely
  not a love song.’

Do (select the whole curly quote and make it uppercase):
  viCU

Expect (an uppercase curly quote):
  ‘THIS IS NOT A LOVE SONG. THIS IS DEFINITELY
  NOT A LOVE SONG.’

Given (a two-line single curly quote with an apostrophe in it):
  ‘This isn’t a love song. This is definitely
  not a love song.’

Do (select the whole curly quote and make it uppercase):
  viCU

Expect (an uppercase curly quote):
  ‘THIS ISN’T A LOVE SONG. THIS IS DEFINITELY
  NOT A LOVE SONG.’

Given (a two-line single curly quote):
  ‘This is not a love song. This is definitely
  not a love song.’

Do (change the entire curly quote):
  ciCThis might be a love song.

Expect (an uppercase curly quote):
  ‘This might be a love song.’

Given (a two-line single curly quote):
  ‘This is not a love song. This is definitely
  not a love song.’

Do (delete the whole curly quote):
  diC

Expect (only the single quotes to remain):
  ‘’

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (a two-line single curly quote):
  ‘This is not a love song. This is definitely
  not a love song.’

Do (move to the second line, select the quote, and make it uppercase):
  jviCU

Expect (an uppercase curly quote):
  ‘THIS IS NOT A LOVE SONG. THIS IS DEFINITELY
  NOT A LOVE SONG.’

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (a long single curly quote with a double curly quote inside it):
  ‘This is not a love song. This is “definitely
  not” a love song.’

Do (move to the second line, select the quote, and make it uppercase):
  jviCU

Expect (an uppercase curly quote):
  ‘THIS IS NOT A LOVE SONG. THIS IS “DEFINITELY
  NOT” A LOVE SONG.’

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (a straightforward single curly quote):
  ‘This is not a love song.’

Do (select around single curly quotes and make it uppercase):
  vaCU

Expect (an uppercase curly quote):
  ‘THIS IS NOT A LOVE SONG.’

Given (a straightforward single curly quote):
  ‘This is not a love song.’

Do (change text including the single curly quotes):
  caCThis is a love song.

Expect (the new text):
  This is a love song.

Given (a straightforward single curly quote):
  ‘This is not a love song.’

Do (delete text including the double curly quotes):
  daC

Expect (an empty string):
# Two spaces are necessary here in order to get vader to register this as an
# empty string. Do not remove or let tooling remove these two spaces!
  

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Given (a one character single curly quote):
  ‘t’

Do (select around single curly quotes and make it uppercase):
  viCU

Expect (an uppercase curly quote):
  ‘T’

Given (a straightforward single curly quote):
  ‘t’

Do (change text including the single curly quotes):
  ciCThis is a love song.

Expect (the new text):
  ‘This is a love song.’

Given (a straightforward single curly quote):
  ‘t’

Do (delete text including the single curly quotes):
  diC

Expect (only the single curly quotes to remain):
  ‘’

A  => vimrc +9 -0
@@ 1,9 @@
filetype off
set runtimepath+=_vader.vim
set runtimepath+=_vtou
set runtimepath+=.
filetype plugin indent on
syntax enable
set nomore
set noswapfile
set viminfo=