summaryrefslogtreecommitdiff
path: root/runtime/ftplugin/ada.vim
blob: 13fa8f93f475f84fccd799541de8d553eb1a4185 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
" Vim Ada plugin file
" Language:	Ada
" Maintainer:	Neil Bird <neil@fnxweb.com>
" Last Change:	2006 Apr 21
" Version:	$Id$
" Look for the latest version at http://vim.sourceforge.net/
"
" Perform Ada specific completion & tagging.
"
"
" Provides mapping overrides for tag jumping that figure out the current
" Ada object and tag jump to that, not the 'simple' vim word.
" Similarly allows <Ctrl-N> matching of full-length ada entities from tags.
" Exports 'AdaWord()' function to return full name of Ada entity under the
" cursor( or at given line/column), stripping whitespace/newlines as necessary.

" Only do this when not done yet for this buffer
if exists("b:did_ftplugin")
  finish
endif

" Don't load another plugin for this buffer
let b:did_ftplugin = 1

" Temporarily set cpoptions to ensure the script loads OK
let s:cpoptions = &cpoptions
set cpo-=C

" Ada comments
setlocal comments+=O:--

" Make local tag mappings for this buffer (if not already set)
if mapcheck('<C-]>','n') == ''
  nnoremap <unique> <buffer> <C-]>    :call JumpToTag_ada('')<cr>
endif
if mapcheck('g<C-]>','n') == ''
  nnoremap <unique> <buffer> g<C-]>   :call JumpToTag_ada('','stj')<cr>
endif

if mapcheck('<C-N>','i') == ''
  inoremap <unique> <buffer> <C-N> <C-R>=<SID>AdaCompletion("\<lt>C-N>")<cr>
endif
if mapcheck('<C-P>','i') == ''
  inoremap <unique> <buffer> <C-P> <C-R>=<SID>AdaCompletion("\<lt>C-P>")<cr>
endif
if mapcheck('<C-X><C-]>','i') == ''
  inoremap <unique> <buffer> <C-X><C-]> <C-R>=<SID>AdaCompletion("\<lt>C-X>\<lt>C-]>")<cr>
endif
if mapcheck('<bs>','i') == ''
  inoremap <silent> <unique> <buffer> <bs> <C-R>=<SID>AdaInsertBackspace()<cr>
endif


" Only do this when not done yet for this buffer & matchit is used
if ! exists("b:match_words")  &&  exists("loaded_matchit")
  " The following lines enable the macros/matchit.vim plugin for
  " Ada-specific extended matching with the % key.
  let s:notend = '\%(\<end\s\+\)\@<!'
  let b:match_words=
  \ s:notend . '\<if\>:\<elsif\>:\<else\>:\<end\>\s\+\<if\>,' .
  \ s:notend . '\<case\>:\<when\>:\<end\>\s\+\<case\>,' .
  \ '\%(\<while\>.*\|\<for\>.*\|'.s:notend.'\)\<loop\>:\<end\>\s\+\<loop\>,' .
  \ '\%(\<do\>\|\<begin\>\):\<exception\>:\<end\>\s*\%($\|[;A-Z]\),' .
  \ s:notend . '\<record\>:\<end\>\s\+\<record\>'
endif


" Prevent re-load of functions
if exists('s:id')
  finish
endif

" Get this script's unique id
map <script> <SID>?? <SID>??
let s:id = substitute( maparg('<SID>??'), '^<SNR>\(.*\)_??$', '\1', '' )
unmap <script> <SID>??


" Extract current Ada word across multiple lines
" AdaWord( [line, column] )\
let s:AdaWordRegex = '\a\w*\(\_s*\.\_s*\a\w*\)*'
let s:AdaComment   = "\\v^(\"[^\"]*\"|'.'|[^\"']){-}\\zs\\s*--.*"

function! AdaWord(...)
  if a:0 > 1
    let linenr = a:1
    let colnr  = a:2 - 1
  else
    let linenr = line('.')
    let colnr  = col('.') - 1
  endif
  let line = substitute( getline(linenr), s:AdaComment, '', '' )
  " Cope with tag searching for items in comments; if we are, don't loop
  " backards looking for previous lines
  if colnr > strlen(line)
    " We were in a comment
    let line = getline(linenr)
    let search_prev_lines = 0
  else
    let search_prev_lines = 1
  endif

  " Go backwards until we find a match (Ada ID) that *doesn't* include our
  " location - i.e., the previous ID. This is because the current 'correct'
  " match will toggle matching/not matching as we traverse characters
  " backwards. Thus, we have to find the previous unrelated match, exclude
  " it, then use the next full match (ours).
  " Remember to convert vim column 'colnr' [1..n] to string offset [0..(n-1)]
  " ... but start, here, one after the required char.
  let newcol = colnr + 1
  while 1
    let newcol = newcol - 1
    if newcol < 0
      " Have to include previous line from file
      let linenr = linenr - 1
      if linenr < 1  ||  !search_prev_lines
	" Start of file or matching in a comment
	let linenr = 1
	let newcol = 0
	let ourmatch = match( line, s:AdaWordRegex )
	break
      endif
      " Get previous line, and prepend it to our search string
      let newline = substitute( getline(linenr), s:AdaComment, '', '' )
      let newcol  = strlen(newline) - 1
      let colnr   = colnr + newcol
      let line    = newline . line
    endif
    " Check to see if this is a match excluding 'us'
    let mend = newcol + matchend( strpart(line,newcol), s:AdaWordRegex ) - 1
    if mend >= newcol  &&  mend < colnr
      " Yes
      let ourmatch = mend+1 + match( strpart(line,mend+1), s:AdaWordRegex )
      break
    endif
  endwhile

  " Got anything?
  if ourmatch < 0
    return ''
  else
    let line = strpart( line, ourmatch)
  endif

  " Now simply add further lines until the match gets no bigger
  let matchstr = matchstr( line, s:AdaWordRegex )
  let lastline  = line('$')
  let linenr    = line('.') + 1
  while linenr <= lastline
    let lastmatch = matchstr
    let line = line . substitute( getline(linenr), s:AdaComment, '', '' )
    let matchstr = matchstr( line, s:AdaWordRegex )
    if matchstr == lastmatch
      break
    endif
  endwhile

  " Strip whitespace & return
  return substitute( matchstr, '\s\+', '', 'g' )
endfunction


" Word tag - include '.' and if Ada make uppercase
" Name allows a common JumpToTag() to look for an ft specific JumpToTag_ft().
function! JumpToTag_ada(word,...)
  if a:word == ''
    " Get current word
    let word = AdaWord()
    if word == ''
      return
    endif
  else
    let word = a:word
  endif
  if a:0 > 0
    let mode = a:1
  else
    let mode = 'tj'
  endif

  let v:errmsg = ''
  execute 'silent!' mode word
  if v:errmsg != ''
    if v:errmsg =~ '^E426:'  " Tag not found
      let ignorecase = &ignorecase
      set ignorecase
      execute mode word
      let &ignorecase = ignorecase
    else
      " Repeat to give error
      execute mode word
    endif
  endif
endfunction


" Word completion (^N/^R/^X^]) - force '.' inclusion
function! s:AdaCompletion(cmd)
  set iskeyword+=46
  return a:cmd . "\<C-R>=<SNR>" . s:id . "_AdaCompletionEnd()\<CR>"
endfunction
function! s:AdaCompletionEnd()
  set iskeyword-=46
  return ''
endfunction


" Backspace at end of line after auto-inserted commentstring '-- ' wipes it
function! s:AdaInsertBackspace()
  let line = getline('.')
  if col('.') > strlen(line) && match(line,'-- $') != -1 && match(&comments,'--') != -1
    return "\<bs>\<bs>\<bs>"
  else
    return "\<bs>"
  endif
endfunction


" Reset cpoptions
let &cpoptions = s:cpoptions
unlet s:cpoptions

" vim: sts=2 sw=2 :