Fixed generic() parser
[vip:vip.git] / plugin / vip.vim
1 " VIP : VHDL Interface Plugin
2 " File:        vip.vim
3 " Version:     0.1.7
4 " Last Change: nov. 19 2010
5 " Author:      Jean-Paul Ricaud
6 " License:     LGPLv3
7 " Description: Copy entity (or component) and paste as component (or entity)
8 "              or instance of component
9
10 if exists("g:loaded_VIP")
11   finish
12 endif
13 let g:loaded_VIP = 1
14
15 " Global variables for user
16 if !exists("g:instSuffix_VIP")
17   let g:instSuffix_VIP = "_"            " the suffix added at the end of an instance name
18 endif
19 if !exists("g:sigPrefix_VIP")
20   let g:sigPrefix_VIP = "s_"            " the prefix added to signals names
21 endif
22 if !exists("g:entityWord_VIP")
23   let g:entityWord_VIP = "entity"       " the 'entity' word when pasted as entity
24 endif
25 if !exists("g:componentWord_VIP")
26   let g:componentWord_VIP = "component" " the 'component' word when pasted as component
27 endif
28 if !exists("g:autoInc_VIP")
29   let g:autoInc_VIP = 1                 " allows auto-incrementation of instance's name
30 endif
31
32 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
33 " Simple paste
34 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
35 function s:SPaste(yankBlock)
36   call append(line("."), a:yankBlock)
37   return 1
38 endfunction
39
40 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
41 " Paste a component / entity as an entity / component
42 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
43 function s:PasteEC(blockType, blockSubstitute, yankBlock)
44   let copyType  = a:blockType " to avoid alteration of the copied block
45   let copyBlock = copy(a:yankBlock) " to avoid alteration of the original block
46   let newBlock = map(copyBlock, 'substitute(v:val, copyType, a:blockSubstitute, "g")')
47   call append(line("."), newBlock)
48   return 1
49 endfunction
50
51 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
52 " Paste an instance of component as an instance of component
53 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
54 function s:PasteII(autoInc, instanceNumb, instSuffix, yankBlock)
55   let copyBlock = copy(a:yankBlock) " to avoid alteration of the original block
56   let currentList = split(copyBlock[0])
57   let pos = 0
58   let posN = 0
59
60   " Let's check if the instance has already a suffix
61   " Get the position of the last suffix in the name if it exisits
62   while posN != -1
63     let posNumb = posN
64     let pos += 1
65     let posN = match(currentList[0], a:instSuffix, pos)
66   endwhile
67
68   if posNumb > 0
69     " Instance has already a suffix
70     if a:autoInc == 1
71       let posNumb += strlen(a:instSuffix)
72       let instNumb = str2nr(strpart(currentList[0], posNumb)) + 1
73       let instNumb += a:instanceNumb
74       let newName = strpart(currentList[0], 0, posNumb)
75       let currentList[0] = newName.instNumb
76     endif
77   else
78     " Instance hasn't a suffix, adding a suffix
79     let currentList[0] = currentList[0].a:instSuffix
80     if a:autoInc == 1
81       let currentList[0] = currentList[0].a:instanceNumb
82     endif
83   endif
84
85   let copyBlock[0] = join(currentList)
86   " Let's add the original indentation of the instance
87   let indentPos = match(a:yankBlock[0], "[a-zA-Z]") " first char of an identifiers must be a letter
88   let indentVal = strpart(a:yankBlock[0], 0, indentPos)
89   let copyBlock[0] = indentVal.copyBlock[0]
90   let result = s:SPaste(copyBlock)
91   return 1
92 endfunction
93
94 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
95 " Paste a component / entity as an instance of component
96 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
97 function s:PasteECI(instanceNumb, instSuffix, sigPrefix, yankBlock)
98   let instanceBlock = []
99   let braceCnt = 0
100   let inPort = 0
101   let inGeneric = 0
102   let i = 0
103   let nbOfLines = len(a:yankBlock) - 3
104
105   " Head and tail of the instance
106   let instanceName = split(a:yankBlock[0])
107   let indentPos = match(a:yankBlock[0], "[a-zA-Z]") " first char of an identifiers must be a letter
108   let indentVal = strpart(a:yankBlock[0], 0, indentPos)
109   let instanceBlock += [indentVal.instanceName[1].a:instSuffix.a:instanceNumb." : ".instanceName[1]]
110
111   " Get signals inside entity / component
112   for i in range(0, nbOfLines)
113     let currentList = split(a:yankBlock[i])
114     let currentLine = a:yankBlock[i]
115     let signalBefore = substitute(currentLine, "\:.*$", "", "g") " remove everything after :
116     let signalName = substitute(signalBefore, "\[ \t]", "", "g") " remove space & tab at begenning of line
117     let currentLine = substitute(currentLine, "\;", "", "g") " remove the ;
118
119     if match(signalName, "--") != -1
120       let vhdlComment = 1
121       let instanceBlock += [currentLine] " add comment
122     else
123       let vhdlComment = 0
124     endif
125
126     let j = 0
127     for currentWord in currentList
128
129       if (currentWord ==? "generic") || (currentWord ==? "generic(")
130         let inGeneric = 1 " inside generic body
131         let j = 1 " skip this line
132         if (signalName ==? "generic (") || (signalName ==? "generic(")
133           let instanceBlock += [indentVal."generic map ("]
134         else
135           let instanceBlock[i] = instanceBlock[i]." generic map ("
136         endif
137       endif
138
139       if (currentWord ==? "port") || (currentWord ==? "port(")
140         let inPort = 1 " inside port body
141         let j = 1 " skip this line
142         if (signalName ==? "port (") || (signalName ==? "port(")
143           let instanceBlock += [indentVal."port map ("]
144         else
145           let instanceBlock[i] = instanceBlock[i]." port map ("
146         endif
147       endif
148
149       if (match(currentWord, "(") != -1)
150         let braceCnt += 1
151       endif
152
153       if (braceCnt > 0) && (j == 0) && (vhdlComment == 0)
154         if inGeneric == 1
155           let instanceBlock += [signalBefore." => ,"]
156         endif
157         if inPort == 1
158           let instanceBlock += [signalBefore." => ".a:sigPrefix.signalName.","]
159         endif
160       endif
161
162       if (match(currentWord, ")")) != -1
163         let braceCnt -= 1
164         if match(currentWord, "))") != -1 " in case of a (m downto n));
165           let braceCnt -= 1 " the first ) has been counted above, the second is counted here
166         endif
167         if braceCnt == 0
168           if signalName == ");" " have we a closing brace at a new line ?
169             let instanceBlock[i-1] = substitute(instanceBlock[i-1], "\,", "", "g") " remove the , of last signal
170             let instanceBlock[i] = currentLine
171           else
172             let instanceBlock[i] = substitute(instanceBlock[i], "\,", "", "g") " remove the , of last signal
173             let instanceBlock[i] = instanceBlock[i]." )"
174           endif
175
176           if inGeneric == 1
177             let inGeneric = 0
178           endif
179           if inPort == 1
180             let inPort = 0
181             let instanceBlock[i] = instanceBlock[i].";"
182           endif
183
184         endif
185       endif
186
187       let j += 1
188    endfor
189
190   endfor
191
192   let instanceBlock += [""] " Add a blank line after the instance
193   call append(line("."), instanceBlock)
194   return 1
195 endfunction
196
197 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
198 " Copy the block
199 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
200 function s:CopyLines(blockType)
201   let braceCnt = 0
202   let openBlock = 0
203   let closeBrace = 0
204   let i = 0
205   let currentLine = []
206   let fLine = line(".")
207
208   while ((braceCnt != 0) || (closeBrace == 0))
209     let currentLine += [getline(fLine + i)]
210     let currentList = split(currentLine[i])
211     if currentList == []
212       echohl WarningMsg | echo  "error : end of block not detected, missing \")\" or \");\" ?" | echohl None
213       return []
214     endif
215     for currentWord in currentList
216       if (currentWord==? "end")
217         echohl WarningMsg | echo  "error : \"end\" detected" | echohl None
218         return []
219       endif
220       if (currentWord ==? "port") || (currentWord ==? "port(")
221         let openBlock = 1 "Opening of the block detected
222       endif
223       if ((match(currentWord, "(") != -1) && (openBlock == 1))
224         let braceCnt += 1
225       endif
226       if ((match(currentWord, ")") != -1) && (openBlock == 1))
227         let braceCnt -= 1
228         let closeBrace = 1
229         if match(currentWord, "))") != -1 " in case of a (m downto n));
230           let braceCnt -= 1 " the first ) has been counted above, the second is counted here
231         endif
232       endif
233     endfor
234     let i += 1
235   endwhile
236
237   if ((a:blockType == "entity") || (a:blockType == "component"))
238     let currentLine += [getline(fLine + i)] " Get the end entity / end component line
239   endif
240   let currentLine += [""] " let add a blank element to get an empty line after the block
241   return currentLine
242 endfunction
243
244 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
245 " Get the line under the cursor, and check if it is an entity,
246 " a component, an instance
247 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
248 function s:CheckType()
249   let firstLine = split(getline("."))
250
251   if (firstLine == [])
252     " empty line
253     echohl WarningMsg | echo "error : please palce the cursor on entity, component or instance line" | echohl None
254     return ""
255   endif
256   if ((firstLine[0] ==? "port") || (firstLine[0] ==? "generic") || (firstLine[0] ==? ")") || (firstLine[0] ==? ");"))
257     " Bad cursor position, cursor should be on "entity", "component"
258     " or on the instance name line
259     echohl WarningMsg | echo "error : please palce the cursor on entity, component or instance line" | echohl None
260     return ""
261   endif
262   for firstLineWord in firstLine
263     if (firstLineWord ==? "entity") || (firstLineWord ==? "component") || (firstLineWord ==? "port" || (firstLineWord ==? "generic"))
264       return firstLineWord
265     endif
266   endfor
267
268   " Search for an instance under the current line
269   let firstLine = split(getline(line(".") + 1))
270   if ((firstLine[0] ==? "port") || (firstLine[0] ==? "generic"))
271     return firstLine[0]
272   endif
273   echohl WarningMsg | echo "error : please palce the cursor on entity, component or instance line" | echohl None
274   return ""
275 endfunction
276
277 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
278 " Yank an entity, a component or an instance
279 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
280 function s:YankB()
281   let yankBlock = []
282   let blockType = s:CheckType() " check if we copy an entity, or a component, etc.
283   if (blockType != "")
284     let yankBlock = s:CopyLines(blockType)
285   endif
286   return [blockType,yankBlock]
287 endfunction
288
289 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
290 " Action : what to do ? copy or paste / convert ?
291 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
292 function s:Action(actionToDo)
293   " Copy
294   if (a:actionToDo == "yank")
295     let [s:VHDLType,s:VHDLBlock] = s:YankB()
296     let s:instanceNumb = 0
297   endif
298   " Paste
299   if s:VHDLBlock != []
300     " Simple paste
301     if (a:actionToDo == "paste")
302       let result = s:SPaste(s:VHDLBlock)
303     endif
304     " Entity paste
305     if (s:VHDLType == "entity")
306       if (a:actionToDo == "entity")
307         let result = s:SPaste(s:VHDLBlock)
308       endif
309       if (a:actionToDo == "component")
310         let result = s:PasteEC(s:VHDLType, g:componentWord_VIP, s:VHDLBlock)
311       endif
312       if (a:actionToDo == "instance")
313         let result = s:PasteECI(s:instanceNumb, g:instSuffix_VIP, g:sigPrefix_VIP, s:VHDLBlock)
314         let s:instanceNumb += 1
315       endif
316     endif
317     " Component paste
318     if (s:VHDLType == "component")
319       if (a:actionToDo == "entity")
320         let result = s:PasteEC(s:VHDLType, g:entityWord_VIP, s:VHDLBlock)
321       endif
322       if (a:actionToDo == "component")
323         let result = s:SPaste(s:VHDLBlock)
324       endif
325       if (a:actionToDo == "instance")
326         let result = s:PasteECI(s:instanceNumb, g:instSuffix_VIP, g:sigPrefix_VIP, s:VHDLBlock)
327         let s:instanceNumb += 1
328       endif
329     endif
330     " Instance paste
331     if ((s:VHDLType == "port") || (s:VHDLType == "generic"))
332       if (a:actionToDo == "entity")
333       endif
334       if (a:actionToDo == "component")
335       endif
336       if (a:actionToDo == "instance")
337         let result = s:PasteII(g:autoInc_VIP, s:instanceNumb, g:instSuffix_VIP, s:VHDLBlock)
338         let s:instanceNumb += 1
339       endif
340     endif
341   endif
342 endfunction
343
344 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
345 " Main
346 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
347 let s:VHDLBlock = [] " container for the block to be copied
348 let s:VHDLType = ""  " type of the block to copy
349 let s:instanceNumb = 0
350
351 """""""""""""" Yank
352 if !hasmapto('<Plug>SpecialVHDLAction')
353   map <unique> <leader>y <Plug>SpecialVHDLYank
354 endif
355 noremap <unique> <script> <Plug>SpecialVHDLYank <SID>Yank
356 noremap <SID>Yank :call <SID>Action("yank")<CR>
357
358 if !exists(":Viy")
359   command -nargs=0 Viy :call s:Action("yank")
360 endif
361
362 """""""""""""" Paste as same
363 if !hasmapto('<Plug>SpecialVHDLPaste')
364   map <unique> <leader>p <Plug>SpecialVHDLPaste
365 endif
366 noremap <unique> <script> <Plug>SpecialVHDLPaste <SID>Paste
367 noremap <SID>Paste :call <SID>Action("paste")<CR>
368
369 if !exists(":Vip")
370   command -nargs=0 Vip :call s:Action("paste")
371 endif
372
373 """"""""""""""" Paste as entity
374 if !hasmapto('<Plug>SpecialVHDLPasteEntity')
375   map <unique> <leader>e <Plug>SpecialVHDLPasteEntity
376 endif
377 noremap <unique> <script> <Plug>SpecialVHDLPasteEntity <SID>PasteEntity
378 noremap <SID>PasteEntity :call <SID>Action("entity")<CR>
379
380 if !exists(":Vie")
381   command -nargs=0 Vie :call s:Action("entity")
382 endif
383
384 """"""""""""""" Paste as component
385 if !hasmapto('<Plug>SpecialVHDLPasteComponent')
386   map <unique> <leader>c <Plug>SpecialVHDLPasteComponent
387 endif
388 noremap <unique> <script> <Plug>SpecialVHDLPasteComponent <SID>PasteComponent
389 noremap <SID>PasteComponent :call <SID>Action("component")<CR>
390
391 if !exists(":Vic")
392   command -nargs=0 Vic :call s:Action("component")
393 endif
394
395 """"""""""""""" Paste as instance
396 if !hasmapto('<Plug>SpecialVHDLPasteInstance')
397   map <unique> <leader>i <Plug>SpecialVHDLPasteInstance
398 endif
399 noremap <unique> <script> <Plug>SpecialVHDLPasteInstance <SID>PasteInstance
400 noremap <SID>Paste Instance:call <SID>Action("instance")<CR>
401
402 if !exists(":Vii")
403   command -nargs=0 Vii :call s:Action("instance")
404 endif
405
406 "vim:ff=unix