├── CHANGELOG.md ├── README.md ├── autoload └── ctrlp │ └── tjump.vim └── plugin └── tjump.vim /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.3.0 2 | 3 | Add `g:ctrlp_tjump_skip_tag_name` to skip tag name (thanks @Konfekt) 4 | 5 | # 0.2.0 6 | 7 | Support all CtrlP file open modes (current window, new tab, vertical split, horizontal split) 8 | 9 | # 0.1.0 10 | 11 | * Add CtrlPtjumpVisual command to go to declaration of the selected text 12 | * Bugfixes. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CtrlP tjump 2 | 3 | CtrlP extension for fuzzy-search in tag matches. 4 | May be used instead of `:tjump` or `:tselect` for IDE-like `Goto declaration` functionality, 5 | which jumps to the declaration in case of one match, and shows quick-search window in case of multiple matches. 6 | 7 | Two vim commands are created by this plugin: 8 | 9 | * `CtrlPtjump` - go to declaration of the identifier supplied as an argument, if not use the word under cursor 10 | * `CtrlPtjumpVisual` - go to declaration of the visual selected text 11 | 12 | ![CtrlP tjump][1] 13 | 14 | ## Prerequisites 15 | 16 | 1. [CtrlP][2] should be installed 17 | 2. Tags should already work with `:tag`, `:tselect` and `:tjump` commands. More information on that [here](http://vim.wikia.com/wiki/Browsing_programs_with_tags). 18 | 19 | ## Installation 20 | 21 | 1. Use your favorite method (I prefer [Vundle][3]) 22 | 2. (Optional) Create mapping 23 | 24 | ``` 25 | nnoremap :CtrlPtjump 26 | vnoremap :CtrlPtjumpVisual 27 | ``` 28 | 29 | ## Basic Usage 30 | 31 | 1. Move cursor to the Class/Method usage in your code 32 | 2. Press `c-]` (if you have created mapping) or just execute `:CtrlPtjump` 33 | (or `:CtrlPtjumpVisual` in visual mode) in the command line. 34 | 35 | Or provide the symbol as an argument: 36 | 37 | :CtrlPtjump MyFavoriteClass 38 | 39 | ## Configuration 40 | 41 | It is possible to configure shortener for filenames, that will be displayed in 42 | CtrlP window. In the example below, RegExp `'/home/.*/gems/'` will be 43 | substituted by string `'.../'`. This may be useful if filename (with path) of 44 | generated tag is long and you want to make it shorter. 45 | 46 | let g:ctrlp_tjump_shortener = ['/home/.*/gems/', '.../'] 47 | 48 | If there is only one tag found, it is possible to open it without opening CtrlP 49 | window: 50 | 51 | let g:ctrlp_tjump_only_silent = 1 52 | 53 | The tag name itself takes valuable screen estate and can be disabled by: 54 | 55 | let g:ctrlp_tjump_skip_tag_name = 1 56 | 57 | ## Contributing 58 | 59 | 1. Fork it 60 | 2. Create your feature branch (`git checkout -b my-new-feature`) 61 | 3. Commit your changes (`git commit -am 'Added some feature'`) 62 | 4. Push to the branch (`git push origin my-new-feature`) 63 | 5. Create new Pull Request 64 | 65 | ## Self-Promotion 66 | 67 | If you like this project, please follow the repository on [GitHub](https://github.com/ivalkeen/vim-ctrlp-tjump) and vote for it on 68 | [vim.org](http://www.vim.org/scripts/script.php?script_id=4673). Also, you might consider visiting my [blog](http://www.tkalin.com) and following me on [Twitter](https://twitter.com/ivalkeen) and [Github](https://github.com/ivalkeen). 69 | 70 | 71 | [1]: http://i.imgur.com/1UrMOpd.png 72 | [2]: https://github.com/kien/ctrlp.vim 73 | [3]: https://github.com/gmarik/vundle 74 | -------------------------------------------------------------------------------- /autoload/ctrlp/tjump.vim: -------------------------------------------------------------------------------- 1 | if (exists('g:loaded_ctrlp_tjump') && g:loaded_ctrlp_tjump) 2 | \ || v:version < 700 || &cp 3 | finish 4 | endif 5 | let g:loaded_ctrlp_tjump = 1 6 | 7 | " 8 | " configuration options 9 | 10 | " replace expression with pattern in filenames' list 11 | " let g:ctrlp_tjump_shortener = ['scp://.*gems/', '.../'] 12 | 13 | " Skip selection window if only one match found 14 | if !exists('g:ctrlp_tjump_only_silent') | let g:ctrlp_tjump_only_silent = 0 | en 15 | 16 | " Skip tag name in list 17 | if !exists('g:ctrlp_tjump_skip_tag_name') | let g:ctrlp_tjump_skip_tag_name = 0 | en 18 | 19 | call add(g:ctrlp_ext_vars, { 20 | \ 'init': 'ctrlp#tjump#init()', 21 | \ 'accept': 'ctrlp#tjump#accept', 22 | \ 'lname': 'tjump', 23 | \ 'sname': 'tjump', 24 | \ 'type': 'line', 25 | \ 'enter': 'ctrlp#tjump#enter()', 26 | \ 'exit': 'ctrlp#tjump#exit()', 27 | \ 'opts': 'ctrlp#tjump#opts()', 28 | \ 'sort': 0, 29 | \ 'specinput': 0, 30 | \ }) 31 | 32 | function! ctrlp#tjump#exec(mode, ...) 33 | if a:mode == 'v' 34 | let s:word = s:get_visual_selection() 35 | else 36 | if exists('a:1') 37 | let s:word = a:1 38 | else 39 | if (&filetype == 'ruby' || &filetype == 'eruby') && exists("*RubyCursorIdentifier") 40 | let s:word = RubyCursorIdentifier() 41 | else 42 | let s:word = expand('') 43 | endif 44 | en 45 | endif 46 | 47 | let s:taglist = taglist('^'.s:word.'$') 48 | let s:bname = fnamemodify(bufname('%'), ':p') 49 | 50 | if len(s:taglist) == 0 51 | echo("No tags found for: ".s:word) 52 | elseif len(s:taglist) == 1 && g:ctrlp_tjump_only_silent == 1 53 | call feedkeys(":silent! tag ".s:word."\r", 'nt') 54 | else 55 | call ctrlp#init(ctrlp#tjump#id()) 56 | endif 57 | endfunction 58 | 59 | " Provide a list of strings to search in 60 | " 61 | " Return: a Vim's List 62 | " 63 | function! ctrlp#tjump#init() 64 | let tgs = s:order_tags() 65 | let max_short_filename = s:maxlen(tgs, 'short_filename') + 1 66 | let input = map(tgs, ' 67 | \ s:align_left(v:key + 1, 3) . "\t" . 68 | \ v:val["pri"] . "\t" . 69 | \ v:val["kind"] . "\t" . 70 | \ (g:ctrlp_tjump_skip_tag_name ? "" : v:val["name"] . "\t") . 71 | \ s:align_right(v:val["short_filename"], max_short_filename)."\t". 72 | \ v:val["short_cmd"] 73 | \ ') 74 | 75 | if !ctrlp#nosy() 76 | cal ctrlp#hicheck('CtrlPTabExtra', 'Comment') 77 | 78 | if g:ctrlp_tjump_skip_tag_name 79 | sy match CtrlPTabExtra `\(.\{-}\t\)\{3}` 80 | else 81 | sy match CtrlPTabExtra `\(.\{-}\t\)\{4}` 82 | endif 83 | 84 | sy match CtrlPTabExtra `.\{-}\t\zs.*\ze` 85 | en 86 | return input 87 | endfunction 88 | 89 | " The action to perform on the selected string 90 | " 91 | " Arguments: 92 | " a:mode the mode that has been chosen by pressing or 93 | " the values are 'e', 'v', 't' and 'h', respectively 94 | " a:str the selected string 95 | " 96 | function! ctrlp#tjump#accept(mode, str) 97 | " For this example, just exit ctrlp and run help 98 | call ctrlp#exit() 99 | call s:open_tag(a:str, a:mode) 100 | endfunction 101 | 102 | " (optional) Do something before enterting ctrlp 103 | function! ctrlp#tjump#enter() 104 | endfunction 105 | 106 | " (optional) Do something after exiting ctrlp 107 | function! ctrlp#tjump#exit() 108 | endfunction 109 | 110 | " (optional) Set or check for user options specific to this extension 111 | function! ctrlp#tjump#opts() 112 | endfunction 113 | 114 | 115 | " Give the extension an ID 116 | let s:id = g:ctrlp_builtins + len(g:ctrlp_ext_vars) 117 | 118 | " Allow it to be called later 119 | function! ctrlp#tjump#id() 120 | return s:id 121 | endfunction 122 | 123 | function! s:open_tag(str, mode) 124 | " If 'cscopetag' is set, the 'tag' command will actually use the 'cstag' 125 | " command which in turn performs a 'tjump'. Since 'tjump' doesn't support 126 | " ranges, if there is more than one match, the default tags menu is 127 | " displayed. To work around this, we temporarily disable using 'cstag', 128 | " however, in order to restore the option after a selection has been made we 129 | " have to use 'exec' instead of 'feedkeys', otherwise the script will exit 130 | " with the options restored before the 'tag' command is actually run. 131 | let cstopt = &cst 132 | set nocst 133 | let idx = split(a:str, '\t')[0] 134 | if a:mode == 'e' 135 | exec ":silent! ".idx."tag ".s:word 136 | elseif a:mode == 't' 137 | exec ":silent! tab ".idx."tag ".s:word 138 | elseif a:mode == 'v' 139 | exec ":silent! vertical ".idx."stag ".s:word 140 | elseif a:mode == 'h' 141 | exec ":silent! ".idx."stag ".s:word 142 | end 143 | let &cst = cstopt 144 | endfunction 145 | 146 | function! s:get_visual_selection() 147 | let [lnum1, col1] = getpos("'<")[1:2] 148 | let [lnum2, col2] = getpos("'>")[1:2] 149 | let lines = getline(lnum1, lnum2) 150 | let lines[-1] = lines[-1][: col2 - (&selection == 'inclusive' ? 1 : 2)] 151 | let lines[0] = lines[0][col1 - 1:] 152 | return join(lines, "\n") 153 | endfunction 154 | 155 | " Order must match tselect's order (see :help tag-priority) 156 | function! s:order_tags() 157 | let [FSC, F_C, F__, FS_, _SC, __C, ___, _S_] = [[], [], [], [], [], [], [], []] 158 | 159 | for tgi in s:taglist 160 | let priority = s:priority(tgi) 161 | let lst = substitute(priority, ' ', '_', 'g') 162 | let tgi['pri'] = priority 163 | let tgi['short_cmd'] = s:short_cmd(tgi) 164 | let tgi['short_filename'] = s:short_filename(tgi['filename']) 165 | call call('add', [{lst}, tgi]) 166 | endfo 167 | 168 | return FSC + F_C + F__ + FS_ + _SC + __C + ___ + _S_ 169 | endfunction 170 | 171 | function! s:align_left(str, width) 172 | let pad = a:width - strlen(a:str) 173 | return repeat(' ', pad).a:str 174 | endfunction 175 | 176 | function! s:align_right(str, width) 177 | let pad = a:width - strlen(a:str) 178 | return a:str.repeat(' ', pad) 179 | endfunction 180 | 181 | function! s:maxlen(tgs, key) 182 | let max = 0 183 | for tgi in a:tgs 184 | let len = strlen(tgi[a:key]) 185 | if len > max 186 | let max = len 187 | endif 188 | endfo 189 | return max 190 | endfunction 191 | 192 | " Return the FSC priority string of a tag, see :help tag-priority 193 | function! s:priority(tgi) 194 | let c_full_match = s:word ==# a:tgi['name'] ? 'F' : ' ' 195 | let c_static_tag = 1 == a:tgi['static'] ? 'S' : ' ' 196 | let c_current_file = s:bname == fnamemodify(a:tgi['filename'], ':p') ? 'C' : ' ' 197 | let priority = c_full_match.c_static_tag.c_current_file 198 | return priority 199 | endfunction 200 | 201 | " Extract the trimmed cmd string between prefix and suffix 202 | " Valid tag cmd prefixes: /^ | ?^ | / | ? 203 | " Valid tag cmd suffixes: $/ | $? | / | ? 204 | function! s:short_cmd(tgi) 205 | let short_cmd = substitute(a:tgi['cmd'], '\v^(/\^|\?\^|/|\?)?\s*(.{-})\s*(\$/|\$\?|/|\?)?$', '\2', '') 206 | return short_cmd 207 | endfunction 208 | 209 | " Shorten file name 210 | function! s:short_filename(filename) 211 | if exists('g:ctrlp_tjump_shortener') 212 | let short_filename = substitute(a:filename, g:ctrlp_tjump_shortener[0], g:ctrlp_tjump_shortener[1], 'g') 213 | else 214 | let short_filename = a:filename 215 | end 216 | return short_filename 217 | endfunction 218 | 219 | " vim:sw=2:ts=2:et 220 | -------------------------------------------------------------------------------- /plugin/tjump.vim: -------------------------------------------------------------------------------- 1 | command! -nargs=? CtrlPtjump call ctrlp#tjump#exec('n', ) 2 | command! -range CtrlPtjumpVisual ,call ctrlp#tjump#exec('v') 3 | --------------------------------------------------------------------------------