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.
4 " Maintainer: Usman Latif Email: latif@techuser.net
5 " Webpage: http://www.techuser.net
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
15 map <silent> <Leader>tt :call TableToggle()<CR>
16 map <silent> <Leader>th :call TableHeading()<CR>
17 map <silent> <Leader>ta :call TableAlign()<CR>
21 let s:fieldsep = ' \{2,}'
23 " Function: TableHeading
26 " use current line as the heading line of the table
27 " current line should be non-empty
30 " get heading line and store it in a script variable
31 let s:heading = TrimWS(ExpandTabs(getline(".")))
33 if !ValidHeading(s:heading)
37 " map keys to invoke table navigation functions
43 " Function: ValidHeading
47 " returns 1 if heading is valid, i.e., non-whitespace
50 func! ValidHeading(heading)
51 " heading line empty ==> invalid heading
53 if strlen(str) == matchend(str,'^ *')
59 " Function: TableToggle
63 " Enable/Disable maps for tablemode keys
67 if !ValidHeading(s:heading)
79 let s:tablemode = !s:tablemode
82 " Function: Enable Maps
85 " Enable maps for tablemode keys
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>
94 " Function: Disable Maps
97 " Disable maps for tablemode keys
107 " Function: TableAlign
109 " Description: align the fields of the row with the fields of the heading
116 let linetext = TrimWS(ExpandTabs(getline('.')))
118 let nfhead = LenWS(s:heading,0) + 1
119 let nftext = LenWS(linetext,0) + 1
123 " flag error if current field too big to fit
124 if (nfhead - strlen(temp)) <= 1 && strlen(temp) != 0
129 " pad to next field of heading and add contents of the next text
131 let temp = temp . Replicate(' ',nfhead - strlen(temp)-1) . Gettext(linetext,nftext-1)
133 let nfhead = NextFieldPos(s:heading,s:fieldsep,nfhead)
134 let nftext = NextFieldPos(linetext,s:fieldsep,nftext)
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
145 if !error && temp != linetext
146 call setline('.',temp)
151 " Function: PrevField
154 " position the cursor at the start of the prev field position
160 let linenum = line('.')
161 let fstfield = LenWS(s:heading,0) + 1
164 let lastpos = nextpos
165 let nextpos = NextFieldPos(s:heading,s:fieldsep,nextpos)
166 if pos > lastpos && (pos <= nextpos || nextpos == 0)
171 if pos <= fstfield && linenum != 1 && col('.') <= fstfield
172 let linenum = linenum - 1
176 call cursor(linenum,pos)
180 " Function: NextField
183 " position the cursor at the start of next field position
184 " pad the current line with spaces if needed when in insertion
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('.')
192 "If no nextfield on line goto next line
193 "append an empty line if in insert/replace mode
196 call append(linenum,'')
198 let linenum = linenum+1
199 let startnext = LenWS(s:heading,0) + 1
202 let l:linetext = ExpandTabs(getline(linenum))
203 let l:linelen = strlen(linetext)
206 if linelen < startnext
207 let linetext = linetext . Replicate(' ',startnext-linelen+1)
208 call setline(linenum,linetext)
211 if linenum > line('$')
212 let linenum = line('$')
213 let startnext = col('.')
215 call cursor(linenum,startnext)
219 " Function: NextFieldPos
220 " Args: string,pattern,startposition
222 " returns the position of the end of field in which pos
225 func! NextFieldPos(str,pat,pos)
226 return matchend(a:str,a:pat,a:pos) + 1
232 " Description: Extract the text contents of a field from the
233 " string str, starting at position pos
235 func! Gettext(str,pos)
236 let endpos = match(a:str,s:fieldsep,a:pos)
238 let endpos = strlen(a:str) - 1
240 return strpart(a:str,a:pos,endpos - a:pos + 1)
246 " Description: Trim any WS at the end of the string str
249 let len = match(a:str,' \{1,}$',0)
253 return strpart(a:str,0,len)
259 " Args: str, startpos
260 " Description: Length of contiguous whitespace starting at
261 " position startpos in string str
263 func! LenWS(str,startpos)
265 while a:str[a:startpos+i] == ' '
272 " Function: Replicate
275 " Repeat the given string cnt number of times
277 func! Replicate(str,cnt)
282 let temp = temp . a:str
290 " Function: ExpandTabs
292 " Return value: string
294 " Expand all tabs in the string to spaces
295 " according to tabstop value
297 func! ExpandTabs(str)
302 while i < strlen(str)
304 let temp = temp . Replicate(' ',&tabstop)
306 let temp = temp . str[i]
316 " Description: return the max of x and y