Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1 | " Vim Ada plugin file |
| 2 | " Language: Ada |
| 3 | " Maintainer: Neil Bird <neil@fnxweb.com> |
| 4 | " Last Change: 2003 May 11 |
| 5 | " Version: $Id$ |
| 6 | " Look for the latest version at http://vim.sourceforge.net/ |
| 7 | " |
| 8 | " Perform Ada specific completion & tagging. |
| 9 | " |
| 10 | " |
| 11 | " Provides mapping overrides for tag jumping that figure out the current |
| 12 | " Ada object and tag jump to that, not the 'simple' vim word. |
| 13 | " Similarly allows <Ctrl-N> matching of full-length ada entities from tags. |
| 14 | " Exports 'AdaWord()' function to return full name of Ada entity under the |
| 15 | " cursor( or at given line/column), stripping whitespace/newlines as necessary. |
| 16 | |
| 17 | |
| 18 | " Only do this when not done yet for this buffer |
| 19 | if exists("b:did_ftplugin") |
| 20 | finish |
| 21 | endif |
| 22 | |
| 23 | " Don't load another plugin for this buffer |
| 24 | let b:did_ftplugin = 1 |
| 25 | |
| 26 | " Temporarily set cpoptions to ensure the script loads OK |
| 27 | let s:cpoptions = &cpoptions |
| 28 | set cpo-=C |
| 29 | |
| 30 | |
| 31 | " Ada comments |
| 32 | setlocal comments+=O:-- |
| 33 | |
| 34 | |
| 35 | " Make local tag mappings for this buffer (if not already set) |
| 36 | if mapcheck('<C-]>','n') == '' |
| 37 | nnoremap <unique> <buffer> <C-]> :call JumpToTag_ada('')<cr> |
| 38 | endif |
| 39 | if mapcheck('g<C-]>','n') == '' |
| 40 | nnoremap <unique> <buffer> g<C-]> :call JumpToTag_ada('','stj')<cr> |
| 41 | endif |
| 42 | |
| 43 | if mapcheck('<C-N>','i') == '' |
| 44 | inoremap <unique> <buffer> <C-N> <C-R>=<SID>AdaCompletion("\<lt>C-N>")<cr> |
| 45 | endif |
| 46 | if mapcheck('<C-P>','i') == '' |
| 47 | inoremap <unique> <buffer> <C-P> <C-R>=<SID>AdaCompletion("\<lt>C-P>")<cr> |
| 48 | endif |
| 49 | if mapcheck('<C-X><C-]>','i') == '' |
| 50 | inoremap <unique> <buffer> <C-X><C-]> <C-R>=<SID>AdaCompletion("\<lt>C-X>\<lt>C-]>")<cr> |
| 51 | endif |
| 52 | if mapcheck('<bs>','i') == '' |
| 53 | inoremap <silent> <unique> <buffer> <bs> <C-R>=<SID>AdaInsertBackspace()<cr> |
| 54 | endif |
| 55 | |
| 56 | |
| 57 | " Only do this when not done yet for this buffer & matchit is used |
| 58 | if ! exists("b:match_words") && exists("loaded_matchit") |
| 59 | " The following lines enable the macros/matchit.vim plugin for |
| 60 | " Ada-specific extended matching with the % key. |
| 61 | let s:notend = '\%(\<end\s\+\)\@<!' |
| 62 | let b:match_words= |
| 63 | \ s:notend . '\<if\>:\<elsif\>:\<else\>:\<end\>\s\+\<if\>,' . |
| 64 | \ s:notend . '\<case\>:\<when\>:\<end\>\s\+\<case\>,' . |
| 65 | \ '\%(\<while\>.*\|\<for\>.*\|'.s:notend.'\)\<loop\>:\<end\>\s\+\<loop\>,' . |
| 66 | \ '\%(\<do\>\|\<begin\>\):\<exception\>:\<end\>\s*\%($\|[;A-Z]\),' . |
| 67 | \ s:notend . '\<record\>:\<end\>\s\+\<record\>' |
| 68 | endif |
| 69 | |
| 70 | |
| 71 | " Prevent re-load of functions |
| 72 | if exists('s:id') |
| 73 | finish |
| 74 | endif |
| 75 | |
| 76 | " Get this script's unique id |
| 77 | map <script> <SID>?? <SID>?? |
| 78 | let s:id = substitute( maparg('<SID>??'), '^<SNR>\(.*\)_??$', '\1', '' ) |
| 79 | unmap <script> <SID>?? |
| 80 | |
| 81 | |
| 82 | " Extract current Ada word across multiple lines |
| 83 | " AdaWord( [line, column] )\ |
| 84 | let s:AdaWordRegex = '\a\w*\(\_s*\.\_s*\a\w*\)*' |
| 85 | let s:AdaComment = "\\v^(\"[^\"]*\"|'.'|[^\"']){-}\\zs\\s*--.*" |
| 86 | |
| 87 | function! AdaWord(...) |
| 88 | if a:0 > 1 |
| 89 | let linenr = a:1 |
| 90 | let colnr = a:2 - 1 |
| 91 | else |
| 92 | let linenr = line('.') |
| 93 | let colnr = col('.') - 1 |
| 94 | endif |
| 95 | let line = substitute( getline(linenr), s:AdaComment, '', '' ) |
| 96 | " Cope with tag searching for items in comments; if we are, don't loop |
| 97 | " backards looking for previous lines |
| 98 | if colnr > strlen(line) |
| 99 | " We were in a comment |
| 100 | let line = getline(linenr) |
| 101 | let search_prev_lines = 0 |
| 102 | else |
| 103 | let search_prev_lines = 1 |
| 104 | endif |
| 105 | |
| 106 | " Go backwards until we find a match (Ada ID) that *doesn't* include our |
| 107 | " location - i.e., the previous ID. This is because the current 'correct' |
| 108 | " match will toggle matching/not matching as we traverse characters |
| 109 | " backwards. Thus, we have to find the previous unrelated match, exclude |
| 110 | " it, then use the next full match (ours). |
| 111 | " Remember to convert vim column 'colnr' [1..n] to string offset [0..(n-1)] |
| 112 | " ... but start, here, one after the required char. |
| 113 | let newcol = colnr + 1 |
| 114 | while 1 |
| 115 | let newcol = newcol - 1 |
| 116 | if newcol < 0 |
| 117 | " Have to include previous line from file |
| 118 | let linenr = linenr - 1 |
| 119 | if linenr < 1 || !search_prev_lines |
| 120 | " Start of file or matching in a comment |
| 121 | let linenr = 1 |
| 122 | let newcol = 0 |
| 123 | let ourmatch = match( line, s:AdaWordRegex ) |
| 124 | break |
| 125 | endif |
| 126 | " Get previous line, and prepend it to our search string |
| 127 | let newline = substitute( getline(linenr), s:AdaComment, '', '' ) |
| 128 | let newcol = strlen(newline) - 1 |
| 129 | let colnr = colnr + newcol |
| 130 | let line = newline . line |
| 131 | endif |
| 132 | " Check to see if this is a match excluding 'us' |
| 133 | let mend = newcol + matchend( strpart(line,newcol), s:AdaWordRegex ) - 1 |
| 134 | if mend >= newcol && mend < colnr |
| 135 | " Yes |
| 136 | let ourmatch = mend+1 + match( strpart(line,mend+1), s:AdaWordRegex ) |
| 137 | break |
| 138 | endif |
| 139 | endwhile |
| 140 | |
| 141 | " Got anything? |
| 142 | if ourmatch < 0 |
| 143 | return '' |
| 144 | else |
| 145 | let line = strpart( line, ourmatch) |
| 146 | endif |
| 147 | |
| 148 | " Now simply add further lines until the match gets no bigger |
| 149 | let matchstr = matchstr( line, s:AdaWordRegex ) |
| 150 | let lastline = line('$') |
| 151 | let linenr = line('.') + 1 |
| 152 | while linenr <= lastline |
| 153 | let lastmatch = matchstr |
| 154 | let line = line . substitute( getline(linenr), s:AdaComment, '', '' ) |
| 155 | let matchstr = matchstr( line, s:AdaWordRegex ) |
| 156 | if matchstr == lastmatch |
| 157 | break |
| 158 | endif |
| 159 | endwhile |
| 160 | |
| 161 | " Strip whitespace & return |
| 162 | return substitute( matchstr, '\s\+', '', 'g' ) |
| 163 | endfunction |
| 164 | |
| 165 | |
| 166 | " Word tag - include '.' and if Ada make uppercase |
| 167 | " Name allows a common JumpToTag() to look for an ft specific JumpToTag_ft(). |
| 168 | function! JumpToTag_ada(word,...) |
| 169 | if a:word == '' |
| 170 | " Get current word |
| 171 | let word = AdaWord() |
| 172 | if word == '' |
| 173 | return |
| 174 | endif |
| 175 | else |
| 176 | let word = a:word |
| 177 | endif |
| 178 | if a:0 > 0 |
| 179 | let mode = a:1 |
| 180 | else |
| 181 | let mode = 'tj' |
| 182 | endif |
| 183 | |
| 184 | let v:errmsg = '' |
| 185 | execute 'silent!' mode word |
| 186 | if v:errmsg != '' |
| 187 | if v:errmsg =~ '^E426:' " Tag not found |
| 188 | let ignorecase = &ignorecase |
| 189 | set ignorecase |
| 190 | execute mode word |
| 191 | let &ignorecase = ignorecase |
| 192 | else |
| 193 | " Repeat to give error |
| 194 | execute mode word |
| 195 | endif |
| 196 | endif |
| 197 | endfunction |
| 198 | |
| 199 | |
| 200 | " Word completion (^N/^R/^X^]) - force '.' inclusion |
| 201 | function! s:AdaCompletion(cmd) |
| 202 | set iskeyword+=46 |
| 203 | return a:cmd . "\<C-R>=<SNR>" . s:id . "_AdaCompletionEnd()\<CR>" |
| 204 | endfunction |
| 205 | function! s:AdaCompletionEnd() |
| 206 | set iskeyword-=46 |
| 207 | return '' |
| 208 | endfunction |
| 209 | |
| 210 | |
| 211 | " Backspace at end of line after auto-inserted commentstring '-- ' wipes it |
| 212 | function! s:AdaInsertBackspace() |
| 213 | let line = getline('.') |
| 214 | if col('.') > strlen(line) && match(line,'-- $') != -1 && match(&comments,'--') != -1 |
| 215 | return "\<bs>\<bs>\<bs>" |
| 216 | else |
| 217 | return "\<bs>" |
| 218 | endif |
| 219 | endfunction |
| 220 | |
| 221 | |
| 222 | " Reset cpoptions |
| 223 | let &cpoptions = s:cpoptions |
| 224 | unlet s:cpoptions |
| 225 | |
| 226 | " vim: sts=2 sw=2 : |