├── .gitignore ├── .travis.yml ├── test ├── setup.vader ├── history.vader └── oblique.vader ├── README.md ├── doc └── oblique.txt └── plugin └── oblique.vim /.gitignore: -------------------------------------------------------------------------------- 1 | doc/tags 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: vim 2 | 3 | before_script: | 4 | git clone https://github.com/junegunn/vader.vim.git 5 | git clone https://github.com/junegunn/vim-pseudocl.git 6 | git clone https://github.com/tpope/vim-repeat 7 | 8 | hg clone https://code.google.com/p/vim/ 9 | cd vim 10 | # ./configure --with-features=huge --enable-rubyinterp --enable-pythoninterp 11 | ./configure --with-features=huge 12 | make 13 | sudo make install 14 | cd - 15 | 16 | script: | 17 | /usr/local/bin/vim -Nu <(cat << VIMRC 18 | set rtp+=vader.vim 19 | set rtp+=vim-pseudocl 20 | set rtp+=vim-repeat 21 | set rtp+=. 22 | VIMRC) -c 'Vader! test/oblique.vader' > /dev/null 23 | -------------------------------------------------------------------------------- /test/setup.vader: -------------------------------------------------------------------------------- 1 | # Setup 2 | # ============================================================================== 3 | 4 | Execute (Setup): 5 | " Default should be 1 6 | unlet! g:oblique#clear_highlight g:oblique#min_length g:oblique#very_magic 7 | 8 | autocmd! User Oblique 9 | autocmd! User ObliqueStar 10 | autocmd! User ObliqueRepeat 11 | 12 | syntax enable 13 | silent! nunmap " 14 | silent! xunmap " 15 | 16 | function! CurrentMatch(exists, msg) 17 | Assert a:exists == 18 | \ !empty(filter(getmatches(), "v:val.group == 'ObliqueCurrentMatch'")), 19 | \ a:msg 20 | endfunction 21 | command! -bang -nargs=1 AssertCurrentMatch call CurrentMatch(empty(''), ) 22 | 23 | Before: 24 | set smartcase ignorecase 25 | set hlsearch 26 | " Log string(map(range(1, &history), 'histget("/", - v:val)')) 27 | 28 | Given: 29 | Copyright (c) 2014 Junegunn Choi 30 | 31 | MIT License 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining 34 | a copy of this software and associated documentation files (the 35 | "Software"), to deal in the Software without restriction, including 36 | without limitation the rights to use, copy, modify, merge, publish, 37 | distribute, sublicense, and/or sell copies of the Software, and to 38 | permit persons to whom the Software is furnished to do so, subject to 39 | the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be 42 | included in all copies or substantial portions of the Software. 43 | 44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 45 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 46 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 47 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 48 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 49 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 50 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 51 | 52 | -------------------------------------------------------------------------------- /test/history.vader: -------------------------------------------------------------------------------- 1 | Include: setup.vader 2 | 3 | # Search history 4 | # ============================================================================== 5 | 6 | After: 7 | AssertEqual 'copy', histget('/', -1) 8 | Do (Short pattern is not added to search history): 9 | /copy\ 10 | :Log string(map(range(1, &history), 'histget("/", -v:val)'))\ 11 | /co\ 12 | :Log string(map(range(1, &history), 'histget("/", -v:val)'))\ 13 | 14 | After: 15 | AssertEqual 'copy', histget('/', -2) 16 | AssertEqual 'license', histget('/', -1) 17 | Do (Short pattern is not added to search history): 18 | /copy\ 19 | /co\ 20 | /license\ 21 | 22 | After: 23 | Execute (Set g:oblique#min_length to 2): 24 | let g:oblique#min_length = 2 25 | 26 | After: 27 | AssertEqual 'copy', histget('/', -3) 28 | AssertEqual 'license', histget('/', -2) 29 | AssertEqual 'co', histget('/', -1) 30 | Do (2-char pattern is then added to search history, but not 1-char pattern): 31 | /co\ 32 | /c\ 33 | 34 | After: 35 | Execute (Unlet g:oblique#min_length): 36 | unlet g:oblique#min_length 37 | 38 | After: 39 | AssertEqual [3, 5], [line('.'), col('.')] 40 | AssertEqual 'co', histget('/', -2) 41 | AssertEqual 'license', histget('/', -1) 42 | Do (CTRL-P and Up to go up the history): 43 | /\\\ 44 | 45 | After: 46 | AssertEqual [3, 5], [line('.'), col('.')] 47 | AssertEqual 'license', histget('/', -1) 48 | Do (// will choose the last pattern): 49 | //\ 50 | 51 | After: 52 | AssertEqual [19, 51], [line('.'), col('.')] 53 | AssertEqual 'copyright', histget('/', -1) 54 | Do (?? will choose the last pattern): 55 | /copyright\ 56 | G 57 | ??\ 58 | 59 | After: 60 | AssertEqual 'xxxnotfound', histget('/', -1) 61 | Do (Unmatched pattern also should be added): 62 | /xxxnotfound\ 63 | 64 | 65 | # Star-search history 66 | # ============================================================================== 67 | 68 | After: 69 | AssertEqual [16, 14], [line('.'), col('.')] 70 | AssertEqual '\Vlicense', histget('/', -1) 71 | Do (*-search, then n and N, short pattern is not added to history): 72 | 5Gw 73 | *nnnN 74 | 75 | After: 76 | AssertEqual [16, 14], [line('.'), col('.')] 77 | AssertEqual '\Vlicense', histget('/', -1) 78 | Do (#-search, then n and N, short pattern is not added to history): 79 | 5Gw 80 | *nnnN 81 | 82 | 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | vim-oblique ![travis-ci](https://travis-ci.org/junegunn/vim-oblique.svg?branch=master) 2 | =========== 3 | 4 | *Disclaimer: this plugin has many issues that cannot be easily fixed. 5 | I suggest that you try simpler alternatives like 6 | [vim-evanesco](https://github.com/pgdouyon/vim-evanesco) or 7 | [vim-slash](https://github.com/junegunn/vim-slash).* 8 | 9 | Improved `/`-search for Vim. 10 | 11 | - Different highlight for the match under the cursor 12 | - Automatically clears search highlight when cursor is moved 13 | - Does not append short patterns to search history 14 | - Readline key bindings 15 | - Fuzzy-search 16 | - Improved star-search (visual-mode, highlighting without moving) 17 | 18 | Installation 19 | ------------ 20 | 21 | Use your favorite plugin manager. vim-oblique requires 22 | [vim-pseudocl](https://github.com/junegunn/vim-pseudocl). 23 | 24 | With [vim-plug](https://github.com/junegunn/vim-plug): 25 | 26 | ```vim 27 | Plug 'junegunn/vim-pseudocl' 28 | Plug 'junegunn/vim-oblique' 29 | ``` 30 | 31 | Usage 32 | ----- 33 | 34 | vim-oblique overrides the following keys by default: 35 | 36 | | Default Key | `` map | Description | 37 | | ----------- | -------------------- | ------------------------------------------------ | 38 | | `/` | `(Oblique-/)` | Forward search | 39 | | `?` | `(Oblique-?)` | Backward search | 40 | | `z/` | `(Oblique-F/)` | Forward fuzzy-search | 41 | | `z?` | `(Oblique-F?)` | Backward fuzzy-search | 42 | | `n` | `(Oblique-n)` | Repeat the last search | 43 | | `N` | `(Oblique-N)` | Repeat the last search in the opposite direction | 44 | | | `(Oblique-n!)` | Repeat the last search (always forward) | 45 | | | `(Oblique-N!)` | Repeat the last search (always backward) | 46 | | `*` | `(Oblique-*)` | Forward star-search (in normal and visual mode) | 47 | | `#` | `(Oblique-#)` | Backward star-search (in normal and visual mode) | 48 | | `g*` | `(Oblique-g*)` | Forward star-search (no word boundary match) | 49 | | `g#` | `(Oblique-g#)` | Backward star-search (no word boundary match) | 50 | 51 | (Unlike the default star-search, the overridden version will not move the cursor) 52 | 53 | Customization 54 | ------------- 55 | 56 | ### Maps 57 | 58 | Use the `` maps in the above table to customize the maps. 59 | 60 | ### Options 61 | 62 | - `g:oblique#min_length` (default: 3) 63 | - Patterns shorter than this will not be added to search history 64 | - `g:oblique#incsearch_highlight_all` (default: 0) 65 | - To highlight all incremental matches (requires `incsearch`) 66 | - `g:oblique#clear_highlight` (default: 1) 67 | - To clear search highlight or not 68 | - `g:oblique#prefix` (default: '') 69 | - Option to prefix pattern with 70 | - `g:oblique#enable_cmap` (default: 1) 71 | - Enable experimental cmap emulation 72 | 73 | ### Events 74 | 75 | You can customize the behavior of vim-oblique by registering custom actions to 76 | the following events of `User` group. 77 | 78 | | Event | When | 79 | | --------------- | -------------------- | 80 | | `Oblique` | `/`, `?`, `z/`, `z?` | 81 | | `ObliqueStar` | `*`, `#`, `g*`, `g#` | 82 | | `ObliqueRepeat` | `n`, `N` | 83 | 84 | The following example will move your cursor line to the middle of the screen 85 | after search. 86 | 87 | ```vim 88 | autocmd! User Oblique normal! zz 89 | autocmd! User ObliqueStar normal! zz 90 | autocmd! User ObliqueRepeat normal! zz 91 | ``` 92 | 93 | ### Highlighting 94 | 95 | Define the following highlight groups to change the color: 96 | 97 | - `ObliquePrompt` (default: linked to `Label`) 98 | - `ObliqueLine` (default: linked to `None`) 99 | - `ObliqueCurrentMatch` (default: linked to `IncSearch`) 100 | - `ObliqueCurrentIncSearch` (default: linked to `IncSearch`) 101 | 102 | #### Example 103 | 104 | ```vim 105 | hi! def link ObliqueCurrentMatch Keyword 106 | hi! def link ObliquePrompt Structure 107 | hi! def link ObliqueLine String 108 | ``` 109 | 110 | License 111 | ------- 112 | 113 | MIT 114 | 115 | -------------------------------------------------------------------------------- /doc/oblique.txt: -------------------------------------------------------------------------------- 1 | oblique.txt oblique Last change: December 14 2014 2 | OBLIQUE - TABLE OF CONTENTS *oblique* *oblique-toc* 3 | ============================================================================== 4 | 5 | vim-oblique 6 | Installation |oblique-1| 7 | Usage |oblique-2| 8 | Customization |oblique-3| 9 | Maps |oblique-3-1| 10 | Options |oblique-3-2| 11 | Events |oblique-3-3| 12 | Highlighting |oblique-3-4| 13 | Example |oblique-3-4-1| 14 | License |oblique-4| 15 | 16 | 17 | VIM-OBLIQUE *vim-oblique* 18 | ============================================================================== 19 | 20 | Improved `/`-search for Vim. 21 | 22 | - Different highlight for the match under the cursor 23 | - Automatically clears search highlight when cursor is moved 24 | - Does not append short patterns to search history 25 | - Readline key bindings 26 | - Fuzzy-search 27 | - Improved star-search (visual-mode, highlighting without moving) 28 | 29 | 30 | *oblique-1* 31 | INSTALLATION *oblique-installation* 32 | ============================================================================== 33 | 34 | Use your favorite plugin manager. vim-oblique requires {vim-pseudocl}{1}. 35 | 36 | With {vim-plug}{2}: 37 | > 38 | Plug 'junegunn/vim-pseudocl' 39 | Plug 'junegunn/vim-oblique' 40 | < 41 | {1} https://github.com/junegunn/vim-pseudocl 42 | {2} https://github.com/junegunn/vim-plug 43 | 44 | 45 | *oblique-2* 46 | USAGE *oblique-usage* 47 | ============================================================================== 48 | 49 | vim-oblique overrides the following keys by default: 50 | 51 | *(Oblique-/)* *(Oblique-?)* *(Oblique-F/)* *(Oblique-F?)* 52 | *(Oblique-n)* *(Oblique-N)* *(Oblique-#)* *(Oblique-g#)* 53 | 54 | ------------+--------------------+------------------------------------------------- 55 | Default Key | map | Description ~ 56 | ------------+--------------------+------------------------------------------------- 57 | `/` | (Oblique-/) | Forward search 58 | `?` | (Oblique-?) | Backward search 59 | `z/` | (Oblique-F/) | Forward fuzzy-search 60 | `z?` | (Oblique-F?) | Backward fuzzy-search 61 | `n` | (Oblique-n) | Repeat the last search 62 | `N` | (Oblique-N) | Repeat the last search in the opposite direction 63 | | (Oblique-n!) | Repeat the last search (always forward) 64 | | (Oblique-N!) | Repeat the last search (always backward) 65 | `*` | (Oblique-*) | Forward star-search (in normal and visual mode) 66 | `#` | (Oblique-#) | Backward star-search (in normal and visual mode) 67 | `g*` | (Oblique-g*) | Forward star-search (no word boundary match) 68 | `g#` | (Oblique-g#) | Backward star-search (no word boundary match) 69 | ------------+--------------------+------------------------------------------------- 70 | 71 | (Unlike the default star-search, the overridden version will not move the 72 | cursor) 73 | 74 | 75 | *oblique-3* 76 | CUSTOMIZATION *oblique-customization* 77 | ============================================================================== 78 | 79 | 80 | < Maps >______________________________________________________________________~ 81 | *oblique-maps* 82 | *oblique-3-1* 83 | 84 | Use the maps in the above table to customize the maps. 85 | 86 | 87 | < Options >___________________________________________________________________~ 88 | *oblique-options* 89 | *oblique-3-2* 90 | 91 | *g:oblique#min_length* *g:oblique#incsearch_highlight_all* 92 | *g:oblique#clear_highlight* *g:oblique#prefix* *g:oblique#enable_cmap* 93 | 94 | - `g:oblique#min_length` (default: 3) 95 | - Patterns shorter than this will not be added to search history 96 | - `g:oblique#incsearch_highlight_all` (default: 0) 97 | - To highlight all incremental matches (requires `incsearch`) 98 | - `g:oblique#clear_highlight` (default: 1) 99 | - To clear search highlight or not 100 | - `g:oblique#prefix` (default: "') 101 | - Option to prefix pattern with 102 | - `g:oblique#enable_cmap` (default: 1) 103 | - Enable experimental cmap emulation 104 | 105 | 106 | < Events >____________________________________________________________________~ 107 | *oblique-events* 108 | *oblique-3-3* 109 | 110 | You can customize the behavior of vim-oblique by registering custom actions to 111 | the following events of `User` group. 112 | 113 | ----------------+--------------------- 114 | Event | When ~ 115 | ----------------+--------------------- 116 | `Oblique` | `/` , `?` , `z/` , `z?` 117 | `ObliqueStar` | `*` , `#` , `g*` , `g#` 118 | `ObliqueRepeat` | `n` , `N` 119 | ----------------+--------------------- 120 | 121 | The following example will move your cursor line to the middle of the screen 122 | after search. 123 | > 124 | " Clear autocommand 125 | autocmd! User Oblique 126 | autocmd! User ObliqueStar 127 | autocmd! User ObliqueRepeat 128 | 129 | autocmd User Oblique normal! zz 130 | autocmd User ObliqueStar normal! zz 131 | autocmd User ObliqueRepeat normal! zz 132 | < 133 | 134 | < Highlighting >______________________________________________________________~ 135 | *oblique-highlighting* 136 | *oblique-3-4* 137 | 138 | Define the following highlight groups to change the color: 139 | 140 | - `ObliquePrompt` (default: linked to `Label`) 141 | - `ObliqueLine` (default: linked to `None`) 142 | - `ObliqueCurrentMatch` (default: linked to `IncSearch`) 143 | - `ObliqueCurrentIncSearch` (default: linked to `IncSearch`) 144 | 145 | 146 | Example~ 147 | *oblique-example* 148 | *oblique-3-4-1* 149 | > 150 | hi! def link ObliqueCurrentMatch Keyword 151 | hi! def link ObliquePrompt Structure 152 | hi! def link ObliqueLine String 153 | < 154 | 155 | *oblique-4* 156 | LICENSE *oblique-license* 157 | ============================================================================== 158 | 159 | MIT 160 | 161 | ============================================================================== 162 | vim:tw=78:sw=2:ts=2:ft=help:norl:nowrap: 163 | -------------------------------------------------------------------------------- /test/oblique.vader: -------------------------------------------------------------------------------- 1 | Include: setup.vader 2 | 3 | # Basic search 4 | # ============================================================================== 5 | 6 | Do (Highlight NOT cleared when cursor is not moved): 7 | /license\ 8 | Then: 9 | AssertEqual [3, 5], [line('.'), col('.')] 10 | AssertEqual 1, &hlsearch 11 | doautocmd CursorMoved 12 | AssertEqual 1, &hlsearch 13 | 14 | Do (Highlight cleared when cursor is moved): 15 | /license\l 16 | Then: 17 | AssertEqual [3, 6], [line('.'), col('.')] 18 | AssertEqual 1, &hlsearch 19 | doautocmd CursorMoved 20 | AssertEqual 0, &hlsearch 21 | 22 | Do (Highlight cleared when switched to insert mode): 23 | /license\i\ 24 | Then: 25 | AssertEqual 0, &hlsearch 26 | 27 | Do (Highlight is restored on n): 28 | /license\ln 29 | Then: 30 | AssertEqual [9, 16], [line('.'), col('.')] 31 | AssertEqual 1, &hlsearch 32 | doautocmd CursorMoved 33 | AssertEqual 1, &hlsearch 34 | 35 | Execute (g:oblique#clear_highlight set to 0): 36 | let g:oblique#clear_highlight = 0 37 | " To clear lingering autocmd 38 | doautocmd CursorMoved 39 | 40 | Do (Highlight not cleared even when switched to insert mode): 41 | /license\i\ 42 | Then: 43 | AssertEqual 1, &hlsearch 44 | 45 | Execute (Unlet g:oblique#clear_highlight): 46 | unlet g:oblique#clear_highlight 47 | 48 | Do (n and N on forward search): 49 | /copy\ 50 | nnnN 51 | Then: 52 | AssertEqual 8, line('.') 53 | doautocmd CursorMoved 54 | AssertEqual 1, &hlsearch 55 | 56 | Do (n and N on backward search): 57 | G 58 | ?copy\ 59 | nnnN 60 | Then: 61 | AssertEqual 8, line('.') 62 | doautocmd CursorMoved 63 | AssertEqual 1, &hlsearch 64 | 65 | Do (/ with command): 66 | 5G 67 | d/dealings\ 68 | 69 | Expect: 70 | Copyright (c) 2014 Junegunn Choi 71 | 72 | MIT License 73 | 74 | DEALINGS IN THE SOFTWARE. 75 | 76 | Do (/ with command and nowrapscan): 77 | :set nowrapscan\ 78 | 5G 79 | d/dealings\ 80 | 81 | Expect: 82 | Copyright (c) 2014 Junegunn Choi 83 | 84 | MIT License 85 | 86 | DEALINGS IN THE SOFTWARE. 87 | 88 | Then: 89 | set wrapscan 90 | 91 | # CTRL-L 92 | # ============================================================================== 93 | 94 | Do (FIXME CTRL-L): 95 | /here 96 | \\\\\\ 97 | gny 98 | Then: 99 | AssertEqual "hereby gr", @" 100 | 101 | # /Offsets 102 | # ============================================================================== 103 | 104 | Do (/0): 105 | /here/0 106 | \ 107 | yiw 108 | Then: 109 | AssertEqual "Permission", @" 110 | 111 | Do (/+0): 112 | /here/+0 113 | \ 114 | yiw 115 | Then: 116 | AssertEqual "Permission", @" 117 | 118 | Do (/-0): 119 | /here/-0 120 | \ 121 | yiw 122 | Then: 123 | AssertEqual "Permission", @" 124 | 125 | Do (/+): 126 | /here/+ 127 | \ 128 | yiw 129 | Then: 130 | AssertEqual "a", @" 131 | 132 | Do (/3): 133 | /here/3 134 | \ 135 | yiw 136 | Then: 137 | AssertEqual "without", @" 138 | 139 | Do (Repeat with //3): 140 | //3 141 | \ 142 | yiw 143 | Then: 144 | AssertEqual "without", @" 145 | 146 | Do (/+3): 147 | /here/+3 148 | \ 149 | yiw 150 | Then: 151 | AssertEqual "without", @" 152 | 153 | Do (/-): 154 | /here/- 155 | \ 156 | yy 157 | Then: 158 | AssertEqual "\n", @" 159 | 160 | Do (/-2): 161 | /here/-2 162 | \ 163 | yiw 164 | Then: 165 | AssertEqual "MIT", @" 166 | 167 | Do (/e): 168 | /here/e 169 | \ 170 | xyiw 171 | Then: 172 | AssertEqual "herby", @" 173 | 174 | Do (/e+): 175 | /here/e+ 176 | \ 177 | xyiw 178 | Then: 179 | AssertEqual "herey", @" 180 | 181 | Do (/e2): 182 | /here/e2 183 | \ 184 | xbyiw 185 | Then: 186 | AssertEqual "hereb", @" 187 | 188 | Do (/e+2): 189 | /here/e+2 190 | \ 191 | xbyiw 192 | Then: 193 | AssertEqual "hereb", @" 194 | 195 | Do (/e-): 196 | /here/e- 197 | \ 198 | xyiw 199 | Then: 200 | AssertEqual "heeby", @" 201 | 202 | Do (/e-12): 203 | /here/e-12 204 | \ 205 | xyiw 206 | Then: 207 | AssertEqual "Permision", @" 208 | 209 | Do (/s): 210 | /here/s 211 | \ 212 | xyiw 213 | Then: 214 | AssertEqual "ereby", @" 215 | 216 | Do (/b): 217 | /here/b 218 | \ 219 | xyiw 220 | Then: 221 | AssertEqual "ereby", @" 222 | 223 | Do (/s+): 224 | /here/s+ 225 | \ 226 | xyiw 227 | Then: 228 | AssertEqual "hreby", @" 229 | 230 | Do (/b+): 231 | /here/b+ 232 | \ 233 | xyiw 234 | Then: 235 | AssertEqual "hreby", @" 236 | 237 | Do (/s2): 238 | /here/s2 239 | \ 240 | xyiw 241 | Then: 242 | AssertEqual "heeby", @" 243 | 244 | Do (/b2): 245 | /here/b2 246 | \ 247 | xyiw 248 | Then: 249 | AssertEqual "heeby", @" 250 | 251 | Do (/s+2): 252 | /here/s+2 253 | \ 254 | xyiw 255 | Then: 256 | AssertEqual "heeby", @" 257 | 258 | Do (/b+2): 259 | /here/b+2 260 | \ 261 | xyiw 262 | Then: 263 | AssertEqual "heeby", @" 264 | 265 | Do (/s-): 266 | /here/s- 267 | \ 268 | xyiw 269 | Then: 270 | AssertEqual "ishereby", @" 271 | 272 | Do (/b-): 273 | /here/b- 274 | \ 275 | xyiw 276 | Then: 277 | AssertEqual "ishereby", @" 278 | 279 | Do (/s-2): 280 | /here/s-2 281 | \ 282 | xbyiw 283 | Then: 284 | AssertEqual "i", @" 285 | 286 | Do (/b-2): 287 | /here/b-2 288 | \ 289 | xbyiw 290 | Then: 291 | AssertEqual "i", @" 292 | 293 | # Repeat 294 | # ============================================================================== 295 | 296 | Do (d/): 297 | d/limit\ 298 | 299 | Expect: 300 | limitation the rights to use, copy, modify, merge, publish, 301 | distribute, sublicense, and/or sell copies of the Software, and to 302 | permit persons to whom the Software is furnished to do so, subject to 303 | the following conditions: 304 | 305 | The above copyright notice and this permission notice shall be 306 | included in all copies or substantial portions of the Software. 307 | 308 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 309 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 310 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 311 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 312 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 313 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 314 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 315 | 316 | Do (FIXME d/.): 317 | d/limit\ 318 | l. 319 | 320 | Expect: 321 | LIMITED TO THE WARRANTIES OF 322 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 323 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 324 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 325 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 326 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 327 | 328 | ^ Very magic (deprecated) 329 | ^ ============================================================================== 330 | Do (/.): 331 | /.\ 332 | Then: 333 | AssertEqual [1, 1], [line('.'), col('.')] 334 | 335 | Do (/\V.): 336 | /\V.\ 337 | Then: 338 | AssertEqual [14, 63], [line('.'), col('.')] 339 | 340 | Execute (Set g:oblique#very_magic): 341 | let g:oblique#very_magic = 1 342 | 343 | Do (\V is automatically prepended to .): 344 | /.\ 345 | Then: 346 | AssertEqual [14, 63], [line('.'), col('.')] 347 | 348 | Do (Delete prepended \V): 349 | /\.\ 350 | Then: 351 | AssertEqual [1, 1], [line('.'), col('.')] 352 | 353 | Execute (Unlet g:oblique#very_magic): 354 | unlet g:oblique#very_magic 355 | 356 | # Prefix 357 | # ============================================================================== 358 | 359 | Execute (Set g:oblique#very_magic): 360 | let g:oblique#prefix = '\V' 361 | 362 | Do (\V is automatically prepended to .): 363 | /.\ 364 | Then: 365 | AssertEqual [14, 63], [line('.'), col('.')] 366 | 367 | Do (Delete prepended \V): 368 | /\.\ 369 | Then: 370 | AssertEqual [1, 1], [line('.'), col('.')] 371 | unlet g:oblique#prefix 372 | 373 | # Star-search 374 | # ============================================================================== 375 | Do (*-search): 376 | jjw* 377 | Then: 378 | AssertEqual '\V\', histget('/', -1) 379 | AssertEqual [3, 5], [line('.'), col('.')] 380 | AssertEqual 1, &hlsearch 381 | 382 | Do (*-search and n: no more match): 383 | jjw*n 384 | Then: 385 | AssertEqual [3, 5], [line('.'), col('.')] 386 | AssertEqual 1, &hlsearch 387 | 388 | Do (*-search and move cursor to clear highlight): 389 | jjw*l 390 | Then: 391 | AssertEqual [3, 6], [line('.'), col('.')] 392 | doautocmd CursorMoved 393 | AssertEqual 0, &hlsearch 394 | 395 | Do (#-search in visual mode): 396 | 9Gfl 397 | ve# 398 | Then: 399 | AssertEqual '\Vlicense', histget('/', -1) 400 | AssertEqual [9, 16], [line('.'), col('.')] 401 | 402 | Do (#-search in visual mode then n): 403 | 9Gfl 404 | ve#n 405 | Then: 406 | AssertEqual [3, 5], [line('.'), col('.')] 407 | 408 | Do (*-search around a paragraph in visual mode): 409 | 5Gvap* 410 | dgn 411 | 412 | Expect: 413 | Copyright (c) 2014 Junegunn Choi 414 | 415 | MIT License 416 | 417 | The above copyright notice and this permission notice shall be 418 | included in all copies or substantial portions of the Software. 419 | 420 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 421 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 422 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 423 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 424 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 425 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 426 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 427 | 428 | # gStar-search 429 | # ============================================================================== 430 | Do (g*-search): 431 | 6G 432 | wg*N 433 | Then: 434 | AssertEqual '\Vcopy', histget('/', -1) 435 | AssertEqual [1, 1], [line('.'), col('.')] 436 | AssertEqual 1, &hlsearch 437 | 438 | Do (g#-search): 439 | 6G 440 | wg#n 441 | Then: 442 | AssertEqual '\Vcopy', histget('/', -1) 443 | AssertEqual [1, 1], [line('.'), col('.')] 444 | AssertEqual 1, &hlsearch 445 | 446 | # Fuzzy-search 447 | # ============================================================================== 448 | 449 | Do (z/ to fuzzy-search): 450 | z/ccc\ 451 | gn"ay 452 | nnNgn"by 453 | ngn"cy 454 | Then: 455 | AssertEqual [13, 11], [line('.'), col('.')] 456 | AssertEqual 'Copyright (c) 2014 Junegunn C', @a 457 | AssertEqual 'copy of this software and associated doc', @b 458 | AssertEqual 'copyright notice and this permission notic', @c 459 | 460 | Do (z? to backward-fuzzy-search): 461 | G 462 | z?ccc\ 463 | gn"ay 464 | nnNgn"by 465 | ngn"cy 466 | Then: 467 | AssertEqual [6, 3], [line('.'), col('.')] 468 | AssertEqual 'CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN C', @a 469 | AssertEqual 'copyright notice and this permission notic', @b 470 | AssertEqual 'copy of this software and associated doc', @c 471 | 472 | # Regression 473 | # ============================================================================== 474 | 475 | Do (Invalid regular expression should not throw exception): 476 | /\?\\right\ 477 | Then: 478 | AssertEqual [1, 5], [line('.'), col('.')] 479 | 480 | Execute (n should not raise error when no match is found): 481 | execute "normal /xxxyyyzzz\n" 482 | 483 | Execute (N also should not raise error when no match is found): 484 | execute "normal /xxxyyyzzz\N" 485 | 486 | Execute (n should not raise error in visual mode when no match is found): 487 | execute "normal v/xxxyyyzzz\N" 488 | 489 | Given: 490 | \ { 'on': ['GHDashboard', 'GHActivity'] } 491 | Do (Star-search should not fail in the presence of backslashes): 492 | V* 493 | 494 | # (#9) Event 495 | Execute (Set up custom autocommand): 496 | autocmd User Oblique normal! m`gU$`` 497 | autocmd User ObliqueStar normal! m`gUiW`` 498 | autocmd User ObliqueRepeat normal! ~h 499 | 500 | Given: 501 | hello 502 | ----- 503 | world 504 | 505 | Do: 506 | /l\ 507 | nn 508 | 509 | Expect: 510 | heLlO 511 | ----- 512 | worLd 513 | 514 | Given: 515 | hello 516 | ----- 517 | hello world 518 | 519 | Do: 520 | ll*n 521 | 522 | Expect: 523 | HELLO 524 | ----- 525 | Hello world 526 | 527 | Execute (Clear autocommands): 528 | autocmd! User Oblique 529 | autocmd! User ObliqueStar 530 | autocmd! User ObliqueRepeat 531 | 532 | # (#2) Open fold on match 533 | Given: 534 | 1. a 535 | 2. b 536 | 3. a 537 | 4. b 538 | 5. a 539 | 540 | Do (Open fold on match (/)): 541 | * Fold line 2 & 3 542 | jzfj 543 | * Go back to top 544 | gg 545 | * / search to b 546 | /b\ 547 | 548 | Then: 549 | AssertEqual -1, foldclosed('.') 550 | 551 | Do (Open fold on match (* n)): 552 | * Fold line 2 & 3 553 | jzfj 554 | * Go to a at line 1 555 | ggW 556 | * Search to the 2nd occurrence of a 557 | *n 558 | 559 | Then: 560 | AssertEqual -1, foldclosed('.') 561 | 562 | # Error on '**' 563 | Given (Pattern with *s): 564 | a/*/b/**/c 565 | 566 | Do (/**): 567 | fb 568 | /**\ 569 | x 570 | 571 | Expect: 572 | a/*//**/c 573 | 574 | # Empty pattern == previous pattern 575 | Given: 576 | abc 577 | abc 578 | abc 579 | abc 580 | 581 | xyz 582 | xyz 583 | xyz 584 | xyz 585 | 586 | Do (Repeat previous search with empty string): 587 | /bc\ 588 | D 589 | /\ 590 | D 591 | //\ 592 | D 593 | G$ 594 | ?yz\ 595 | D 596 | nD 597 | ?\ 598 | nD 599 | 600 | Expect: 601 | a 602 | a 603 | a 604 | abc 605 | 606 | x 607 | xyz 608 | x 609 | x 610 | 611 | Do (Should also work with star-search): 612 | * 613 | nnn 614 | ?\ 615 | nnD 616 | 617 | G# 618 | nnn 619 | /\ 620 | nnD 621 | 622 | 623 | Expect: 624 | abc 625 | 626 | abc 627 | abc 628 | 629 | xyz 630 | xyz 631 | 632 | xyz 633 | 634 | # \< and \> should not be used is the start/end of the pattern is not iskeyword 635 | Given: 636 | ===== 637 | ===== 638 | ===== 639 | 640 | Do (Delete the second occurrence of =====): 641 | *nD 642 | 643 | Expect: 644 | ===== 645 | 646 | ===== 647 | 648 | # No string under cursor 649 | Given: 650 | Do (Star-search on empty line): 651 | /apple\ 652 | * 653 | :AssertEqual 'apple', @/\ 654 | 655 | Given: 656 | 1apple 657 | 2apple 658 | 3apple 659 | 4apple 660 | 5apple 661 | 6apple 662 | 663 | Do (Counted n/N): 664 | /apple\ 665 | 3nD 666 | 2NCbanana 667 | 668 | Expect: 669 | 1apple 670 | 2banana 671 | 3apple 672 | 4 673 | 5apple 674 | 6apple 675 | 676 | # (#21) Search pattern highlighted when it isn't supposed to be 677 | Given: 678 | test this 679 | test this 680 | 681 | Execute (Search, move, and cancelled search): 682 | execute "normal /test\" 683 | AssertCurrentMatch 'Should highlight current match' 684 | normal! w 685 | doautocmd CursorMoved 686 | AssertCurrentMatch! 'Should clear highlight when cursor is moved' 687 | execute "normal /this\\\\" 688 | AssertCurrentMatch! 'Cancelled search should not restore highlight' 689 | 690 | # (#20) gd/gD highlighting breaks after doing some search 691 | Execute (gd): 692 | normal jgd 693 | AssertEqual 1, line('.') 694 | AssertEqual 3, col('.') 695 | AssertCurrentMatch 'Should highlight keyword on gd' 696 | normal! w 697 | doautocmd CursorMoved 698 | AssertCurrentMatch! 'Highlighting cleared on cursor move' 699 | 700 | # (#25) Fix current word highlighting for star-search 701 | Given: 702 | test this test 703 | 704 | Execute (#): 705 | normal e# 706 | Assert filter(getmatches(), "v:val.group == 'ObliqueCurrentMatch'")[0].pattern 707 | \ =~ '%1c' 708 | 709 | # (#26) Add [count] support for all commands 710 | Given: 711 | item 1 712 | item 2 713 | item 3 714 | item 4 715 | item 5 716 | - item 6 717 | 718 | Execute (/ and ? with count): 719 | normal! gg0 720 | execute "normal /item\" 721 | AssertEqual 1, line('.') 722 | AssertEqual 1, col('.') 723 | 724 | normal! gg0 725 | execute "normal 1/item\" 726 | AssertEqual 1, line('.') 727 | AssertEqual 1, col('.') 728 | 729 | normal! gg0 730 | execute "normal 2/item\" 731 | AssertEqual 2, line('.') 732 | AssertEqual 1, col('.') 733 | execute "normal 3/item\" 734 | AssertEqual 4, line('.') 735 | AssertEqual 1, col('.') 736 | execute "normal 2?item\" 737 | AssertEqual 3, line('.') 738 | AssertEqual 1, col('.') 739 | 740 | Execute (/ with cursor not on the first match): 741 | normal! ggw 742 | execute "normal /item\" 743 | AssertEqual 2, line('.') 744 | AssertEqual 1, col('.') 745 | 746 | normal! ggw 747 | execute "normal 1/item\" 748 | AssertEqual 2, line('.') 749 | AssertEqual 1, col('.') 750 | 751 | normal! ggw 752 | execute "normal 2/item\" 753 | AssertEqual 3, line('.') 754 | AssertEqual 1, col('.') 755 | 756 | Execute (* and # with count): 757 | normal 2* 758 | AssertEqual 2, line('.') 759 | AssertEqual 1, col('.') 760 | normal 3* 761 | AssertEqual 4, line('.') 762 | AssertEqual 1, col('.') 763 | normal 2# 764 | AssertEqual 3, line('.') 765 | AssertEqual 1, col('.') 766 | 767 | Execute (* and # with cursor not on the first match): 768 | normal! G0 769 | normal * 770 | AssertEqual 6, line('.') 771 | AssertEqual 1, col('.') 772 | 773 | normal! G0 774 | normal 1* 775 | AssertEqual 6, line('.') 776 | AssertEqual 1, col('.') 777 | 778 | normal! G0 779 | normal 2* 780 | AssertEqual 1, line('.') 781 | AssertEqual 1, col('.') 782 | 783 | normal! G0 784 | normal # 785 | AssertEqual 6, line('.') 786 | AssertEqual 1, col('.') 787 | 788 | normal! G0 789 | normal 1# 790 | AssertEqual 6, line('.') 791 | AssertEqual 1, col('.') 792 | 793 | Execute (Correct highlighting on * and #): 794 | normal G0* 795 | let match = filter(getmatches(), "v:val.group == 'ObliqueCurrentMatch'")[0].pattern 796 | Assert match =~ '%6l' 797 | Assert match =~ '%3c' 798 | 799 | normal G0# 800 | let match = filter(getmatches(), "v:val.group == 'ObliqueCurrentMatch'")[0].pattern 801 | Assert match =~ '%6l' 802 | Assert match =~ '%3c' 803 | 804 | Given: 805 | item 806 | item 807 | 808 | Execute (#39 Visual-* on the region starting with non-keyword character): 809 | normal G0lvft* 810 | let match = filter(getmatches(), "v:val.group == 'ObliqueCurrentMatch'")[0].pattern 811 | Assert match =~ '%2l', match 812 | Assert match =~ '%2c', match 813 | 814 | Execute (#40 Escape / or ? when valid offset is not given): 815 | execute "normal /item/e\" 816 | AssertEqual 'item', @/ 817 | 818 | execute "normal /item/item\" 819 | AssertEqual 'item\/item', @/ 820 | 821 | execute "normal ?item?e\" 822 | AssertEqual 'item', @/ 823 | 824 | execute "normal ?item?item\" 825 | AssertEqual 'item\?item', @/ 826 | 827 | execute "normal /item\\/item2\" 828 | AssertEqual 'item\/item2', @/ 829 | 830 | # Cleanup 831 | # ============================================================================== 832 | Execute (Cleanup): 833 | set nohlsearch 834 | unlet match 835 | delfunction CurrentMatch 836 | delcommand AssertCurrentMatch 837 | 838 | -------------------------------------------------------------------------------- /plugin/oblique.vim: -------------------------------------------------------------------------------- 1 | " Copyright (c) 2014 Junegunn Choi 2 | " 3 | " MIT License 4 | " 5 | " Permission is hereby granted, free of charge, to any person obtaining 6 | " a copy of this software and associated documentation files (the 7 | " "Software"), to deal in the Software without restriction, including 8 | " without limitation the rights to use, copy, modify, merge, publish, 9 | " distribute, sublicense, and/or sell copies of the Software, and to 10 | " permit persons to whom the Software is furnished to do so, subject to 11 | " the following conditions: 12 | " 13 | " The above copyright notice and this permission notice shall be 14 | " included in all copies or substantial portions of the Software. 15 | " 16 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | " EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | " MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | " NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | " LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | " OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | " WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | let s:cpo_save = &cpo 25 | set cpo&vim 26 | 27 | if !hlexists('ObliqueLine') 28 | hi def link ObliqueLine None 29 | endif 30 | 31 | if !hlexists('ObliquePrompt') 32 | hi def link ObliquePrompt Label 33 | endif 34 | 35 | if !hlexists('ObliqueCurrentMatch') 36 | hi def link ObliqueCurrentMatch IncSearch 37 | endif 38 | 39 | if !hlexists('ObliqueCurrentIncSearch') 40 | hi def link ObliqueCurrentIncSearch IncSearch 41 | endif 42 | 43 | let s:DEFAULT = { 44 | \ 'min_length': 3, 45 | \ 'incsearch_highlight_all': 0, 46 | \ 'clear_highlight': 1, 47 | \ 'very_magic': 0, 48 | \ 'prefix': '', 49 | \ 'enable_cmap': 1 50 | \ } 51 | 52 | let s:backward = 0 53 | let s:fuzzy = 0 54 | let s:matching = '' 55 | let s:prev = '' 56 | 57 | function! s:optval(key) 58 | return get(g:, 'oblique#'.a:key, s:DEFAULT[a:key]) 59 | endfunction 60 | 61 | function! s:match1(expr, pat) 62 | let arr = matchlist(a:expr, a:pat) 63 | if empty(arr) 64 | return [0, ''] 65 | else 66 | return [1, arr[1]] 67 | endif 68 | endfunction 69 | 70 | " TODO ;{pattern} perform another search, see |//;| 71 | function! s:split(repeat, pat) 72 | return matchlist(a:pat, '\(.\{-}\)\(\\\@(Oblique-Repeat)") 149 | if len(last) < mlen 150 | call histdel('/', -1) 151 | endif 152 | if exists('#User#Oblique') 153 | doautocmd User Oblique 154 | endif 155 | else 156 | if len(last) >= mlen 157 | call histadd('/', @/) 158 | endif 159 | end 160 | endfunction 161 | 162 | function! s:repeat() 163 | execute printf("normal! %s%s%s\", 164 | \ v:operator, s:backward ? '?' : '/', s:input) 165 | silent! call repeat#set("\(Oblique-Repeat)") 166 | endfunction 167 | 168 | function! s:finish_star(gv) 169 | let s:prev = @/ 170 | call s:revert_showcmd() 171 | call winrestview(s:view) 172 | 173 | let under = a:gv || getline('.')[col('.') - 1] =~ '\k' || 174 | \ getline('.')[col('.') :-1] !~ '\k' 175 | try 176 | let ws = &wrapscan 177 | if s:count > 1 178 | execute 'normal! ' . (s:count - under) . 'n' 179 | endif 180 | if !ws 181 | set wrapscan 182 | endif 183 | let pos = winsaveview() 184 | if under 185 | normal! l 186 | execute 'keepjumps normal!' (s:backward ? 'n' : 'N') 187 | elseif s:count == 1 188 | execute 'keepjumps normal!' (s:backward ? 'N' : 'n') 189 | endif 190 | call s:highlight_current_match() 191 | call winrestview(pos) 192 | call s:set_autocmd() 193 | call s:echo_pattern('n') 194 | if len(s:star_word) < s:optval('min_length') 195 | call histdel('/', -1) 196 | endif 197 | if exists('#User#ObliqueStar') 198 | doautocmd User ObliqueStar 199 | endif 200 | catch 201 | echohl ErrorMsg 202 | echo substitute(v:exception, '.\{-}:', '', '') 203 | echohl None 204 | finally 205 | if !ws 206 | set nowrapscan 207 | endif 208 | endtry 209 | endfunction 210 | 211 | function! s:prefix_for(pat, highlight_all) 212 | if a:pat =~? '\\c' 213 | let prefix = '' 214 | elseif !&ignorecase || (&smartcase && s:strip_extra(a:pat) =~# '[A-Z]') 215 | let prefix = '\C' 216 | else 217 | let prefix = '\c' 218 | endif 219 | return a:highlight_all ? prefix : prefix . '\%'.line('.').'l\%'.col('.').'c' 220 | endfunction 221 | 222 | function! s:matchadd(...) 223 | let match_id = call('matchadd', a:000) 224 | if a:1 ==# 'ObliqueCurrentMatch' 225 | silent! call matchdelete(w:current_match_id) 226 | let w:current_match_id = match_id 227 | elseif a:1 ==# 'IncSearch' 228 | silent! call matchdelete(w:incsearch_id) 229 | let w:incsearch_id = match_id 230 | elseif a:1 ==# 'ObliqueCurrentIncSearch' 231 | silent! call matchdelete(w:current_incsearch_id) 232 | let w:current_incsearch_id = match_id 233 | endif 234 | endfunction 235 | 236 | function! g:_oblique_on_change(new, old, cursor) 237 | if getchar(1) != 0 238 | return 239 | endif 240 | 241 | let [pat, _] = s:build_pattern(a:new, s:backward ? '?' : '/', s:fuzzy) 242 | let pmatching = s:matching 243 | let empty = empty(a:new) || empty(s:strip_extra(pat)) 244 | if !empty && s:search(pat) 245 | call s:highlight_current_match('IncSearch', pat, s:optval('incsearch_highlight_all')) 246 | call s:highlight_current_match('ObliqueCurrentIncSearch', pat) 247 | let s:matching = pat 248 | else 249 | if empty 250 | call winrestview(s:view) 251 | endif 252 | silent! call matchdelete(w:incsearch_id) 253 | silent! call matchdelete(w:current_incsearch_id) 254 | let s:matching = '' 255 | endif 256 | if pmatching != s:matching 257 | redraw 258 | endif 259 | endfunction 260 | 261 | function! g:_oblique_on_unknown_key(code, new, cursor) 262 | if !empty(s:matching) && a:code == "\" 263 | let [a, b] = [getreg('a'), getreg('b')] 264 | let [at, bt] = [getregtype('a'), getregtype('b')] 265 | let slash = @/ 266 | try 267 | let @/ = s:matching 268 | normal! gn"ay 269 | normal! gnl"by 270 | let xtra = strpart(@b, len(@a)) 271 | if !empty(xtra) && xtra != "\n" 272 | if xtra == (s:backward ? '?' : '/') 273 | let xtra = '\'.xtra 274 | elseif (&ignorecase || &smartcase) && s:matching !~# '[A-Z]' 275 | let xtra = tolower(xtra) 276 | endif 277 | let new = strpart(a:new, 0, a:cursor) . xtra . strpart(a:new, a:cursor) 278 | let cursor = a:cursor + len(xtra) 279 | return [g:pseudocl#CONTINUE, new, cursor] 280 | endif 281 | finally 282 | call setreg('a', a, at) 283 | call setreg('b', b, bt) 284 | let @/ = slash 285 | endtry 286 | endif 287 | return [g:pseudocl#CONTINUE, a:new, a:cursor] 288 | endfunction 289 | 290 | function! s:set_autocmd() 291 | if !s:optval('clear_highlight') 292 | call s:clear_autocmd() 293 | return 294 | endif 295 | if !&hlsearch 296 | set hlsearch 297 | endif 298 | let b:_oblique_pos = [line('.'), col('.')] 299 | execute 'augroup Oblique'.bufnr('%') 300 | autocmd! 301 | autocmd CursorMoved call s:on_cursor_moved(0) 302 | autocmd InsertEnter call s:on_cursor_moved(1) 303 | autocmd WinLeave call s:on_win_leave() 304 | augroup END 305 | endfunction 306 | 307 | function! s:on_win_leave() 308 | silent! call matchdelete(w:current_match_id) 309 | augroup ObliqueExtra 310 | autocmd! 311 | autocmd CursorMoved * call s:on_win_enter() 312 | augroup END 313 | endfunction 314 | 315 | function! s:on_win_enter() 316 | augroup ObliqueExtra 317 | autocmd! 318 | autocmd CursorMoved * if exists('b:_oblique_pos') 319 | \| call s:on_cursor_moved(0) 320 | \| else 321 | \| set nohlsearch 322 | \| endif 323 | \| augroup ObliqueExtra 324 | \| execute 'autocmd!' 325 | \| augroup END 326 | \| augroup! ObliqueExtra 327 | augroup END 328 | endfunction 329 | 330 | function! s:clear_autocmd() 331 | let grp = 'Oblique'.bufnr('%') 332 | execute 'augroup' grp 333 | autocmd! 334 | augroup END 335 | " execute 'augroup!' grp 336 | endfunction 337 | 338 | function! s:clear() 339 | silent! call matchdelete(w:current_match_id) 340 | call s:clear_autocmd() 341 | endfunction 342 | 343 | function! s:on_cursor_moved(force) 344 | if a:force || !exists('b:_oblique_pos') || line('.') != b:_oblique_pos[0] || col('.') != b:_oblique_pos[1] 345 | set nohlsearch " function-search-undo 346 | call s:clear() 347 | return 1 348 | else 349 | return 0 350 | endif 351 | endfunction 352 | 353 | function! s:highlight_current_match(...) 354 | let group = a:0 > 0 ? a:1 : 'ObliqueCurrentMatch' 355 | let pat = a:0 > 1 ? a:2 : @/ 356 | let highlight_all = a:0 > 2 && a:3 357 | silent! call s:matchadd(group, s:prefix_for(pat, highlight_all) . '\&' . pat) 358 | endfunction 359 | 360 | if exists("*strdisplaywidth") 361 | function! s:strwidth(str) 362 | return strdisplaywidth(a:str) 363 | endfunction 364 | else 365 | function! s:strwidth(str) 366 | return len(a:str) 367 | endfunction 368 | endif 369 | 370 | function! s:echo_pattern(n) 371 | let xy = [&ruler, &showcmd] 372 | set noruler noshowcmd 373 | echon "\r\r" 374 | let bw = (a:n ==# 'n' ? s:backward : !s:backward) 375 | let sym = bw ? '?' : '/' 376 | let [prompt, str] = s:fuzzy ? ['F'.sym, s:input] : [sym, s:term] 377 | if !s:fuzzy && !empty(s:offset) 378 | let str .= sym . s:offset 379 | endif 380 | echohl ObliquePrompt | echon prompt 381 | let max_width = winwidth(winnr()) - 2 382 | if s:strwidth(prompt . str) > max_width 383 | echohl NonText | echon '..' 384 | let str = str[0 : (max_width - s:strwidth('..' . prompt))] 385 | endif 386 | echohl ObliqueLine | echon str | echohl None 387 | let [&ruler, &showcmd] = xy 388 | endfunction 389 | 390 | function! s:e486_fuzzy() 391 | return 'E486: Fuzzy pattern not found: '. s:input 392 | endfunction 393 | 394 | function! s:next(n, cnt, gv) 395 | let s:term = get(s:, 'term', @/) 396 | let s:offset = get(s:, 'offset', '') 397 | let s:count = get(s:, 'count', 1) 398 | call s:clear() 399 | if a:gv 400 | normal! gv 401 | endif 402 | try 403 | execute 'normal! '.a:cnt.a:n 404 | call s:highlight_current_match() 405 | call s:unfold() 406 | call s:set_autocmd() 407 | call s:echo_pattern(a:n) 408 | if exists('#User#ObliqueRepeat') 409 | doautocmd User ObliqueRepeat 410 | endif 411 | catch 412 | let msg = substitute(v:exception, '.\{-}:', '', '') 413 | echohl ErrorMsg 414 | if s:fuzzy && msg =~ '^E486' 415 | echo s:e486_fuzzy() 416 | else 417 | echo msg 418 | endif 419 | echohl None 420 | endtry 421 | endfunction 422 | 423 | function! s:oblique(gv, backward, fuzzy) 424 | let s:count = v:count1 425 | let s:backward = a:backward 426 | let was_fuzzy = s:fuzzy 427 | let s:fuzzy = a:fuzzy 428 | let s:ok = 0 429 | let s:stay = 0 430 | let s:view = winsaveview() 431 | let s:matching = '' 432 | 433 | if a:gv 434 | normal! gv 435 | endif 436 | 437 | let history = map(reverse(range(1, &history)), 'histget("/", -v:val)') 438 | let vmagic = s:optval('very_magic') ? '\V' : '' 439 | 440 | try 441 | let sym = s:backward ? '?' : '/' 442 | let opts = { 443 | \ 'prompt': ['ObliquePrompt', (s:count > 1 ? s:count : '') . (s:fuzzy ? 'F' : '') . sym], 444 | \ 'input': s:optval('prefix') . vmagic, 445 | \ 'history': history, 446 | \ 'map': s:optval('enable_cmap'), 447 | \ 'highlight': 'ObliqueLine' 448 | \ } 449 | if &incsearch 450 | let opts.on_change = function('g:_oblique_on_change') 451 | if !a:fuzzy 452 | let opts.on_unknown_key = function('g:_oblique_on_unknown_key') 453 | endif 454 | endif 455 | 456 | let s:input = pseudocl#start(opts) 457 | let [pat, offset] = s:build_pattern(s:input, sym, s:fuzzy) 458 | if s:search(pat) 459 | let s:ok = 1 460 | " FIXME: does not try to retain the position when offset part is given 461 | let s:stay = empty(offset) && s:stay 462 | let s:term = pat 463 | let s:offset = offset 464 | else 465 | call pseudocl#render#clear() 466 | echohl ErrorMsg 467 | if s:fuzzy 468 | echon s:e486_fuzzy() 469 | else 470 | echon 'E486: Pattern not found: '. pat.offset 471 | endif 472 | echohl None 473 | endif 474 | return pat 475 | catch 'exit' 476 | call pseudocl#render#clear() 477 | let s:fuzzy = was_fuzzy 478 | return @/ 479 | finally 480 | call winrestview(s:view) 481 | endtry 482 | endfunction 483 | 484 | function! s:escape_star_pattern(pat, backward) 485 | return substitute(escape(a:pat, '\' . (a:backward ? '?' : '/')), "\n", '\\n', 'g') 486 | endfunction 487 | 488 | function! s:star_search(backward, word, gv) 489 | silent! call matchdelete(w:current_match_id) 490 | 491 | let s:count = v:count1 492 | let s:backward = a:backward 493 | let s:fuzzy = 0 494 | let s:view = winsaveview() 495 | let s:ok = 1 496 | let s:stay = 0 497 | if a:gv 498 | let [xreg, xregtype] = [getreg('x'), getregtype('x')] 499 | silent! normal! gv"xy 500 | let s:star_word = @x 501 | let pat = '\V' . s:escape_star_pattern(s:star_word, a:backward) 502 | call setreg('x', xreg, xregtype) 503 | else 504 | let s:star_word = expand('') 505 | let esc = s:escape_star_pattern(s:star_word, a:backward) 506 | if empty(esc) 507 | let s:ok = 0 508 | echohl WarningMsg | echo 'No string under cursor' | echohl None 509 | return @/ 510 | endif 511 | let pat = (esc[0] =~ '\k' && a:word) ? ('\V\<' . esc . '\>') : ('\V' . esc) 512 | endif 513 | 514 | let s:term = pat 515 | let s:offset = '' 516 | return pat 517 | endfunction 518 | 519 | function! s:ok() 520 | if s:ok && &showcmd 521 | let s:showcmd = 1 522 | set noshowcmd 523 | endif 524 | return s:ok 525 | endfunction 526 | 527 | function! s:move(bw) 528 | let sym = (a:bw ? '?' : '/') 529 | let off = empty(s:offset) ? '' : (sym . s:offset) 530 | return "normal! ". s:count . sym . s:term . off . "\" . (s:stay ? 'N' : '') 531 | endfunction 532 | 533 | function! s:direction(forward) 534 | return a:forward ? (s:backward ? 'N' : 'n') : (s:backward ? 'n' : 'N') 535 | endfunction 536 | 537 | function! s:define_maps() 538 | silent! call pseudocl#nop() 539 | if !exists('*pseudocl#start') 540 | echoerr 'junegunn/vim-pseudocl not found' 541 | return 542 | endif 543 | 544 | " maps 545 | for fz in [0, 1] 546 | for bw in [0, 1] 547 | for m in ['n', 'o', 'x'] 548 | execute printf(m.'noremap (Oblique-%s) :let @/ = oblique(%s, %d, %d)' 549 | \ . 'if ok()silent execute move(%d)endifcall finish()', 550 | \ (fz ? 'F' : '') . (bw ? '?' : '/'), 551 | \ m == 'x', bw, fz, bw) 552 | endfor 553 | endfor 554 | endfor 555 | 556 | for [bw, w, cmd] in [[0, 1, '*'], [1, 1, '#'], [0, 0, 'g*'], [1, 0, 'g#']] 557 | for m in ['n', 'x'] 558 | if !w && m == 'x' | continue | endif 559 | execute printf(m.'noremap (Oblique-%s) :let @/ = star_search(%d, %d, %d)' 560 | \ . 'if ok()silent! execute move(%d)call finish_star(%d)endif', 561 | \ cmd, bw, w, m == 'x', bw, m == 'x') 562 | endfor 563 | endfor 564 | 565 | " Setup default maps 566 | for m in ['n', 'x', 'o'] 567 | for d in ['/', '?'] 568 | if !hasmapto('(Oblique-'.d.')', m) 569 | execute m.'map '.d.' (Oblique-'.d.')' 570 | endif 571 | if !hasmapto('(Oblique-F'.d.')', m) 572 | execute m.'map z'.d.' (Oblique-F'.d.')' 573 | endif 574 | endfor 575 | endfor 576 | 577 | for m in ['n', 'x'] 578 | if !hasmapto('(Oblique-*)', m) 579 | execute m."map * (Oblique-*)" 580 | endif 581 | if !hasmapto('(Oblique-#)', m) 582 | execute m."map # (Oblique-#)" 583 | endif 584 | endfor 585 | if !hasmapto('(Oblique-g*)', 'n') 586 | execute "nmap g* (Oblique-g*)" 587 | endif 588 | if !hasmapto('(Oblique-g#)', 'n') 589 | execute "nmap g# (Oblique-g#)" 590 | endif 591 | 592 | nnoremap (Oblique-n) :call next('n', v:count1, 0)if &hlsearchset hlsearchendif 593 | nnoremap (Oblique-N) :call next('N', v:count1, 0)if &hlsearchset hlsearchendif 594 | xnoremap (Oblique-n) :call next('n', v:count1, 1)if &hlsearchset hlsearchendif 595 | xnoremap (Oblique-N) :call next('N', v:count1, 1)if &hlsearchset hlsearchendif 596 | nnoremap (Oblique-n!) :call next(direction(1), v:count1, 0)if &hlsearchset hlsearchendif 597 | nnoremap (Oblique-N!) :call next(direction(0), v:count1, 0)if &hlsearchset hlsearchendif 598 | xnoremap (Oblique-n!) :call next(direction(1), v:count1, 1)if &hlsearchset hlsearchendif 599 | xnoremap (Oblique-N!) :call next(direction(0), v:count1, 1)if &hlsearchset hlsearchendif 600 | for m in ['n', 'x'] 601 | if empty(maparg('n', m)) 602 | execute m."map n (Oblique-n)" 603 | endif 604 | if empty(maparg('N', m)) 605 | execute m."map N (Oblique-N)" 606 | endif 607 | endfor 608 | 609 | if empty(maparg('gd', 'n')) 610 | nnoremap gd gd:normal * 611 | endif 612 | if empty(maparg('gD', 'n')) 613 | nnoremap gD gD:normal * 614 | endif 615 | if empty(maparg('gd', 'x')) 616 | xnoremap gd gd:normal *gv 617 | endif 618 | if empty(maparg('gD', 'x')) 619 | xnoremap gD gD:normal *gv 620 | endif 621 | 622 | nnoremap (Oblique-Repeat) :call repeat() 623 | endfunction 624 | 625 | call s:define_maps() 626 | 627 | let &cpo = s:cpo_save 628 | unlet s:cpo_save 629 | 630 | --------------------------------------------------------------------------------