]> git.madduck.net Git - etc/vim.git/blob - .vim/macros/table.vim

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

require a symbol after numbers to make them lists
[etc/vim.git] / .vim / macros / table.vim
1 " Script: table.vim
2 " Version: 0.1 
3 "
4 " Maintainer: Usman Latif Email: latif@techuser.net 
5 " Webpage: http://www.techuser.net
6 "
7 " Description:
8 " This script defines maps for easier editing and alignmnet of tables.
9 " For usage and installation instructions consult the documentation
10 " files that came with this script. In case you are missing the 
11 " documentation files, download a complete distribution of the files
12 " from http://www.techuser.net/files
13
14
15 map <silent> <Leader>tt :call TableToggle()<CR>
16 map <silent> <Leader>th :call TableHeading()<CR>
17 map <silent> <Leader>ta :call TableAlign()<CR>
18
19 let s:tablemode = 0
20 let s:heading = ''
21 let s:fieldsep = ' \{2,}'
22
23 " Function: TableHeading
24 " Args: None
25 "
26 " use current line as the heading line of the table
27 " current line should be non-empty
28  
29 func! TableHeading() 
30     " get heading line and store it in a script variable
31     let s:heading = TrimWS(ExpandTabs(getline(".")))
32
33     if !ValidHeading(s:heading)
34         return 
35     endif
36
37     " map keys to invoke table navigation functions
38     call EnableMaps() 
39
40     let s:tablemode = 1
41 endfunc
42
43 " Function: ValidHeading
44 " Args: None
45 " Return: boolean
46 "
47 " returns 1 if heading is valid, i.e., non-whitespace
48 " returns 0 otherwise 
49
50 func! ValidHeading(heading)
51     " heading line empty ==> invalid heading
52     let l:str = a:heading
53     if strlen(str) == matchend(str,'^ *')
54         return 0
55     endif
56     return 1
57 endfunc
58
59 " Function: TableToggle
60 " Args: None
61 "
62 " Toggle Table Mode
63 " Enable/Disable maps for tablemode keys
64
65 func! TableToggle()
66
67     if !ValidHeading(s:heading)
68         return 
69     endif
70
71     " enable/disable maps
72     if s:tablemode
73         call DisableMaps()
74     else
75         call EnableMaps()
76     endif
77
78     " toggle tablemode
79     let s:tablemode = !s:tablemode
80 endfunc
81
82 " Function: Enable Maps
83 " Args: None
84 "
85 " Enable maps for tablemode keys
86
87 func! EnableMaps()
88     nnoremap <silent> <Tab>    :call NextField(0)<CR>
89     inoremap <silent> <Tab>    <C-O>:call NextField(1)<CR>
90     nnoremap <silent> <S-Tab>  :call PrevField()<CR>
91     inoremap <silent> <S-Tab>  <C-O>:call PrevField()<CR>
92 endfunc
93
94 " Function: Disable Maps
95 " Args: None
96 "
97 " Disable maps for tablemode keys
98
99 func! DisableMaps()
100     nunmap <Tab>
101     iunmap <Tab>
102     nunmap <S-Tab>
103     iunmap <S-Tab>
104 endfunc
105
106
107 " Function: TableAlign
108 " Args: None
109 " Description: align the fields of the row with the fields of the heading
110
111 func! TableAlign()
112     if !s:tablemode
113         return
114     endif
115     let temp = ""
116     let linetext = TrimWS(ExpandTabs(getline('.')))
117
118     let nfhead = LenWS(s:heading,0) + 1
119     let nftext = LenWS(linetext,0) + 1
120     let error = 0
121
122     while 1
123         " flag error if current field too big to fit
124         if (nfhead - strlen(temp))  <= 1 && strlen(temp) != 0 
125             let error = 1 
126             break
127         endif
128
129         " pad to next field of heading and add contents of the next text
130         " field after that
131         let temp = temp . Replicate(' ',nfhead - strlen(temp)-1) . Gettext(linetext,nftext-1)
132
133         let nfhead = NextFieldPos(s:heading,s:fieldsep,nfhead)
134         let nftext = NextFieldPos(linetext,s:fieldsep,nftext)
135
136         " If no next field exit loop
137         if nfhead == 0 || nftext == 0
138             " flag error if row to be aligned has more fields than heading
139             if nftext != 0 
140                 let error = 1
141             endif
142             break
143         endif
144     endwhile
145     if !error && temp != linetext
146         call setline('.',temp)
147     endif
148 endfunc
149
150
151 " Function: PrevField
152 " Args: None
153 "
154 " position the cursor at the start of the prev field position 
155
156 func! PrevField()
157     let nextpos = 1
158     let lastpos = 1
159     let pos = col('.')
160     let linenum = line('.')
161     let fstfield = LenWS(s:heading,0) + 1
162
163     while nextpos != 0
164         let lastpos = nextpos
165         let nextpos = NextFieldPos(s:heading,s:fieldsep,nextpos) 
166         if pos > lastpos && (pos <= nextpos || nextpos == 0)
167             let pos = lastpos
168         endif
169     endwhile
170
171     if pos <= fstfield && linenum != 1 && col('.') <= fstfield
172         let linenum = linenum - 1
173         let pos = lastpos
174     endif
175
176     call cursor(linenum,pos)
177 endfunc
178
179
180 " Function: NextField
181 " Args: curmode
182 "
183 " position the cursor at the start of next field position
184 " pad the current line with spaces if needed when in insertion
185 " or replace mode
186
187 func! NextField(curmode)
188     let l:pos = Max(col('.') - 2,0)
189     let l:startnext = NextFieldPos(s:heading,s:fieldsep,pos)
190     let l:linenum = line('.')
191
192     "If no nextfield on line goto next line
193     "append an empty line if in insert/replace mode
194     if startnext == 0
195         if a:curmode 
196             call append(linenum,'')
197         endif
198         let linenum = linenum+1
199         let startnext = LenWS(s:heading,0) + 1
200     endif
201
202     let l:linetext = ExpandTabs(getline(linenum))
203     let l:linelen = strlen(linetext)
204
205     "If padding required
206     if linelen < startnext
207         let linetext = linetext . Replicate(' ',startnext-linelen+1)
208         call setline(linenum,linetext)
209     endif
210
211     if linenum > line('$')
212         let linenum = line('$')
213         let startnext = col('.')
214     endif
215     call cursor(linenum,startnext)
216 endfunc
217
218
219 " Function: NextFieldPos
220 " Args: string,pattern,startposition
221 "
222 " returns the position of the end of field in which pos
223 " is contained
224
225 func! NextFieldPos(str,pat,pos)
226     return matchend(a:str,a:pat,a:pos) + 1
227 endfunc
228
229
230 " Function: Gettext
231 " Args: str, pos 
232 " Description: Extract the text contents of a field from the 
233 " string str, starting at position pos
234
235 func! Gettext(str,pos)
236     let endpos = match(a:str,s:fieldsep,a:pos)
237     if endpos == -1
238         let endpos = strlen(a:str) - 1
239     endif
240     return strpart(a:str,a:pos,endpos - a:pos + 1)
241 endfunc
242
243
244 " Function: TrimWS
245 " Args: str
246 " Description: Trim any WS at the end of the string str
247
248 func! TrimWS(str)
249     let len = match(a:str,' \{1,}$',0)
250     if len == -1 
251         return a:str 
252     else
253         return strpart(a:str,0,len)
254     endif
255 endfunc
256
257
258 " Function: LenWS
259 " Args: str, startpos
260 " Description: Length of contiguous whitespace starting at
261 " position startpos in string str
262
263 func! LenWS(str,startpos)
264     let i = 0
265     while a:str[a:startpos+i] == ' '
266         let i = i + 1
267     endwhile
268     return i
269 endfunc
270
271
272 " Function: Replicate
273 " Args: str,cnt
274 "
275 " Repeat the given string cnt number of times
276
277 func! Replicate(str,cnt)
278     let l:temp = ""
279
280     let l:i = 0
281     while i < a:cnt
282         let temp = temp . a:str
283         let i = i + 1
284     endwhile
285
286     return temp
287 endfunc
288
289
290 " Function: ExpandTabs
291 " Args: str
292 " Return value: string 
293 "
294 " Expand all tabs in the string to spaces 
295 " according to tabstop value
296
297 func! ExpandTabs(str)
298     let l:str = a:str
299     let l:temp = ""
300
301     let l:i = 0
302     while i < strlen(str)
303         if str[i] == "\t"
304             let temp = temp . Replicate(' ',&tabstop)
305         else
306             let temp = temp . str[i]
307         endif
308         let i = i + 1
309     endwhile
310
311     return temp
312 endfunc
313
314 " Function: Max
315 " Args: x,y
316 " Description: return the max of x and y
317
318 func! Max(x,y)
319     if a:x >= a:y 
320         return a:x
321     else 
322         return a:y
323     endif
324 endfunc