1" Vim syntax support file 2" Maintainer: Bram Moolenaar <[email protected]> 3" Last Change: 2004 Oct 10 4" (modified by David Ne\v{c}as (Yeti) <[email protected]>) 5" (XHTML support by Panagiotis Issaris <[email protected]>) 6 7" Transform a file into HTML, using the current syntax highlighting. 8 9" Number lines when explicitely requested or when `number' is set 10if exists("html_number_lines") 11 let s:numblines = html_number_lines 12else 13 let s:numblines = &number 14endif 15 16" When not in gui we can only guess the colors. 17if has("gui_running") 18 let s:whatterm = "gui" 19else 20 let s:whatterm = "cterm" 21 if &t_Co == 8 22 let s:cterm_color0 = "#808080" 23 let s:cterm_color1 = "#ff6060" 24 let s:cterm_color2 = "#00ff00" 25 let s:cterm_color3 = "#ffff00" 26 let s:cterm_color4 = "#8080ff" 27 let s:cterm_color5 = "#ff40ff" 28 let s:cterm_color6 = "#00ffff" 29 let s:cterm_color7 = "#ffffff" 30 else 31 let s:cterm_color0 = "#000000" 32 let s:cterm_color1 = "#c00000" 33 let s:cterm_color2 = "#008000" 34 let s:cterm_color3 = "#804000" 35 let s:cterm_color4 = "#0000c0" 36 let s:cterm_color5 = "#c000c0" 37 let s:cterm_color6 = "#008080" 38 let s:cterm_color7 = "#c0c0c0" 39 let s:cterm_color8 = "#808080" 40 let s:cterm_color9 = "#ff6060" 41 let s:cterm_color10 = "#00ff00" 42 let s:cterm_color11 = "#ffff00" 43 let s:cterm_color12 = "#8080ff" 44 let s:cterm_color13 = "#ff40ff" 45 let s:cterm_color14 = "#00ffff" 46 let s:cterm_color15 = "#ffffff" 47 endif 48endif 49 50" Return good color specification: in GUI no transformation is done, in 51" terminal return RGB values of known colors and empty string on unknown 52if s:whatterm == "gui" 53 function! s:HtmlColor(color) 54 return a:color 55 endfun 56else 57 function! s:HtmlColor(color) 58 if exists("s:cterm_color" . a:color) 59 execute "return s:cterm_color" . a:color 60 else 61 return "" 62 endif 63 endfun 64endif 65 66if !exists("html_use_css") 67 " Return opening HTML tag for given highlight id 68 function! s:HtmlOpening(id) 69 let a = "" 70 if synIDattr(a:id, "inverse") 71 " For inverse, we always must set both colors (and exchange them) 72 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm)) 73 let a = a . '<span style="background-color: ' . ( x != "" ? x : s:fgc ) . '">' 74 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm)) 75 let a = a . '<font color="' . ( x != "" ? x : s:bgc ) . '">' 76 else 77 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm)) 78 if x != "" | let a = a . '<span style="background-color: ' . x . '">' | endif 79 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm)) 80 if x != "" | let a = a . '<font color="' . x . '">' | endif 81 endif 82 if synIDattr(a:id, "bold") | let a = a . "<b>" | endif 83 if synIDattr(a:id, "italic") | let a = a . "<i>" | endif 84 if synIDattr(a:id, "underline") | let a = a . "<u>" | endif 85 return a 86 endfun 87 88 " Return closing HTML tag for given highlight id 89 function s:HtmlClosing(id) 90 let a = "" 91 if synIDattr(a:id, "underline") | let a = a . "</u>" | endif 92 if synIDattr(a:id, "italic") | let a = a . "</i>" | endif 93 if synIDattr(a:id, "bold") | let a = a . "</b>" | endif 94 if synIDattr(a:id, "inverse") 95 let a = a . '</font></span>' 96 else 97 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm)) 98 if x != "" | let a = a . '</font>' | endif 99 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm)) 100 if x != "" | let a = a . '</span>' | endif 101 endif 102 return a 103 endfun 104endif 105 106" Return CSS style describing given highlight id (can be empty) 107function! s:CSS1(id) 108 let a = "" 109 if synIDattr(a:id, "inverse") 110 " For inverse, we always must set both colors (and exchange them) 111 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm)) 112 let a = a . "color: " . ( x != "" ? x : s:bgc ) . "; " 113 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm)) 114 let a = a . "background-color: " . ( x != "" ? x : s:fgc ) . "; " 115 else 116 let x = s:HtmlColor(synIDattr(a:id, "fg#", s:whatterm)) 117 if x != "" | let a = a . "color: " . x . "; " | endif 118 let x = s:HtmlColor(synIDattr(a:id, "bg#", s:whatterm)) 119 if x != "" | let a = a . "background-color: " . x . "; " | endif 120 endif 121 if synIDattr(a:id, "bold") | let a = a . "font-weight: bold; " | endif 122 if synIDattr(a:id, "italic") | let a = a . "font-style: italic; " | endif 123 if synIDattr(a:id, "underline") | let a = a . "text-decoration: underline; " | endif 124 return a 125endfun 126 127" Figure out proper MIME charset from the 'encoding' option. 128if exists("html_use_encoding") 129 let s:html_encoding = html_use_encoding 130else 131 let s:vim_encoding = &encoding 132 if s:vim_encoding =~ '^8bit\|^2byte' 133 let s:vim_encoding = substitute(s:vim_encoding, '^8bit-\|^2byte-', '', '') 134 endif 135 if s:vim_encoding == 'latin1' 136 let s:html_encoding = 'iso-8859-1' 137 elseif s:vim_encoding =~ "^cp12" 138 let s:html_encoding = substitute(s:vim_encoding, 'cp', 'windows-', '') 139 elseif s:vim_encoding == 'sjis' 140 let s:html_encoding = 'Shift_JIS' 141 elseif s:vim_encoding == 'euc-cn' 142 let s:html_encoding = 'GB_2312-80' 143 elseif s:vim_encoding == 'euc-tw' 144 let s:html_encoding = "" 145 elseif s:vim_encoding =~ '^euc\|^iso\|^koi' 146 let s:html_encoding = substitute(s:vim_encoding, '.*', '\U\0', '') 147 elseif s:vim_encoding == 'cp949' 148 let s:html_encoding = 'KS_C_5601-1987' 149 elseif s:vim_encoding == 'cp936' 150 let s:html_encoding = 'GBK' 151 elseif s:vim_encoding =~ '^ucs\|^utf' 152 let s:html_encoding = 'UTF-8' 153 else 154 let s:html_encoding = "" 155 endif 156endif 157 158 159" Set some options to make it work faster. 160" Expand tabs in original buffer to get 'tabstop' correctly used. 161" Don't report changes for :substitute, there will be many of them. 162let s:old_title = &title 163let s:old_icon = &icon 164let s:old_et = &l:et 165let s:old_report = &report 166let s:old_search = @/ 167set notitle noicon 168setlocal et 169set report=1000000 170 171" Split window to create a buffer with the HTML file. 172let s:orgbufnr = winbufnr(0) 173if expand("%") == "" 174 new Untitled.html 175else 176 new %.html 177endif 178let s:newwin = winnr() 179let s:orgwin = bufwinnr(s:orgbufnr) 180 181set modifiable 182%d 183let s:old_paste = &paste 184set paste 185let s:old_magic = &magic 186set magic 187 188if exists("use_xhtml") 189 exe "normal! a<?xml version=\"1.0\"?>\n\e" 190 let tag_close = '/>' 191else 192 let tag_close = '>' 193endif 194 195" HTML header, with the title and generator ;-). Left free space for the CSS, 196" to be filled at the end. 197exe "normal! a<html>\n<head>\n<title>\e" 198exe "normal! a" . expand("%:p:~") . "</title>\n\e" 199exe "normal! a<meta name=\"Generator\" content=\"Vim/" . v:version/100 . "." . v:version %100 . '"' . tag_close . "\n\e" 200if s:html_encoding != "" 201 exe "normal! a<meta http-equiv=\"content-type\" content=\"text/html; charset=" . s:html_encoding . '"' . tag_close . "\n\e" 202endif 203if exists("html_use_css") 204 exe "normal! a<style type=\"text/css\">\n<!--\n-->\n</style>\n\e" 205endif 206if exists("html_no_pre") 207 exe "normal! a</head>\n<body>\n\e" 208else 209 exe "normal! a</head>\n<body>\n<pre>\n\e" 210endif 211 212exe s:orgwin . "wincmd w" 213 214" List of all id's 215let s:idlist = "," 216 217let s:expandedtab = ' ' 218while strlen(s:expandedtab) < &ts 219 let s:expandedtab = s:expandedtab . ' ' 220endwhile 221 222" Loop over all lines in the original text. 223" Use html_start_line and html_end_line if they are set. 224if exists("html_start_line") 225 let s:lnum = html_start_line 226 if s:lnum < 1 || s:lnum > line("$") 227 let s:lnum = 1 228 endif 229else 230 let s:lnum = 1 231endif 232if exists("html_end_line") 233 let s:end = html_end_line 234 if s:end < s:lnum || s:end > line("$") 235 let s:end = line("$") 236 endif 237else 238 let s:end = line("$") 239endif 240 241" Closed folds are kept in the HTML. Prepare the closed fold template text. 242if has('folding') 243 let s:c = &fillchars[matchend(&fillchars, 'fold:')] 244 if s:c == '' 245 let s:c = '-' 246 endif 247 let s:htmlfoldtext = '+' . s:c 248 while strlen(s:htmlfoldtext) < &columns 249 let s:htmlfoldtext = s:htmlfoldtext . s:c 250 endwhile 251 unlet s:c 252endif 253 254 255while s:lnum <= s:end 256 257 " Get the current line 258 let s:line = getline(s:lnum) 259 let s:new = "" 260 261 if has('folding') && foldclosed(s:lnum) > -1 262 " 263 " This is the beginning of a folded block 264 " 265 if s:numblines 266 let s:new = strpart(' ', 0, strlen(line("$")) - strlen(s:lnum)) . s:lnum . ' ' 267 endif 268 let s:line = foldtextresult(s:lnum) 269 270 let s:new = s:new . s:line 271 if !exists("html_no_pre") 272 let s:new = s:new . strpart(s:htmlfoldtext, strlen(s:new)) 273 endif 274 275 " Replace the reserved html characters 276 let s:new = substitute(substitute(substitute(substitute(substitute(s:new, '&', '\&', 'g'), '<', '\<', 'g'), '>', '\>', 'g'), '"', '\"', 'g'), "\x0c", '<hr class="PAGE-BREAK">', 'g') 277 278 let s:id_name = "Folded" 279 let s:id = hlID(s:id_name) 280 let s:new = '<span class="' . s:id_name . '">' . s:new . '</span>' 281 " Add the class to class list if it's not there yet 282 if stridx(s:idlist, "," . s:id . ",") == -1 283 let s:idlist = s:idlist . s:id . "," 284 endif 285 286 " Skip to the end of the fold 287 let s:lnum = foldclosedend(s:lnum) 288 289 else 290 " 291 " A line that is not folded. 292 " 293 let s:len = strlen(s:line) 294 295 if s:numblines 296 let s:new = '<span class="lnr">' . strpart(' ', 0, strlen(line("$")) - strlen(s:lnum)) . s:lnum . '</span> ' 297 endif 298 299 " Loop over each character in the line 300 let s:col = 1 301 while s:col <= s:len 302 let s:startcol = s:col " The start column for processing text 303 let s:id = synID(s:lnum, s:col, 1) 304 let s:col = s:col + 1 305 " Speed loop (it's small - that's the trick) 306 " Go along till we find a change in synID 307 while s:col <= s:len && s:id == synID(s:lnum, s:col, 1) | let s:col = s:col + 1 | endwhile 308 309 " Output the text with the same synID, with class set to {s:id_name} 310 let s:id = synIDtrans(s:id) 311 let s:id_name = synIDattr(s:id, "name", s:whatterm) 312 let s:new = s:new . '<span class="' . s:id_name . '">' . substitute(substitute(substitute(substitute(substitute(strpart(s:line, s:startcol - 1, s:col - s:startcol), '&', '\&', 'g'), '<', '\<', 'g'), '>', '\>', 'g'), '"', '\"', 'g'), "\x0c", '<hr class="PAGE-BREAK">', 'g') . '</span>' 313 " Add the class to class list if it's not there yet 314 if stridx(s:idlist, "," . s:id . ",") == -1 315 let s:idlist = s:idlist . s:id . "," 316 endif 317 318 if s:col > s:len 319 break 320 endif 321 endwhile 322 endif 323 324 " Expand tabs 325 let s:pad=0 326 let s:start = 0 327 let s:idx = stridx(s:line, "\t") 328 while s:idx >= 0 329 let s:i = &ts - ((s:start + s:pad + s:idx) % &ts) 330 let s:new = substitute(s:new, '\t', strpart(s:expandedtab, 0, s:i), '') 331 let s:pad = s:pad + s:i - 1 332 let s:start = s:start + s:idx + 1 333 let s:idx = stridx(strpart(s:line, s:start), "\t") 334 endwhile 335 336 if exists("html_no_pre") 337 if exists("use_xhtml") 338 let s:new = substitute(s:new, ' ', '\ \ ', 'g') . '<br/>' 339 else 340 let s:new = substitute(s:new, ' ', '\ \ ', 'g') . '<br>' 341 endif 342 endif 343 exe s:newwin . "wincmd w" 344 exe "normal! a" . strtrans(s:new) . "\n\e" 345 exe s:orgwin . "wincmd w" 346 let s:lnum = s:lnum + 1 347 + 348endwhile 349" Finish with the last line 350exe s:newwin . "wincmd w" 351if exists("html_no_pre") 352 exe "normal! a\n</body>\n</html>\e" 353else 354 exe "normal! a</pre>\n</body>\n</html>\e" 355endif 356 357 358" Now, when we finally know which, we define the colors and styles 359if exists("html_use_css") 360 1;/<style type="text/+1 361endif 362 363" Find out the background and foreground color. 364let s:fgc = s:HtmlColor(synIDattr(hlID("Normal"), "fg#", s:whatterm)) 365let s:bgc = s:HtmlColor(synIDattr(hlID("Normal"), "bg#", s:whatterm)) 366if s:fgc == "" 367 let s:fgc = ( &background == "dark" ? "#ffffff" : "#000000" ) 368endif 369if s:bgc == "" 370 let s:bgc = ( &background == "dark" ? "#000000" : "#ffffff" ) 371endif 372 373" Normal/global attributes 374" For Netscape 4, set <body> attributes too, though, strictly speaking, it's 375" incorrect. 376if exists("html_use_css") 377 if exists("html_no_pre") 378 execute "normal! A\nbody { color: " . s:fgc . "; background-color: " . s:bgc . "; font-family: Courier, monospace; }\e" 379 else 380 execute "normal! A\npre { color: " . s:fgc . "; background-color: " . s:bgc . "; }\e" 381 yank 382 put 383 execute "normal! ^cwbody\e" 384 endif 385else 386 if exists("html_no_pre") 387 execute '%s:<body>:<body ' . 'bgcolor="' . s:bgc . '" text="' . s:fgc . '" style="font-family\: Courier, monospace;">' 388 else 389 execute '%s:<body>:<body ' . 'bgcolor="' . s:bgc . '" text="' . s:fgc . '">' 390 endif 391endif 392 393" Line numbering attributes 394if s:numblines 395 if exists("html_use_css") 396 execute "normal! A\n.lnr { " . s:CSS1(hlID("LineNr")) . "}\e" 397 else 398 execute '%s+<span class="lnr">\([^<]*\)</span>+' . s:HtmlOpening(hlID("LineNr")) . '\1' . s:HtmlClosing(hlID("LineNr")) . '+g' 399 endif 400endif 401 402" Gather attributes for all other classes 403let s:idlist = strpart(s:idlist, 1) 404while s:idlist != "" 405 let s:attr = "" 406 let s:col = stridx(s:idlist, ",") 407 let s:id = strpart(s:idlist, 0, s:col) 408 let s:idlist = strpart(s:idlist, s:col + 1) 409 let s:attr = s:CSS1(s:id) 410 let s:id_name = synIDattr(s:id, "name", s:whatterm) 411 " If the class has some attributes, export the style, otherwise DELETE all 412 " its occurences to make the HTML shorter 413 if s:attr != "" 414 if exists("html_use_css") 415 execute "normal! A\n." . s:id_name . " { " . s:attr . "}" 416 else 417 execute '%s+<span class="' . s:id_name . '">\([^<]*\)</span>+' . s:HtmlOpening(s:id) . '\1' . s:HtmlClosing(s:id) . '+g' 418 endif 419 else 420 execute '%s+<span class="' . s:id_name . '">\([^<]*\)</span>+\1+g' 421 if exists("html_use_css") 422 1;/<style type="text/+1 423 endif 424 endif 425endwhile 426 427" Add hyperlinks 428%s+\(http://\S\{-}\)\(\([.,;:}]\=\(\s\|$\)\)\|[\\"'<>]\|>\|<\)+<A HREF="\1">\1</A>\2+ge 429 430" The DTD 431if exists("html_use_css") 432 exe "normal! gg0i<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n\e" 433endif 434 435" Cleanup 436%s:\s\+$::e 437 438" Restore old settings 439let &report = s:old_report 440let &title = s:old_title 441let &icon = s:old_icon 442let &paste = s:old_paste 443let &magic = s:old_magic 444let @/ = s:old_search 445exe s:orgwin . "wincmd w" 446let &l:et = s:old_et 447exe s:newwin . "wincmd w" 448 449" Save a little bit of memory (worth doing?) 450unlet s:old_et s:old_paste s:old_icon s:old_report s:old_title s:old_search 451unlet s:whatterm s:idlist s:lnum s:end s:fgc s:bgc s:old_magic 452unlet! s:col s:id s:attr s:len s:line s:new s:did_retab s:numblines 453unlet s:orgwin s:newwin s:orgbufnr 454delfunc s:HtmlColor 455delfunc s:CSS1 456if !exists("html_use_css") 457 delfunc s:HtmlOpening 458 delfunc s:HtmlClosing 459endif 460