Repository: henrik/vim-indexed-search Branch: master Commit: 763fdd0eb818 Files: 5 Total size: 19.1 KB Directory structure: gitextract_ydkyj2o8/ ├── .gitignore ├── README.md ├── autoload/ │ └── indexed_search.vim ├── doc/ │ └── indexed-search.txt └── plugin/ └── indexed-search.vim ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ doc/tags ================================================ FILE: README.md ================================================ # IndexedSearch.vim Requires vim7.4 Originally by [Yakov Lerner](http://www.vim.org/account/profile.php?user_id=2342) and put on GitHub by [Henrik Nyh](https://github.com/henrik) to have it there in a [Pathogen](http://www.vim.org/scripts/script.php?script_id=2332)-friendly format. Majorly rewritten by [Otto Modinos](https://github.com/otommod). [See the original plugin page at vim.org.](http://www.vim.org/scripts/script.php?script_id=1682) ``` This plugin redefines 6 search commands (/,?,n,N,*,#). At every search command, it automatically prints> "At match #N out of M matches". > -- the total number of matches (M) and the number(index) of current match (N). This helps to get oriented when searching forward and backward. There are no new commands and no new behavior to learn. Just watch the bottom line when you do /,?,n,N,*,#. ``` [See full help file.](https://github.com/henrik/vim-indexed-search/blob/master/doc/indexed-search.txt) ## Alternatives Is this plugin too slow for you? Do you want more (or less) features? Here're some other plugins that do (or can do) the same thing: * On Vim 8.1.1270+, you can use [`:set shortmess-=S`](https://stackoverflow.com/a/4671112/6962) * [google/vim-searchindex](https://github.com/google/vim-searchindex); very fast and unobtrusive * [osyo-manga/vim-anzu](https://github.com/osyo-manga/vim-anzu); tons of features * [romainl/vim-cool](https://github.com/romainl/vim-cool); initally just for disabling the highlighting of matches after a seach, now also show an index * [lacygoill/vim-search](https://github.com/lacygoill/vim-search); meant for [personal use](https://github.com/junegunn/vim-slash/issues/7) but can be used by everyone ================================================ FILE: autoload/indexed_search.vim ================================================ function! s:echohl(hl, msg) exec 'echohl' a:hl echo a:msg echohl None endfunction function! s:old_search(force) let winview = winsaveview() let line = winview["lnum"] let col = winview["col"] + 1 let [index, total, is_on_match, first_match_lnum, last_match_lnum] = [0, 0, 0, 0, 0] call cursor(1, 1) let [matchline, matchcol] = searchpos(@/, 'Wc') let first_match_lnum = matchline while matchline && (total <= g:indexed_search_max_hits || a:force) let total += 1 let last_match_lnum = matchline if matchline < line || (matchline == line && matchcol <= col) let index = total let is_on_match = matchline == line && matchcol == col endif let [matchline, matchcol] = searchpos(@/, 'W') endwhile let out_of_time = (!a:force && total > g:indexed_search_max_hits) \ + (!a:force && index > g:indexed_search_max_hits) call winrestview(winview) return [index, total, is_on_match, out_of_time, first_match_lnum, last_match_lnum] endfunction function! s:search(force) let [before, after, is_on_match, first_match_lnum, last_match_lnum] = [0, 0, 1, 0, 0] let now = reltime() let winview = winsaveview() let [save_ws, save_fen] = [&wrapscan, &foldenable] set nowrapscan nofoldenable " If we're at the last line and the file contains no EOL there, " `line2byte()` seems (to me) to give a wrong result. let eolbug = line('.') == line('$') && !&eol && (&bin || !&fixeol) " We need to find out whether the cursor is currently on a match or not " since that'll affect our numbering. Naturally, there's no easy way to " get such information. The hard way is to wiggle the cursor a bit and " try to search back and check if we ended up where we started. There are " two edge cases though. let curpos = getpos('.') if line2byte(line('$') + 1) <= 3 " The buffer is empty or has only one character. " In this case, we can't wiggle the cursor, so we just search and " check for the 'E486 Pattern not found' error. set wrapscan try silent keepjumps normal! n catch /^Vim[^)]\+):E486\D/ let is_on_match = 0 endtry set nowrapscan elseif line2byte('.') + col('.') - 1 <= 1 " We're at the very start of the buffer. " We move the cursor forwards. silent! keepjumps goto 2 silent! exec 'keepjumps normal!' (v:searchforward ? 'N' : 'n') else " In every other case, we move the cursor backwards. This works even " if we're at the very edge of the buffer which is nice because I " couldn't find any surefire way to check for that. silent! exec 'keepjumps goto' (line2byte('.') + col('.') - (eolbug ? 0 : 2)) silent! exec 'keepjumps normal!' (v:searchforward ? 'n' : 'N') endif if getpos('.') != curpos | let is_on_match = 0 | endif call winrestview(winview) " This is the algorithm itself; we first count all the matches before the " cursor and then all the ones after it. To count these, we first try " moving in tens; running '10n' is (mostly) the same as running 'n' 10 " times but it's faster since it runs in C. If however there are only, " say, 9 matches, Vim will internally run 'n' 9 times before announcing " that the 10th found no match but with no way to see how many matched; " other than counting them one-by-one. While this wastes some searches as " a whole it ends up being far faster than doing it all one-by-one. try while before <= g:indexed_search_max_hits || a:force " if reltimefloat(reltime(now)) > 0.1 | break | endif try silent keepjumps normal! 10N let before += 10 catch /^Vim[^)]\+):E38[45]\D/ try silent keepjumps normal! N let before += 1 catch /^Vim[^)]\+):E38[45]\D/ let first_match_lnum = line('.') break endtry endtry endwhile call winrestview(winview) while before + after <= g:indexed_search_max_hits || a:force " if reltimefloat(reltime(now)) > 0.1 | break | endif try silent keepjumps normal! 10n let after += 10 catch /^Vim[^)]\+):E38[45]\D/ try silent keepjumps normal! n let after += 1 catch /^Vim[^)]\+):E38[45]\D/ let last_match_lnum = line('.') break endtry endtry endwhile finally let [&wrapscan, &foldenable] = [save_ws, save_fen] call winrestview(winview) endtry if !v:searchforward let [after, before] = [before, after] let [first_match_lnum, last_match_lnum] = [last_match_lnum, first_match_lnum] end let out_of_time = (!a:force && before > g:indexed_search_max_hits) \ + (!a:force && after + before > g:indexed_search_max_hits) let index = before + is_on_match let total = before + after + is_on_match return [index, total, is_on_match, out_of_time, first_match_lnum, last_match_lnum] endfunction function! s:index_message(index, total, is_on_match, out_of_time, first_match_lnum, last_match_lnum) let hl = 'Directory' let msg = '' let matches = a:total if a:out_of_time let matches = '> '. a:total if !a:is_on_match || a:out_of_time > 1 return [hl, matches .' matches'] endif endif let line_info = "" if g:indexed_search_line_info let line_info = ' (FM:'. a:first_match_lnum .', LM:'. a:last_match_lnum .')' endif let shortmatch = matches . line_info . (g:indexed_search_shortmess ? '' : ' matches') if a:total == 0 let hl = 'Error' let msg = 'No matches' elseif !a:is_on_match && a:index == 0 let hl = 'WarningMsg' let msg = 'Before first match, of '. shortmatch if a:total == 1 | let msg = 'Before single match' | endif elseif !a:is_on_match && a:index == a:total let hl = 'WarningMsg' let msg = 'After last match of '. shortmatch if a:total == 1 | let msg = 'After single match' | endif elseif !a:is_on_match " hl remains default let msg = 'Between matches '. a:index .'-'. (a:index+1) .' of '. matches . line_info elseif !g:indexed_search_numbered_only && a:index == 1 && a:total == 1 let hl = 'Search' let msg = 'Single match' elseif !g:indexed_search_numbered_only && a:index == 1 let hl = 'Search' let msg = 'First of '. shortmatch elseif !g:indexed_search_numbered_only && a:index == a:total let hl = 'LineNr' let msg = 'Last of '. shortmatch else " hl remains default let msg = (g:indexed_search_shortmess ? '' : 'Match '). a:index .' of '. matches . line_info endif return [hl, msg.' /'.@/.'/'] endfunction function! indexed_search#show_index(force) if @/ == '' || (!a:force && line('$') >= g:indexed_search_max_lines) return endif let results = s:search(a:force) let [hl, msg] = call('s:index_message', results) call s:echohl(g:indexed_search_colors ? hl : 'None', msg) endfunction ================================================ FILE: doc/indexed-search.txt ================================================ *indexed-search.txt* Show match's count and index with search command Author: Yakov Lerner Otto Modinos INTRODUCTION *indexed-search* This plugin redefines 6 search commands (/,?,n,N,*,#). At every search command, it automatically prints > "At match #N out of M matches". < -- the total number of matches (M) and the number(index) of current match (N). This helps to get oriented when searching forward and backward. There's only one command and no new behavior to learn. Just watch the bottom line when you do /,?,n,N,*,#. Works on vim7. Won't cause slowdown on very large files (but then counters are not displayed). ============================================================================= COMMANDS *indexed-search-commands* *:ShowSearchIndex* :ShowSearchIndex Shows a message indicating your position relative to the search matches in the file. The message's content also depends on the value of the |g:indexed_search_shortmess| |g:indexed_search_line_info| and |g:indexed_search_colors| options. ============================================================================= OPTIONS *indexed-search-options* *g:indexed_search_mappings* g:indexed_search_mappings If 0, does not create mappings. |indexed-search-mappings| Default: 1 *g:indexed_search_dont_move* g:indexed_search_dont_move If 1, the mappings for * and # stay on the word under the cursor. Basically, *N (or #N). Default: 0 *g:indexed_search_center* g:indexed_search_center If 1, the mappings for n and N also center the cursor on the match. Basically, nzz (or Nzz). Default: 0 *g:indexed_search_max_lines* g:indexed_search_max_lines If a file has more lines than this, the plugin doesn't display messages, for performance reasons. |indexed-search-performance| Default: 3000 *g:indexed_search_max_hits* g:indexed_search_max_hits When there are more matches than this, the plugin doesn't try to count all of them. Instead it says, e.g. "> 1000 matches" Default: 1000 |indexed-search-performance| *g:indexed_search_colors* g:indexed_search_colors If 0, the messages are not displayed in color Default: 1 *g:indexed_search_line_info* g:indexed_search_line_info If 1, the messages also mention the first and the last match line number in the following format: FM:, LM: Default: 0 *g:indexed_search_shortmess* g:indexed_search_shortmess If 1, the messages are (a bit) shorter. Default: 0 *g:indexed_search_numbered_only* g:indexed_search_numbered_only If 1, the messages only contain the search count. No "First of" and "Last of". Default: 0 *g:indexed_search_n_always_searches_forward* g:indexed_search_n_always_searches_forward In vim, by default, the direction of n and N depends on whether / or ? was used for searching forward or backward respectively. If 1, n always search forward and N backward Default: 0 ============================================================================= MAPPINGS *indexed-search-mappings* Indexed Search remaps the standard Vim keys /, ?, *, #, n and N, so that the appropriate message is displayed afterwards. If you don't want that, you can disable these mappings by letting |g:indexed_search_mappings| to 0. ============================================================================= PERFORMANCE *indexed-search-performance* Plugin bypasses the calculation of match index when it would take too much time (too many matches, too large file). You can use |g:indexed_search_max_lines| and |g:indexed_search_max_hits| to tune these performance limits. vim:tw=78:ts=8:ft=help:norl: ================================================ FILE: plugin/indexed-search.vim ================================================ " Author: Yakov Lerner " URL: http://www.vim.org/scripts/script.php?script_id=1682 " Last change: 2018-03-21 " This script redefines 6 search commands (/,?,n,N,*,#). At each search, it " shows at which match number you are, and the total number of matches, like " this: "At nth match out of N". This is printed at the bottom line at every " n,N,/,?,*,# search command, automatically. " " I am posting this plugin because I find it useful. " :ShowSearchIndex - Checking your match index " ----------------------------------------------------- " At any time, you can use :ShowSearchIndex to show at which match index you " are without moving the cursor. " " If cursor is exactly on the match, the message is: " At Nth match of M " If cursor is between matches, following messages are displayed: " Betwen matches 189-190 of 300 " Before first match, of 300 " After last match, of 300 " To disable colors for messages, set g:indexed_search_colors to 0. " " Performance " ------------------------------------------------------ " Plugin bypasses match counting when it would take too much time, i.e. too " many matches or too large a file. You can change these limits with " g:indexed_search_max_lines and g:indexed_search_max_hits. if exists("g:loaded_indexed_search") || &cp || v:version < 700 finish endif let g:loaded_indexed_search = 1 let s:save_cpo = &cpo set cpo&vim " Performance tuning limits if !exists('g:indexed_search_max_lines') " Max filesize (in lines) up to where the plugin works let g:indexed_search_max_lines = 30000 endif if !exists("g:indexed_search_max_hits") " Max number of matches up to where the plugin stops counting let g:indexed_search_max_hits = 1000 endif " Appearance settings if !exists('g:indexed_search_colors') " Whether to use colors for messages let g:indexed_search_colors = 1 endif if !exists('g:indexed_search_shortmess') " Make messages shorter let g:indexed_search_shortmess = 0 endif if !exists('g:indexed_search_numbered_only') " Only show index number, no extra words let g:indexed_search_numbered_only = 0 endif if !exists('g:indexed_search_line_info') let g:indexed_search_line_info = 0 endif " Mappings if !exists('g:indexed_search_mappings') let g:indexed_search_mappings = 1 endif if !exists('g:indexed_search_dont_move') let g:indexed_search_dont_move = 0 endif if !exists('g:indexed_search_center') let g:indexed_search_center = 0 endif if !exists('g:indexed_search_n_always_searches_forward') let g:indexed_search_n_always_searches_forward = 0 endif command! -bang ShowSearchIndex :call indexed_search#show_index(0) function! s:should_unfold() return has('folding') && &fdo =~ 'search\|all' endfunction function! s:has_mapping(name) return !empty(maparg(a:name, mode())) endfunction function! s:restview() call winrestview(s:winview) endfunction function! s:star(seq) if g:indexed_search_dont_move let s:winview = winsaveview() return a:seq . "\(indexed-search-restview)" endif return a:seq endfunction function! s:n(seq) if g:indexed_search_n_always_searches_forward && !v:searchforward return ["\(indexed-search-n)", "\(indexed-search-N)"][a:seq ==# 'n'] endif return a:seq endfunction function! s:after() return (s:should_unfold() ? 'zv' : '') \ .(g:indexed_search_center ? 'zz' : '') \ .(s:has_mapping('(indexed-search-custom)') ? "\(indexed-search-custom)" : '') \ ."\(indexed-search-index)" endfunction if g:indexed_search_mappings noremap (indexed-search-index) nnoremap (indexed-search-index) :ShowSearchIndex xnoremap (indexed-search-index) :ShowSearchIndexgv noremap (indexed-search-n) n noremap (indexed-search-N) N noremap (indexed-search-restview) :call restview() xnoremap (indexed-search-restview) :call restview()gv map (indexed-search-after) after() imap (indexed-search-after) cmap "\" . (getcmdtype() =~ '[/?]' ? "\(indexed-search-after)" : '') " map gd 'gd' . "\(indexed-search-after)" " map gD 'gD' . "\(indexed-search-after)" map * star('*') . "\(indexed-search-after)" map # star('#') . "\(indexed-search-after)" map g* star('g*') . "\(indexed-search-after)" map g# star('g#') . "\(indexed-search-after)" map n n('n') . "\(indexed-search-after)" map N n('N') . "\(indexed-search-after)" endif let &cpo = s:save_cpo " Wishlist " - using high-precision timer of vim7, count number of millisec " to run the counters, and base auto-disabling on time it takes. " very complex regexes can be terribly slow even of files like 'man bash' " which is mere 5k lines long. Also when there are >10k matches in the file " set limit to 200 millisec " - implement CursorHold bg counting to which too_slow will resort " - even on large files, we can show "At last match", "After last match" " - define global vars for all highlights, with defaults