blob: f3ce8bed8d78b5cd981dff993630acc33241fd79 [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001" Vim syntax support file
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02002" Maintainer: Ben Fritz <fritzophrenic@gmail.com>
fritzophrenic86cfb392023-09-08 12:20:01 -05003" Last Change: 2023 Sep 05
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02004"
5" Additional contributors:
6"
Bram Moolenaar7510fe72010-07-25 12:46:44 +02007" Original by Bram Moolenaar <Bram@vim.org>
8" Modified by David Ne\v{c}as (Yeti) <yeti@physics.muni.cz>
9" XHTML support by Panagiotis Issaris <takis@lumumba.luc.ac.be>
10" Made w3 compliant by Edd Barrett <vext01@gmail.com>
11" Added html_font. Edd Barrett <vext01@gmail.com>
12" Progress bar based off code from "progressbar widget" plugin by
13" Andreas Politz, heavily modified:
14" http://www.vim.org/scripts/script.php?script_id=2006
Bram Moolenaar349b2fb2010-07-16 20:35:36 +020015"
Bram Moolenaar7510fe72010-07-25 12:46:44 +020016" See Mercurial change logs for more!
Bram Moolenaar071d4272004-06-13 20:20:40 +000017
18" Transform a file into HTML, using the current syntax highlighting.
19
Bram Moolenaar5c736222010-01-06 20:54:52 +010020" this file uses line continuations
21let s:cpo_sav = &cpo
Bram Moolenaar349b2fb2010-07-16 20:35:36 +020022let s:ls = &ls
Bram Moolenaar0c0734d2019-11-26 21:44:46 +010023let s:ei_sav = &eventignore
Bram Moolenaar6c35bea2012-07-25 17:49:10 +020024set cpo&vim
Bram Moolenaar5c736222010-01-06 20:54:52 +010025
Bram Moolenaar0c0734d2019-11-26 21:44:46 +010026" HTML filetype can take a while to load/highlight if the destination file
27" already exists.
28set eventignore+=FileType
29
Bram Moolenaar349b2fb2010-07-16 20:35:36 +020030let s:end=line('$')
Bram Moolenaar349b2fb2010-07-16 20:35:36 +020031
Bram Moolenaar313b7232007-05-05 17:56:55 +000032" Font
Bram Moolenaarb02cbe32010-07-11 22:38:52 +020033if exists("g:html_font")
Bram Moolenaar60cce2f2015-10-13 23:21:27 +020034 if type(g:html_font) == type([])
fritzophrenic86cfb392023-09-08 12:20:01 -050035 let s:htmlfont = "'".. join(g:html_font,"','") .. "', monospace"
Bram Moolenaar60cce2f2015-10-13 23:21:27 +020036 else
fritzophrenic86cfb392023-09-08 12:20:01 -050037 let s:htmlfont = "'".. g:html_font .. "', monospace"
Bram Moolenaar60cce2f2015-10-13 23:21:27 +020038 endif
Bram Moolenaar313b7232007-05-05 17:56:55 +000039else
40 let s:htmlfont = "monospace"
41endif
42
Bram Moolenaar076e8b22010-08-05 21:54:00 +020043let s:settings = tohtml#GetUserSettings()
Bram Moolenaar5c736222010-01-06 20:54:52 +010044
Bram Moolenaar0c0734d2019-11-26 21:44:46 +010045if s:settings.use_xhtml
46 let s:html5 = 0
47elseif s:settings.use_css && !s:settings.no_pre
48 let s:html5 = 1
49else
50 let s:html5 = 0
51endif
52
Bram Moolenaar6c35bea2012-07-25 17:49:10 +020053if !exists('s:FOLDED_ID')
54 let s:FOLDED_ID = hlID("Folded") | lockvar s:FOLDED_ID
55 let s:FOLD_C_ID = hlID("FoldColumn") | lockvar s:FOLD_C_ID
56 let s:LINENR_ID = hlID('LineNr') | lockvar s:LINENR_ID
57 let s:DIFF_D_ID = hlID("DiffDelete") | lockvar s:DIFF_D_ID
58 let s:DIFF_A_ID = hlID("DiffAdd") | lockvar s:DIFF_A_ID
59 let s:DIFF_C_ID = hlID("DiffChange") | lockvar s:DIFF_C_ID
60 let s:DIFF_T_ID = hlID("DiffText") | lockvar s:DIFF_T_ID
61 let s:CONCEAL_ID = hlID('Conceal') | lockvar s:CONCEAL_ID
62endif
63
Bram Moolenaar8e5af3e2011-04-28 19:02:44 +020064" Whitespace
65if s:settings.pre_wrap
66 let s:whitespace = "white-space: pre-wrap; "
67else
68 let s:whitespace = ""
69endif
70
Bram Moolenaar6c35bea2012-07-25 17:49:10 +020071if !empty(s:settings.prevent_copy)
72 if s:settings.no_invalid
73 " User has decided they don't want invalid markup. Still works in
74 " OpenOffice, and for text editors, but when pasting into Microsoft Word the
75 " input elements get pasted too and they cannot be deleted (at least not
76 " easily).
77 let s:unselInputType = ""
78 else
79 " Prevent from copy-pasting the input elements into Microsoft Word where
80 " they cannot be deleted easily by deliberately inserting invalid markup.
81 let s:unselInputType = " type='invalid_input_type'"
82 endif
83endif
84
Bram Moolenaar0c0734d2019-11-26 21:44:46 +010085" When gui colors are not supported, we can only guess the colors.
86" TODO - is this true anymore? Is there a way to ask the terminal what colors
87" each number means or read them from some file?
88if &termguicolors || has("gui_running")
Bram Moolenaar071d4272004-06-13 20:20:40 +000089 let s:whatterm = "gui"
90else
91 let s:whatterm = "cterm"
92 if &t_Co == 8
Bram Moolenaar6c35bea2012-07-25 17:49:10 +020093 let s:cterm_color = {
94 \ 0: "#808080", 1: "#ff6060", 2: "#00ff00", 3: "#ffff00",
95 \ 4: "#8080ff", 5: "#ff40ff", 6: "#00ffff", 7: "#ffffff"
96 \ }
Bram Moolenaar071d4272004-06-13 20:20:40 +000097 else
Bram Moolenaar6c35bea2012-07-25 17:49:10 +020098 let s:cterm_color = {
99 \ 0: "#000000", 1: "#c00000", 2: "#008000", 3: "#804000",
100 \ 4: "#0000c0", 5: "#c000c0", 6: "#008080", 7: "#c0c0c0",
101 \ 8: "#808080", 9: "#ff6060", 10: "#00ff00", 11: "#ffff00",
102 \ 12: "#8080ff", 13: "#ff40ff", 14: "#00ffff", 15: "#ffffff"
103 \ }
Bram Moolenaar313b7232007-05-05 17:56:55 +0000104
105 " Colors for 88 and 256 come from xterm.
106 if &t_Co == 88
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200107 call extend(s:cterm_color, {
108 \ 16: "#000000", 17: "#00008b", 18: "#0000cd", 19: "#0000ff",
109 \ 20: "#008b00", 21: "#008b8b", 22: "#008bcd", 23: "#008bff",
110 \ 24: "#00cd00", 25: "#00cd8b", 26: "#00cdcd", 27: "#00cdff",
111 \ 28: "#00ff00", 29: "#00ff8b", 30: "#00ffcd", 31: "#00ffff",
112 \ 32: "#8b0000", 33: "#8b008b", 34: "#8b00cd", 35: "#8b00ff",
113 \ 36: "#8b8b00", 37: "#8b8b8b", 38: "#8b8bcd", 39: "#8b8bff",
114 \ 40: "#8bcd00", 41: "#8bcd8b", 42: "#8bcdcd", 43: "#8bcdff",
115 \ 44: "#8bff00", 45: "#8bff8b", 46: "#8bffcd", 47: "#8bffff",
116 \ 48: "#cd0000", 49: "#cd008b", 50: "#cd00cd", 51: "#cd00ff",
117 \ 52: "#cd8b00", 53: "#cd8b8b", 54: "#cd8bcd", 55: "#cd8bff",
118 \ 56: "#cdcd00", 57: "#cdcd8b", 58: "#cdcdcd", 59: "#cdcdff",
119 \ 60: "#cdff00", 61: "#cdff8b", 62: "#cdffcd", 63: "#cdffff",
120 \ 64: "#ff0000"
121 \ })
122 call extend(s:cterm_color, {
123 \ 65: "#ff008b", 66: "#ff00cd", 67: "#ff00ff", 68: "#ff8b00",
124 \ 69: "#ff8b8b", 70: "#ff8bcd", 71: "#ff8bff", 72: "#ffcd00",
125 \ 73: "#ffcd8b", 74: "#ffcdcd", 75: "#ffcdff", 76: "#ffff00",
126 \ 77: "#ffff8b", 78: "#ffffcd", 79: "#ffffff", 80: "#2e2e2e",
127 \ 81: "#5c5c5c", 82: "#737373", 83: "#8b8b8b", 84: "#a2a2a2",
128 \ 85: "#b9b9b9", 86: "#d0d0d0", 87: "#e7e7e7"
129 \ })
Bram Moolenaar313b7232007-05-05 17:56:55 +0000130 elseif &t_Co == 256
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200131 call extend(s:cterm_color, {
132 \ 16: "#000000", 17: "#00005f", 18: "#000087", 19: "#0000af",
133 \ 20: "#0000d7", 21: "#0000ff", 22: "#005f00", 23: "#005f5f",
134 \ 24: "#005f87", 25: "#005faf", 26: "#005fd7", 27: "#005fff",
135 \ 28: "#008700", 29: "#00875f", 30: "#008787", 31: "#0087af",
136 \ 32: "#0087d7", 33: "#0087ff", 34: "#00af00", 35: "#00af5f",
137 \ 36: "#00af87", 37: "#00afaf", 38: "#00afd7", 39: "#00afff",
138 \ 40: "#00d700", 41: "#00d75f", 42: "#00d787", 43: "#00d7af",
139 \ 44: "#00d7d7", 45: "#00d7ff", 46: "#00ff00", 47: "#00ff5f",
140 \ 48: "#00ff87", 49: "#00ffaf", 50: "#00ffd7", 51: "#00ffff",
141 \ 52: "#5f0000", 53: "#5f005f", 54: "#5f0087", 55: "#5f00af",
142 \ 56: "#5f00d7", 57: "#5f00ff", 58: "#5f5f00", 59: "#5f5f5f",
143 \ 60: "#5f5f87", 61: "#5f5faf", 62: "#5f5fd7", 63: "#5f5fff",
144 \ 64: "#5f8700"
145 \ })
146 call extend(s:cterm_color, {
147 \ 65: "#5f875f", 66: "#5f8787", 67: "#5f87af", 68: "#5f87d7",
148 \ 69: "#5f87ff", 70: "#5faf00", 71: "#5faf5f", 72: "#5faf87",
149 \ 73: "#5fafaf", 74: "#5fafd7", 75: "#5fafff", 76: "#5fd700",
150 \ 77: "#5fd75f", 78: "#5fd787", 79: "#5fd7af", 80: "#5fd7d7",
151 \ 81: "#5fd7ff", 82: "#5fff00", 83: "#5fff5f", 84: "#5fff87",
152 \ 85: "#5fffaf", 86: "#5fffd7", 87: "#5fffff", 88: "#870000",
153 \ 89: "#87005f", 90: "#870087", 91: "#8700af", 92: "#8700d7",
154 \ 93: "#8700ff", 94: "#875f00", 95: "#875f5f", 96: "#875f87",
155 \ 97: "#875faf", 98: "#875fd7", 99: "#875fff", 100: "#878700",
156 \ 101: "#87875f", 102: "#878787", 103: "#8787af", 104: "#8787d7",
157 \ 105: "#8787ff", 106: "#87af00", 107: "#87af5f", 108: "#87af87",
158 \ 109: "#87afaf", 110: "#87afd7", 111: "#87afff", 112: "#87d700"
159 \ })
160 call extend(s:cterm_color, {
161 \ 113: "#87d75f", 114: "#87d787", 115: "#87d7af", 116: "#87d7d7",
162 \ 117: "#87d7ff", 118: "#87ff00", 119: "#87ff5f", 120: "#87ff87",
163 \ 121: "#87ffaf", 122: "#87ffd7", 123: "#87ffff", 124: "#af0000",
164 \ 125: "#af005f", 126: "#af0087", 127: "#af00af", 128: "#af00d7",
165 \ 129: "#af00ff", 130: "#af5f00", 131: "#af5f5f", 132: "#af5f87",
166 \ 133: "#af5faf", 134: "#af5fd7", 135: "#af5fff", 136: "#af8700",
167 \ 137: "#af875f", 138: "#af8787", 139: "#af87af", 140: "#af87d7",
168 \ 141: "#af87ff", 142: "#afaf00", 143: "#afaf5f", 144: "#afaf87",
169 \ 145: "#afafaf", 146: "#afafd7", 147: "#afafff", 148: "#afd700",
170 \ 149: "#afd75f", 150: "#afd787", 151: "#afd7af", 152: "#afd7d7",
171 \ 153: "#afd7ff", 154: "#afff00", 155: "#afff5f", 156: "#afff87",
172 \ 157: "#afffaf", 158: "#afffd7"
173 \ })
174 call extend(s:cterm_color, {
175 \ 159: "#afffff", 160: "#d70000", 161: "#d7005f", 162: "#d70087",
176 \ 163: "#d700af", 164: "#d700d7", 165: "#d700ff", 166: "#d75f00",
177 \ 167: "#d75f5f", 168: "#d75f87", 169: "#d75faf", 170: "#d75fd7",
178 \ 171: "#d75fff", 172: "#d78700", 173: "#d7875f", 174: "#d78787",
179 \ 175: "#d787af", 176: "#d787d7", 177: "#d787ff", 178: "#d7af00",
180 \ 179: "#d7af5f", 180: "#d7af87", 181: "#d7afaf", 182: "#d7afd7",
181 \ 183: "#d7afff", 184: "#d7d700", 185: "#d7d75f", 186: "#d7d787",
182 \ 187: "#d7d7af", 188: "#d7d7d7", 189: "#d7d7ff", 190: "#d7ff00",
183 \ 191: "#d7ff5f", 192: "#d7ff87", 193: "#d7ffaf", 194: "#d7ffd7",
184 \ 195: "#d7ffff", 196: "#ff0000", 197: "#ff005f", 198: "#ff0087",
185 \ 199: "#ff00af", 200: "#ff00d7", 201: "#ff00ff", 202: "#ff5f00",
186 \ 203: "#ff5f5f", 204: "#ff5f87"
187 \ })
188 call extend(s:cterm_color, {
189 \ 205: "#ff5faf", 206: "#ff5fd7", 207: "#ff5fff", 208: "#ff8700",
190 \ 209: "#ff875f", 210: "#ff8787", 211: "#ff87af", 212: "#ff87d7",
191 \ 213: "#ff87ff", 214: "#ffaf00", 215: "#ffaf5f", 216: "#ffaf87",
192 \ 217: "#ffafaf", 218: "#ffafd7", 219: "#ffafff", 220: "#ffd700",
193 \ 221: "#ffd75f", 222: "#ffd787", 223: "#ffd7af", 224: "#ffd7d7",
194 \ 225: "#ffd7ff", 226: "#ffff00", 227: "#ffff5f", 228: "#ffff87",
195 \ 229: "#ffffaf", 230: "#ffffd7", 231: "#ffffff", 232: "#080808",
196 \ 233: "#121212", 234: "#1c1c1c", 235: "#262626", 236: "#303030",
197 \ 237: "#3a3a3a", 238: "#444444", 239: "#4e4e4e", 240: "#585858",
198 \ 241: "#626262", 242: "#6c6c6c", 243: "#767676", 244: "#808080",
199 \ 245: "#8a8a8a", 246: "#949494", 247: "#9e9e9e", 248: "#a8a8a8",
200 \ 249: "#b2b2b2", 250: "#bcbcbc", 251: "#c6c6c6", 252: "#d0d0d0",
201 \ 253: "#dadada", 254: "#e4e4e4", 255: "#eeeeee"
202 \ })
Bram Moolenaar313b7232007-05-05 17:56:55 +0000203 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000204 endif
205endif
206
207" Return good color specification: in GUI no transformation is done, in
Bram Moolenaar313b7232007-05-05 17:56:55 +0000208" terminal return RGB values of known colors and empty string for unknown
Bram Moolenaar071d4272004-06-13 20:20:40 +0000209if s:whatterm == "gui"
210 function! s:HtmlColor(color)
211 return a:color
212 endfun
213else
214 function! s:HtmlColor(color)
Bram Moolenaar313b7232007-05-05 17:56:55 +0000215 if has_key(s:cterm_color, a:color)
216 return s:cterm_color[a:color]
Bram Moolenaar071d4272004-06-13 20:20:40 +0000217 else
218 return ""
219 endif
220 endfun
221endif
222
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200223" Find out the background and foreground color for use later
fritzophrenic86cfb392023-09-08 12:20:01 -0500224let s:fgc = s:HtmlColor(synIDattr(hlID("Normal")->synIDtrans(), "fg#", s:whatterm))
225let s:bgc = s:HtmlColor(synIDattr(hlID("Normal")->synIDtrans(), "bg#", s:whatterm))
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200226if s:fgc == ""
227 let s:fgc = ( &background == "dark" ? "#ffffff" : "#000000" )
228endif
229if s:bgc == ""
230 let s:bgc = ( &background == "dark" ? "#000000" : "#ffffff" )
231endif
232
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200233if !s:settings.use_css
Bram Moolenaar071d4272004-06-13 20:20:40 +0000234 " Return opening HTML tag for given highlight id
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200235 function! s:HtmlOpening(id, extra_attrs)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000236 let a = ""
fritzophrenic86cfb392023-09-08 12:20:01 -0500237 let translated_ID = synIDtrans(a:id)
238 if synIDattr(translated_ID, "inverse")
Bram Moolenaar071d4272004-06-13 20:20:40 +0000239 " For inverse, we always must set both colors (and exchange them)
fritzophrenic86cfb392023-09-08 12:20:01 -0500240 let x = s:HtmlColor(synIDattr(translated_ID, "fg#", s:whatterm))
241 let a = a .. '<span '..a:extra_attrs..'style="background-color: ' .. ( x != "" ? x : s:fgc ) .. '">'
242 let x = s:HtmlColor(synIDattr(translated_ID, "bg#", s:whatterm))
243 let a = a .. '<font color="' .. ( x != "" ? x : s:bgc ) .. '">'
Bram Moolenaar071d4272004-06-13 20:20:40 +0000244 else
fritzophrenic86cfb392023-09-08 12:20:01 -0500245 let x = s:HtmlColor(synIDattr(translated_ID, "bg#", s:whatterm))
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200246 if x != ""
fritzophrenic86cfb392023-09-08 12:20:01 -0500247 let a = a .. '<span '..a:extra_attrs..'style="background-color: ' .. x .. '">'
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200248 elseif !empty(a:extra_attrs)
fritzophrenic86cfb392023-09-08 12:20:01 -0500249 let a = a .. '<span '..a:extra_attrs..'>'
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200250 endif
fritzophrenic86cfb392023-09-08 12:20:01 -0500251 let x = s:HtmlColor(synIDattr(translated_ID, "fg#", s:whatterm))
252 if x != "" | let a = a .. '<font color="' .. x .. '">' | endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000253 endif
fritzophrenic86cfb392023-09-08 12:20:01 -0500254 if synIDattr(translated_ID, "bold") | let a = a .. "<b>" | endif
255 if synIDattr(translated_ID, "italic") | let a = a .. "<i>" | endif
256 if synIDattr(translated_ID, "underline") | let a = a .. "<u>" | endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000257 return a
258 endfun
259
260 " Return closing HTML tag for given highlight id
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200261 function! s:HtmlClosing(id, has_extra_attrs)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000262 let a = ""
fritzophrenic86cfb392023-09-08 12:20:01 -0500263 let translated_ID = synIDtrans(a:id)
264 if synIDattr(translated_ID, "underline") | let a = a .. "</u>" | endif
265 if synIDattr(translated_ID, "italic") | let a = a .. "</i>" | endif
266 if synIDattr(translated_ID, "bold") | let a = a .. "</b>" | endif
267 if synIDattr(translated_ID, "inverse")
268 let a = a .. '</font></span>'
Bram Moolenaar071d4272004-06-13 20:20:40 +0000269 else
fritzophrenic86cfb392023-09-08 12:20:01 -0500270 let x = s:HtmlColor(synIDattr(translated_ID, "fg#", s:whatterm))
271 if x != "" | let a = a .. '</font>' | endif
272 let x = s:HtmlColor(synIDattr(translated_ID, "bg#", s:whatterm))
273 if x != "" || a:has_extra_attrs | let a = a .. '</span>' | endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000274 endif
275 return a
276 endfun
277endif
278
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200279" Use a different function for formatting based on user options. This way we
280" can avoid a lot of logic during the actual execution.
281"
282" Build the function line by line containing only what is needed for the options
283" in use for maximum code sharing with minimal branch logic for greater speed.
284"
285" Note, 'exec' commands do not recognize line continuations, so must concatenate
286" lines rather than continue them.
287if s:settings.use_css
288 " save CSS to a list of rules to add to the output at the end of processing
289
290 " first, get the style names we need
fritzophrenic86cfb392023-09-08 12:20:01 -0500291 let s:wrapperfunc_lines = []
292 call add(s:wrapperfunc_lines, [])
293 let s:wrapperfunc_lines[-1] =<< trim ENDLET
294 function! s:BuildStyleWrapper(style_id, diff_style_id, extra_attrs, text, make_unselectable, unformatted)
295
296 let l:style_name = synIDattr(a:style_id, "name", s:whatterm)
297 ENDLET
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200298 if &diff
fritzophrenic86cfb392023-09-08 12:20:01 -0500299 call add(s:wrapperfunc_lines, [])
300 let s:wrapperfunc_lines[-1] =<< trim ENDLET
301 let l:diff_style_name = synIDattr(a:diff_style_id, "name", s:whatterm)
302 ENDLET
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200303
fritzophrenic86cfb392023-09-08 12:20:01 -0500304 " Add normal groups and diff groups to separate lists so we can order them to
305 " allow diff highlight to override normal highlight
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200306
fritzophrenic86cfb392023-09-08 12:20:01 -0500307 " if primary style IS a diff style, grab it from the diff cache instead
308 " (always succeeds because we pre-populate it)
309 call add(s:wrapperfunc_lines, [])
310 let s:wrapperfunc_lines[-1] =<< trim ENDLET
311
312 if a:style_id == s:DIFF_D_ID || a:style_id == s:DIFF_A_ID || a:style_id == s:DIFF_C_ID || a:style_id == s:DIFF_T_ID
313 let l:saved_style = get(s:diffstylelist,a:style_id)
314 else
315 ENDLET
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200316 endif
317
318 " get primary style info from cache or build it on the fly if not found
fritzophrenic86cfb392023-09-08 12:20:01 -0500319 call add(s:wrapperfunc_lines, [])
320 let s:wrapperfunc_lines[-1] =<< trim ENDLET
321 let l:saved_style = get(s:stylelist,a:style_id)
322 if type(l:saved_style) == type(0)
323 unlet l:saved_style
324 let l:saved_style = s:CSS1(a:style_id)
325 if l:saved_style != ""
326 let l:saved_style = "." .. l:style_name .. " { " .. l:saved_style .. "}"
327 endif
328 let s:stylelist[a:style_id] = l:saved_style
329 endif
330 ENDLET
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200331 if &diff
fritzophrenic86cfb392023-09-08 12:20:01 -0500332 call add(s:wrapperfunc_lines, [])
333 let s:wrapperfunc_lines[-1] =<< trim ENDLET
334 endif
335 ENDLET
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200336 endif
fritzophrenic86cfb392023-09-08 12:20:01 -0500337" Ignore this comment, just bypassing a highlighting issue: if
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200338
339 " Build the wrapper tags around the text. It turns out that caching these
340 " gives pretty much zero performance gain and adds a lot of logic.
341
fritzophrenic86cfb392023-09-08 12:20:01 -0500342 call add(s:wrapperfunc_lines, [])
343 let s:wrapperfunc_lines[-1] =<< trim ENDLET
344
345 if l:saved_style == "" && empty(a:extra_attrs)
346 ENDLET
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200347 if &diff
fritzophrenic86cfb392023-09-08 12:20:01 -0500348 call add(s:wrapperfunc_lines, [])
349 let s:wrapperfunc_lines[-1] =<< trim ENDLET
350 if a:diff_style_id <= 0
351 ENDLET
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200352 endif
353 " no surroundings if neither primary nor diff style has any info
fritzophrenic86cfb392023-09-08 12:20:01 -0500354 call add(s:wrapperfunc_lines, [])
355 let s:wrapperfunc_lines[-1] =<< trim ENDLET
356 return a:text
357 ENDLET
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200358 if &diff
359 " no primary style, but diff style
fritzophrenic86cfb392023-09-08 12:20:01 -0500360 call add(s:wrapperfunc_lines, [])
361 let s:wrapperfunc_lines[-1] =<< trim ENDLET
362 else
363 return '<span class="' ..l:diff_style_name .. '">'..a:text.."</span>"
364 endif
365 ENDLET
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200366 endif
fritzophrenic86cfb392023-09-08 12:20:01 -0500367 " Ignore this comment, just bypassing a highlighting issue: if
368
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200369 " open tag for non-empty primary style
fritzophrenic86cfb392023-09-08 12:20:01 -0500370 call add(s:wrapperfunc_lines, [])
371 let s:wrapperfunc_lines[-1] =<< trim ENDLET
372 else
373 ENDLET
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200374 " non-empty primary style. handle either empty or non-empty diff style.
375 "
376 " separate the two classes by a space to apply them both if there is a diff
377 " style name, unless the primary style is empty, then just use the diff style
378 " name
fritzophrenic86cfb392023-09-08 12:20:01 -0500379 let s:diffstyle =
380 \ (&diff ? '(a:diff_style_id <= 0 ? "" : " " .. l:diff_style_name)..'
381 \ : '')
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200382 if s:settings.prevent_copy == ""
fritzophrenic86cfb392023-09-08 12:20:01 -0500383 call add(s:wrapperfunc_lines, [])
384 let s:wrapperfunc_lines[-1] =<< trim eval ENDLET
385 return "<span "..a:extra_attrs..'class="' .. l:style_name ..{s:diffstyle}'">'..a:text.."</span>"
386 ENDLET
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200387 else
388
Bram Moolenaar0c0734d2019-11-26 21:44:46 +0100389 " New method: use generated content in the CSS. The only thing needed here
390 " is a span with no content, with an attribute holding the desired text.
391 "
392 " Old method: use an <input> element when text is unsectable. This is still
393 " used in conditional comments for Internet Explorer, where the new method
394 " doesn't work.
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200395 "
396 " Wrap the <input> in a <span> to allow fixing the stupid bug in some fonts
397 " which cause browsers to display a 1px gap between lines when these
398 " <input>s have a background color (maybe not really a bug, this isn't
399 " well-defined)
400 "
401 " use strwidth, because we care only about how many character boxes are
402 " needed to size the input, we don't care how many characters (including
403 " separately counted composing chars, from strchars()) or bytes (from
404 " len())the string contains. strdisplaywidth() is not needed because none of
405 " the unselectable groups can contain tab characters (fold column, fold
406 " text, line number).
407 "
408 " Note, if maxlength property needs to be added in the future, it will need
409 " to use strchars(), because HTML specifies that the maxlength parameter
410 " uses the number of unique codepoints for its limit.
fritzophrenic86cfb392023-09-08 12:20:01 -0500411 call add(s:wrapperfunc_lines, [])
412 let s:wrapperfunc_lines[-1] =<< trim eval ENDLET
413 if a:make_unselectable
414 let return_span = "<span "..a:extra_attrs..'class="' .. l:style_name ..{s:diffstyle}'"'
415 ENDLET
Bram Moolenaar0c0734d2019-11-26 21:44:46 +0100416 if s:settings.use_input_for_pc !=# 'all'
fritzophrenic86cfb392023-09-08 12:20:01 -0500417 call add(s:wrapperfunc_lines, [])
418 let s:wrapperfunc_lines[-1] =<< trim ENDLET
419 let return_span ..= " data-" .. l:style_name .. '-content="'..a:text..'"'
420 ENDLET
Bram Moolenaar0c0734d2019-11-26 21:44:46 +0100421 endif
fritzophrenic86cfb392023-09-08 12:20:01 -0500422 call add(s:wrapperfunc_lines, [])
423 let s:wrapperfunc_lines[-1] =<< trim ENDLET
424 let return_span ..= '>'
425 ENDLET
Bram Moolenaar0c0734d2019-11-26 21:44:46 +0100426 if s:settings.use_input_for_pc !=# 'none'
fritzophrenic86cfb392023-09-08 12:20:01 -0500427 call add(s:wrapperfunc_lines, [])
428 let s:wrapperfunc_lines[-1] =<< trim eval ENDLET
429 let return_span ..= '<input'..s:unselInputType..' class="' .. l:style_name ..{s:diffstyle}'"'
430 let return_span ..= ' value="'..substitute(a:unformatted,'\s\+$',"","")..'"'
431 let return_span ..= " onselect='this.blur(); return false;'"
432 let return_span ..= " onmousedown='this.blur(); return false;'"
433 let return_span ..= " onclick='this.blur(); return false;'"
434 let return_span ..= " readonly='readonly'"
435 let return_span ..= ' size="'..strwidth(a:unformatted)..'"'
436 let return_span ..= (s:settings.use_xhtml ? '/>' : '>')
437 ENDLET
Bram Moolenaar0c0734d2019-11-26 21:44:46 +0100438 endif
fritzophrenic86cfb392023-09-08 12:20:01 -0500439 call add(s:wrapperfunc_lines, [])
440 let s:wrapperfunc_lines[-1] =<< trim eval ENDLET
441 return return_span..'</span>'
442 else
443 return "<span "..a:extra_attrs..'class="' .. l:style_name .. {s:diffstyle}'">'..a:text.."</span>"
444 endif
445 ENDLET
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200446 endif
fritzophrenic86cfb392023-09-08 12:20:01 -0500447 call add(s:wrapperfunc_lines, [])
448 let s:wrapperfunc_lines[-1] =<< trim ENDLET
449 endif
450 endfun
451 ENDLET
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200452else
453 " Non-CSS method just needs the wrapper.
454 "
455 " Functions used to get opening/closing automatically return null strings if
456 " no styles exist.
457 if &diff
fritzophrenic86cfb392023-09-08 12:20:01 -0500458 let s:wrapperfunc_lines =<< trim ENDLET
459 function! s:BuildStyleWrapper(style_id, diff_style_id, extra_attrs, text, unusedarg, unusedarg2)
460 if a:diff_style_id <= 0
461 let l:diff_opening = s:HtmlOpening(a:diff_style_id, "")
462 let l:diff_closing = s:HtmlClosing(a:diff_style_id, 0)
463 else
464 let l:diff_opening = ""
465 let l:diff_closing = ""
466 endif
467 return s:HtmlOpening(a:style_id, a:extra_attrs)..l:diff_opening..a:text..l:diff_closing..s:HtmlClosing(a:style_id, !empty(a:extra_attrs))
468 endfun
469 ENDLET
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200470 else
fritzophrenic86cfb392023-09-08 12:20:01 -0500471 let s:wrapperfunc_lines =<< trim ENDLET
472 function! s:BuildStyleWrapper(style_id, diff_style_id, extra_attrs, text, unusedarg, unusedarg2)
473 return s:HtmlOpening(a:style_id, a:extra_attrs)..a:text..s:HtmlClosing(a:style_id, !empty(a:extra_attrs))
474 endfun
475 ENDLET
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200476 endif
477endif
478
479" create the function we built line by line above
fritzophrenic86cfb392023-09-08 12:20:01 -0500480exec join(flatten(s:wrapperfunc_lines), "\n")
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200481
482let s:diff_mode = &diff
483
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000484" Return HTML valid characters enclosed in a span of class style_name with
485" unprintable characters expanded and double spaces replaced as necessary.
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200486"
487" TODO: eliminate unneeded logic like done for BuildStyleWrapper
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200488function! s:HtmlFormat(text, style_id, diff_style_id, extra_attrs, make_unselectable)
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000489 " Replace unprintable characters
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200490 let unformatted = strtrans(a:text)
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000491
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200492 let formatted = unformatted
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +0200493
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000494 " Replace the reserved html characters
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +0100495 let formatted = substitute(formatted, '&', '\&amp;', 'g')
496 let formatted = substitute(formatted, '<', '\&lt;', 'g')
497 let formatted = substitute(formatted, '>', '\&gt;', 'g')
498 let formatted = substitute(formatted, '"', '\&quot;', 'g')
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200499 " &apos; is not valid in HTML but it is in XHTML, so just use the numeric
500 " reference for it instead. Needed because it could appear in quotes
501 " especially if unselectable regions is turned on.
502 let formatted = substitute(formatted, '"', '\&#0039;', 'g')
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +0100503
504 " Replace a "form feed" character with HTML to do a page break
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200505 " TODO: need to prevent this in unselectable areas? Probably it should never
506 " BE in an unselectable area...
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +0100507 let formatted = substitute(formatted, "\x0c", '<hr class="PAGE-BREAK">', 'g')
508
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200509 " Replace double spaces, leading spaces, and trailing spaces if needed
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000510 if ' ' != s:HtmlSpace
fritzophrenic86cfb392023-09-08 12:20:01 -0500511 let formatted = substitute(formatted, ' ', s:HtmlSpace .. s:HtmlSpace, 'g')
Bram Moolenaar8424a622006-04-19 21:23:36 +0000512 let formatted = substitute(formatted, '^ ', s:HtmlSpace, 'g')
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200513 let formatted = substitute(formatted, ' \+$', s:HtmlSpace, 'g')
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000514 endif
515
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200516 " Enclose in the correct format
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200517 return s:BuildStyleWrapper(a:style_id, a:diff_style_id, a:extra_attrs, formatted, a:make_unselectable, unformatted)
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000518endfun
519
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200520" set up functions to call HtmlFormat in certain ways based on whether the
521" element is supposed to be unselectable or not
522if s:settings.prevent_copy =~# 'n'
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200523 if s:settings.number_lines
Bram Moolenaar31c31672013-06-26 13:28:14 +0200524 if s:settings.line_ids
525 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr)
526 if a:lnr > 0
fritzophrenic86cfb392023-09-08 12:20:01 -0500527 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, 'id="'..(exists('g:html_diff_win_num') ? 'W'..g:html_diff_win_num : "")..'L'..a:lnr..s:settings.id_suffix..'" ', 1)
Bram Moolenaar31c31672013-06-26 13:28:14 +0200528 else
529 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1)
530 endif
531 endfun
532 else
533 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200534 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1)
Bram Moolenaar31c31672013-06-26 13:28:14 +0200535 endfun
536 endif
537 elseif s:settings.line_ids
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200538 " if lines are not being numbered the only reason this function gets called
Bram Moolenaar6c391a72021-09-09 21:55:11 +0200539 " is to put the line IDs on each line; "text" will be empty but lnr will
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200540 " always be non-zero, however we don't want to use the <input> because that
541 " won't work as nice for empty text
542 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr)
fritzophrenic86cfb392023-09-08 12:20:01 -0500543 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, 'id="'..(exists('g:html_diff_win_num') ? 'W'..g:html_diff_win_num : "")..'L'..a:lnr..s:settings.id_suffix..'" ', 0)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200544 endfun
545 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200546else
Bram Moolenaar31c31672013-06-26 13:28:14 +0200547 if s:settings.line_ids
548 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr)
549 if a:lnr > 0
fritzophrenic86cfb392023-09-08 12:20:01 -0500550 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, 'id="'..(exists('g:html_diff_win_num') ? 'W'..g:html_diff_win_num : "")..'L'..a:lnr..s:settings.id_suffix..'" ', 0)
Bram Moolenaar31c31672013-06-26 13:28:14 +0200551 else
552 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0)
553 endif
554 endfun
555 else
556 function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200557 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0)
Bram Moolenaar31c31672013-06-26 13:28:14 +0200558 endfun
559 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200560endif
561if s:settings.prevent_copy =~# 'd'
562 function! s:HtmlFormat_d(text, style_id, diff_style_id)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200563 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200564 endfun
565else
566 function! s:HtmlFormat_d(text, style_id, diff_style_id)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200567 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200568 endfun
569endif
570if s:settings.prevent_copy =~# 'f'
Bram Moolenaar0c0734d2019-11-26 21:44:46 +0100571 if s:settings.use_input_for_pc ==# 'none'
572 " Simply space-pad to the desired width inside the generated content (note
573 " that the FoldColumn definition includes a whitespace:pre rule)
574 function! s:FoldColumn_build(char, len, numfill, char2, class, click)
fritzophrenic86cfb392023-09-08 12:20:01 -0500575 return "<a href='#' class='"..a:class.."' onclick='"..a:click.."' data-FoldColumn-content='".
576 \ repeat(a:char, a:len)..a:char2..repeat(' ', a:numfill).
Bram Moolenaar0c0734d2019-11-26 21:44:46 +0100577 \ "'></a>"
578 endfun
579 function! s:FoldColumn_fill()
580 return s:HtmlFormat(repeat(' ', s:foldcolumn), s:FOLD_C_ID, 0, "", 1)
581 endfun
582 else
583 " Note the <input> elements for fill spaces will have a single space for
584 " content, to allow active cursor CSS selection to work.
585 "
586 " Wrap the whole thing in a span for the 1px padding workaround for gaps.
587 "
588 " Build the function line by line containing only what is needed for the
589 " options in use for maximum code sharing with minimal branch logic for
590 " greater speed.
591 "
592 " Note, 'exec' commands do not recognize line continuations, so must
593 " concatenate lines rather than continue them.
fritzophrenic86cfb392023-09-08 12:20:01 -0500594 let s:build_fun_lines = []
595 call add(s:build_fun_lines, [])
596 let s:build_fun_lines[-1] =<< trim ENDLET
597 function! s:FoldColumn_build(char, len, numfill, char2, class, click)
598 let l:input_open = "<input readonly='readonly'"..s:unselInputType
599 let l:input_open ..= " onselect='this.blur(); return false;'"
600 let l:input_open ..= " onmousedown='this.blur(); "..a:click.." return false;'"
601 let l:input_open ..= " onclick='return false;' size='"
602 let l:input_open ..= string(a:len + (empty(a:char2) ? 0 : 1) + a:numfill) .. "' "
603 let l:common_attrs = "class='FoldColumn' value='"
604 let l:input_close = (s:settings.use_xhtml ? "' />" : "'>")
605 let l:return_span = "<span class='"..a:class.."'>"
606 let l:return_span ..= l:input_open..l:common_attrs..repeat(a:char, a:len)..(a:char2)
607 let l:return_span ..= l:input_close
608 ENDLET
Bram Moolenaar0c0734d2019-11-26 21:44:46 +0100609 if s:settings.use_input_for_pc ==# 'fallback'
fritzophrenic86cfb392023-09-08 12:20:01 -0500610 call add(s:build_fun_lines, [])
611 let s:build_fun_lines[-1] =<< trim ENDLET
612 let l:return_span ..= "<a href='#' class='FoldColumn' onclick='"..a:click.."'"
613 let l:return_span ..= " data-FoldColumn-content='"
614 let l:return_span ..= repeat(a:char, a:len)..a:char2..repeat(' ', a:numfill)
615 let l:return_span ..= "'></a>"
616 ENDLET
Bram Moolenaar0c0734d2019-11-26 21:44:46 +0100617 endif
fritzophrenic86cfb392023-09-08 12:20:01 -0500618 call add(s:build_fun_lines, [])
619 let s:build_fun_lines[-1] =<< trim ENDLET
620 let l:return_span ..= "</span>"
621 return l:return_span
622 endfun
623 ENDLET
Bram Moolenaar0c0734d2019-11-26 21:44:46 +0100624 " create the function we built line by line above
fritzophrenic86cfb392023-09-08 12:20:01 -0500625 exec join(flatten(s:build_fun_lines), "\n")
Bram Moolenaar0c0734d2019-11-26 21:44:46 +0100626
627 function! s:FoldColumn_fill()
628 return s:FoldColumn_build(' ', s:foldcolumn, 0, '', 'FoldColumn', '')
629 endfun
630 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200631else
632 " For normal fold columns, simply space-pad to the desired width (note that
633 " the FoldColumn definition includes a whitespace:pre rule)
634 function! s:FoldColumn_build(char, len, numfill, char2, class, click)
fritzophrenic86cfb392023-09-08 12:20:01 -0500635 return "<a href='#' class='"..a:class.."' onclick='"..a:click.."'>".
636 \ repeat(a:char, a:len)..a:char2..repeat(' ', a:numfill).
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200637 \ "</a>"
638 endfun
639 function! s:FoldColumn_fill()
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200640 return s:HtmlFormat(repeat(' ', s:foldcolumn), s:FOLD_C_ID, 0, "", 0)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200641 endfun
642endif
643if s:settings.prevent_copy =~# 't'
644 " put an extra empty span at the end for dynamic folds, so the linebreak can
645 " be surrounded. Otherwise do it as normal.
646 "
647 " TODO: isn't there a better way to do this, than placing it here and using a
648 " substitute later?
649 if s:settings.dynamic_folds
650 function! s:HtmlFormat_t(text, style_id, diff_style_id)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200651 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1) .
652 \ s:HtmlFormat("", a:style_id, 0, "", 0)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200653 endfun
654 else
655 function! s:HtmlFormat_t(text, style_id, diff_style_id)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200656 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200657 endfun
658 endif
659else
660 function! s:HtmlFormat_t(text, style_id, diff_style_id)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200661 return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200662 endfun
663endif
664
Bram Moolenaar071d4272004-06-13 20:20:40 +0000665" Return CSS style describing given highlight id (can be empty)
666function! s:CSS1(id)
667 let a = ""
fritzophrenic86cfb392023-09-08 12:20:01 -0500668 let translated_ID = synIDtrans(a:id)
669 if synIDattr(translated_ID, "inverse")
Bram Moolenaar071d4272004-06-13 20:20:40 +0000670 " For inverse, we always must set both colors (and exchange them)
fritzophrenic86cfb392023-09-08 12:20:01 -0500671 let x = s:HtmlColor(synIDattr(translated_ID, "bg#", s:whatterm))
672 let a = a .. "color: " .. ( x != "" ? x : s:bgc ) .. "; "
673 let x = s:HtmlColor(synIDattr(translated_ID, "fg#", s:whatterm))
674 let a = a .. "background-color: " .. ( x != "" ? x : s:fgc ) .. "; "
Bram Moolenaar071d4272004-06-13 20:20:40 +0000675 else
fritzophrenic86cfb392023-09-08 12:20:01 -0500676 let x = s:HtmlColor(synIDattr(translated_ID, "fg#", s:whatterm))
677 if x != "" | let a = a .. "color: " .. x .. "; " | endif
678 let x = s:HtmlColor(synIDattr(translated_ID, "bg#", s:whatterm))
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200679 if x != ""
fritzophrenic86cfb392023-09-08 12:20:01 -0500680 let a = a .. "background-color: " .. x .. "; "
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200681 " stupid hack because almost every browser seems to have at least one font
682 " which shows 1px gaps between lines which have background
fritzophrenic86cfb392023-09-08 12:20:01 -0500683 let a = a .. "padding-bottom: 1px; "
684 elseif (translated_ID == s:FOLDED_ID || translated_ID == s:LINENR_ID || translated_ID == s:FOLD_C_ID) && !empty(s:settings.prevent_copy)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200685 " input elements default to a different color than the rest of the page
fritzophrenic86cfb392023-09-08 12:20:01 -0500686 let a = a .. "background-color: " .. s:bgc .. "; "
Bram Moolenaar6c35bea2012-07-25 17:49:10 +0200687 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000688 endif
fritzophrenic86cfb392023-09-08 12:20:01 -0500689 if synIDattr(translated_ID, "bold") | let a = a .. "font-weight: bold; " | endif
690 if synIDattr(translated_ID, "italic") | let a = a .. "font-style: italic; " | endif
691 if synIDattr(translated_ID, "underline") | let a = a .. "text-decoration: underline; " | endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000692 return a
693endfun
694
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200695if s:settings.dynamic_folds
Bram Moolenaar5c736222010-01-06 20:54:52 +0100696 " compares two folds as stored in our list of folds
697 " A fold is "less" than another if it starts at an earlier line number,
698 " or ends at a later line number, ties broken by fold level
699 function! s:FoldCompare(f1, f2)
700 if a:f1.firstline != a:f2.firstline
701 " put it before if it starts earlier
702 return a:f1.firstline - a:f2.firstline
703 elseif a:f1.lastline != a:f2.lastline
704 " put it before if it ends later
705 return a:f2.lastline - a:f1.lastline
706 else
707 " if folds begin and end on the same lines, put lowest fold level first
708 return a:f1.level - a:f2.level
709 endif
710 endfunction
711
712endif
713
Bram Moolenaar071d4272004-06-13 20:20:40 +0000714
715" Set some options to make it work faster.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000716" Don't report changes for :substitute, there will be many of them.
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200717" Don't change other windows; turn off scroll bind temporarily
Bram Moolenaar071d4272004-06-13 20:20:40 +0000718let s:old_title = &title
719let s:old_icon = &icon
720let s:old_et = &l:et
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200721let s:old_bind = &l:scrollbind
Bram Moolenaar071d4272004-06-13 20:20:40 +0000722let s:old_report = &report
723let s:old_search = @/
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200724let s:old_more = &more
Bram Moolenaar071d4272004-06-13 20:20:40 +0000725set notitle noicon
726setlocal et
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200727set nomore
Bram Moolenaar071d4272004-06-13 20:20:40 +0000728set report=1000000
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200729setlocal noscrollbind
Bram Moolenaar071d4272004-06-13 20:20:40 +0000730
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200731if exists(':ownsyntax') && exists('w:current_syntax')
732 let s:current_syntax = w:current_syntax
733elseif exists('b:current_syntax')
734 let s:current_syntax = b:current_syntax
735else
736 let s:current_syntax = 'none'
737endif
738
739if s:current_syntax == ''
740 let s:current_syntax = 'none'
741endif
742
Bram Moolenaarf0d58ef2018-11-16 16:13:44 +0100743" If the user is sourcing this script directly then the plugin version isn't
744" known because the main plugin script didn't load. In the usual case where the
745" user still has the full Vim runtime installed, or has this full plugin
746" installed in a package or something, then we can extract the version from the
747" main plugin file at it's usual spot relative to this file. Otherwise the user
748" is assembling their runtime piecemeal and we have no idea what versions of
749" other files may be present so don't even try to make a guess or assume the
750" presence of other specific files with specific meaning.
751"
752" We don't want to actually source the main plugin file here because the user
753" may have a good reason not to (e.g. they define their own TOhtml command or
754" something).
755"
756" If this seems way too complicated and convoluted, it is. Probably I should
757" have put the version information in the autoload file from the start. But the
758" version has been in the global variable for so long that changing it could
759" break a lot of user scripts.
760if exists("g:loaded_2html_plugin")
761 let s:pluginversion = g:loaded_2html_plugin
762else
763 if !exists("g:unloaded_tohtml_plugin")
fritzophrenic86cfb392023-09-08 12:20:01 -0500764 let s:main_plugin_path = expand("<sfile>:p:h:h").."/plugin/tohtml.vim"
Bram Moolenaarf0d58ef2018-11-16 16:13:44 +0100765 if filereadable(s:main_plugin_path)
766 let s:lines = readfile(s:main_plugin_path, "", 20)
767 call filter(s:lines, 'v:val =~ "loaded_2html_plugin = "')
768 if empty(s:lines)
769 let g:unloaded_tohtml_plugin = "unknown"
770 else
771 let g:unloaded_tohtml_plugin = substitute(s:lines[0], '.*loaded_2html_plugin = \([''"]\)\(\%(\1\@!.\)\+\)\1', '\2', '')
772 endif
773 unlet s:lines
774 else
775 let g:unloaded_tohtml_plugin = "unknown"
776 endif
777 unlet s:main_plugin_path
778 endif
779 let s:pluginversion = g:unloaded_tohtml_plugin
780endif
781
Bram Moolenaar071d4272004-06-13 20:20:40 +0000782" Split window to create a buffer with the HTML file.
783let s:orgbufnr = winbufnr(0)
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200784let s:origwin_stl = &l:stl
Bram Moolenaar071d4272004-06-13 20:20:40 +0000785if expand("%") == ""
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200786 if exists('g:html_diff_win_num')
fritzophrenic86cfb392023-09-08 12:20:01 -0500787 exec 'new Untitled_win'..g:html_diff_win_num..'.'.(s:settings.use_xhtml ? 'xhtml' : 'html')
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200788 else
fritzophrenic86cfb392023-09-08 12:20:01 -0500789 exec 'new Untitled.'..(s:settings.use_xhtml ? 'xhtml' : 'html')
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200790 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000791else
fritzophrenic86cfb392023-09-08 12:20:01 -0500792 exec 'new %.'..(s:settings.use_xhtml ? 'xhtml' : 'html')
Bram Moolenaar071d4272004-06-13 20:20:40 +0000793endif
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200794
795" Resize the new window to very small in order to make it draw faster
796let s:old_winheight = winheight(0)
797let s:old_winfixheight = &l:winfixheight
798if s:old_winheight > 2
799 resize 1 " leave enough room to view one line at a time
800 norm! G
801 norm! zt
802endif
803setlocal winfixheight
804
805let s:newwin_stl = &l:stl
806
807" on the new window, set the least time-consuming fold method
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200808let s:old_fen = &foldenable
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200809setlocal foldmethod=manual
Bram Moolenaar8df7f882010-08-13 11:30:02 +0200810setlocal nofoldenable
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200811
Bram Moolenaar071d4272004-06-13 20:20:40 +0000812let s:newwin = winnr()
813let s:orgwin = bufwinnr(s:orgbufnr)
814
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200815setlocal modifiable
Bram Moolenaar071d4272004-06-13 20:20:40 +0000816%d
817let s:old_paste = &paste
818set paste
819let s:old_magic = &magic
820set magic
821
Bram Moolenaar166af9b2010-11-16 20:34:40 +0100822" set the fileencoding to match the charset we'll be using
823let &l:fileencoding=s:settings.vim_encoding
824
825" According to http://www.w3.org/TR/html4/charset.html#doc-char-set, the byte
826" order mark is highly recommend on the web when using multibyte encodings. But,
827" it is not a good idea to include it on UTF-8 files. Otherwise, let Vim
828" determine when it is actually inserted.
829if s:settings.vim_encoding == 'utf-8'
830 setlocal nobomb
831else
832 setlocal bomb
833endif
834
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200835let s:lines = []
836
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200837if s:settings.use_xhtml
Bram Moolenaarbebca9d2010-08-07 15:47:30 +0200838 if s:settings.encoding != ""
fritzophrenic86cfb392023-09-08 12:20:01 -0500839 call add(s:lines, "<?xml version=\"1.0\" encoding=\"" .. s:settings.encoding .. "\"?>")
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000840 else
Bram Moolenaar349b2fb2010-07-16 20:35:36 +0200841 call add(s:lines, "<?xml version=\"1.0\"?>")
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000842 endif
Bram Moolenaar313b7232007-05-05 17:56:55 +0000843 let s:tag_close = ' />'
Bram Moolenaar071d4272004-06-13 20:20:40 +0000844else
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000845 let s:tag_close = '>'
846endif
847
848let s:HtmlSpace = ' '
Bram Moolenaarc1e37902006-04-18 21:55:01 +0000849let s:LeadingSpace = ' '
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +0000850let s:HtmlEndline = ''
Bram Moolenaar076e8b22010-08-05 21:54:00 +0200851if s:settings.no_pre
fritzophrenic86cfb392023-09-08 12:20:01 -0500852 let s:HtmlEndline = '<br' .. s:tag_close
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200853 let s:LeadingSpace = s:settings.use_xhtml ? '&#160;' : '&nbsp;'
fritzophrenic86cfb392023-09-08 12:20:01 -0500854 let s:HtmlSpace = '\' .. s:LeadingSpace
Bram Moolenaar071d4272004-06-13 20:20:40 +0000855endif
856
857" HTML header, with the title and generator ;-). Left free space for the CSS,
858" to be filled at the end.
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +0100859if !s:settings.no_doc
Bram Moolenaar0c0734d2019-11-26 21:44:46 +0100860 call extend(s:lines, [
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +0100861 \ "<html>",
862 \ "<head>"])
863 " include encoding as close to the top as possible, but only if not already
864 " contained in XML information (to avoid haggling over content type)
865 if s:settings.encoding != "" && !s:settings.use_xhtml
866 if s:html5
fritzophrenic86cfb392023-09-08 12:20:01 -0500867 call add(s:lines, '<meta charset="' .. s:settings.encoding .. '"' .. s:tag_close)
Bram Moolenaar5c736222010-01-06 20:54:52 +0100868 else
fritzophrenic86cfb392023-09-08 12:20:01 -0500869 call add(s:lines, "<meta http-equiv=\"content-type\" content=\"text/html; charset=" .. s:settings.encoding .. '"' .. s:tag_close)
Bram Moolenaar5c736222010-01-06 20:54:52 +0100870 endif
Bram Moolenaar0c0734d2019-11-26 21:44:46 +0100871 endif
Bram Moolenaar0c0734d2019-11-26 21:44:46 +0100872 call extend(s:lines, [
fritzophrenic86cfb392023-09-08 12:20:01 -0500873 \ ("<title>"..expand("%:p:~").."</title>"),
874 \ ("<meta name=\"Generator\" content=\"Vim/"..v:version/100.."."..v:version%100..'"'..s:tag_close),
875 \ ("<meta name=\"plugin-version\" content=\""..s:pluginversion..'"'..s:tag_close)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200876 \ ])
fritzophrenic86cfb392023-09-08 12:20:01 -0500877 call add(s:lines, '<meta name="syntax" content="'..s:current_syntax..'"'..s:tag_close)
878 call add(s:lines, '<meta name="settings" content="'..
879 \ join(filter(keys(s:settings),'s:settings[v:val]'),',')..
880 \ ',prevent_copy='..s:settings.prevent_copy..
881 \ ',use_input_for_pc='..s:settings.use_input_for_pc..
882 \ '"'..s:tag_close)
883 call add(s:lines, '<meta name="colorscheme" content="'..
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +0100884 \ (exists('g:colors_name')
885 \ ? g:colors_name
fritzophrenic86cfb392023-09-08 12:20:01 -0500886 \ : 'none').. '"'..s:tag_close)
Bram Moolenaar5c736222010-01-06 20:54:52 +0100887
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +0100888 if s:settings.use_css
889 call extend(s:lines, [
fritzophrenic86cfb392023-09-08 12:20:01 -0500890 \ "<style" .. (s:html5 ? "" : " type=\"text/css\"") .. ">",
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +0100891 \ s:settings.use_xhtml ? "" : "<!--"])
892 let s:ieonly = []
893 if s:settings.dynamic_folds
894 if s:settings.hover_unfold
895 " if we are doing hover_unfold, use css 2 with css 1 fallback for IE6
896 call extend(s:lines, [
897 \ ".FoldColumn { text-decoration: none; white-space: pre; }",
898 \ "",
899 \ "body * { margin: 0; padding: 0; }", "",
900 \ ".open-fold > span.Folded { display: none; }",
901 \ ".open-fold > .fulltext { display: inline; }",
902 \ ".closed-fold > .fulltext { display: none; }",
903 \ ".closed-fold > span.Folded { display: inline; }",
904 \ "",
905 \ ".open-fold > .toggle-open { display: none; }",
906 \ ".open-fold > .toggle-closed { display: inline; }",
907 \ ".closed-fold > .toggle-open { display: inline; }",
908 \ ".closed-fold > .toggle-closed { display: none; }",
909 \ "", "",
910 \ '/* opening a fold while hovering won''t be supported by IE6 and other',
911 \ "similar browsers, but it should fail gracefully. */",
912 \ ".closed-fold:hover > .fulltext { display: inline; }",
913 \ ".closed-fold:hover > .toggle-filler { display: none; }",
914 \ ".closed-fold:hover > .Folded { display: none; }"])
915 " TODO: IE6 is REALLY old and I can't even test it anymore. Maybe we
916 " should remove this? Leave it in for now, it was working at one point,
917 " and doesn't affect any modern browsers. Even newer IE versions should
918 " support the above code and ignore the following.
919 let s:ieonly = [
920 \ "<!--[if lt IE 7]><style type=\"text/css\">",
921 \ ".open-fold .fulltext { display: inline; }",
922 \ ".open-fold span.Folded { display: none; }",
923 \ ".open-fold .toggle-open { display: none; }",
924 \ ".open-fold .toggle-closed { display: inline; }",
925 \ "",
926 \ ".closed-fold .fulltext { display: none; }",
927 \ ".closed-fold span.Folded { display: inline; }",
928 \ ".closed-fold .toggle-open { display: inline; }",
929 \ ".closed-fold .toggle-closed { display: none; }",
930 \ "</style>",
931 \ "<![endif]-->",
932 \]
933 else
934 " if we aren't doing hover_unfold, use CSS 1 only
935 call extend(s:lines, [
936 \ ".FoldColumn { text-decoration: none; white-space: pre; }",
937 \ ".open-fold .fulltext { display: inline; }",
938 \ ".open-fold span.Folded { display: none; }",
939 \ ".open-fold .toggle-open { display: none; }",
940 \ ".open-fold .toggle-closed { display: inline; }",
941 \ "",
942 \ ".closed-fold .fulltext { display: none; }",
943 \ ".closed-fold span.Folded { display: inline; }",
944 \ ".closed-fold .toggle-open { display: inline; }",
945 \ ".closed-fold .toggle-closed { display: none; }",
946 \])
947 endif
948 endif
949 " else we aren't doing any dynamic folding, no need for any special rules
Bram Moolenaarf0d58ef2018-11-16 16:13:44 +0100950
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +0100951 call extend(s:lines, [
952 \ s:settings.use_xhtml ? "" : '-->',
953 \ "</style>",
954 \])
955 call extend(s:lines, s:ieonly)
956 unlet s:ieonly
957 endif
958
959 let s:uses_script = s:settings.dynamic_folds || s:settings.line_ids
960
961 " insert script tag if needed
962 if s:uses_script
963 call extend(s:lines, [
964 \ "",
fritzophrenic86cfb392023-09-08 12:20:01 -0500965 \ "<script" .. (s:html5 ? "" : " type='text/javascript'") .. ">",
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +0100966 \ s:settings.use_xhtml ? '//<![CDATA[' : "<!--"])
967 endif
968
969 " insert javascript to toggle folds open and closed
Bram Moolenaar31c31672013-06-26 13:28:14 +0200970 if s:settings.dynamic_folds
971 call extend(s:lines, [
972 \ "",
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +0100973 \ "function toggleFold(objID)",
974 \ "{",
975 \ " var fold;",
976 \ " fold = document.getElementById(objID);",
977 \ " if (fold.className == 'closed-fold')",
Bram Moolenaar31c31672013-06-26 13:28:14 +0200978 \ " {",
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +0100979 \ " fold.className = 'open-fold';",
Bram Moolenaar31c31672013-06-26 13:28:14 +0200980 \ " }",
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +0100981 \ " else if (fold.className == 'open-fold')",
982 \ " {",
983 \ " fold.className = 'closed-fold';",
984 \ " }",
985 \ "}"
Bram Moolenaar31c31672013-06-26 13:28:14 +0200986 \ ])
987 endif
Bram Moolenaar543b7ef2013-06-01 14:50:56 +0200988
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +0100989 if s:settings.line_ids
990 " insert javascript to get IDs from line numbers, and to open a fold before
991 " jumping to any lines contained therein
992 call extend(s:lines, [
993 \ "",
994 \ "/* function to open any folds containing a jumped-to line before jumping to it */",
995 \ "function JumpToLine()",
996 \ "{",
997 \ " var lineNum;",
998 \ " lineNum = window.location.hash;",
999 \ " lineNum = lineNum.substr(1); /* strip off '#' */",
1000 \ "",
1001 \ " if (lineNum.indexOf('L') == -1) {",
1002 \ " lineNum = 'L'+lineNum;",
1003 \ " }",
1004 \ " var lineElem = document.getElementById(lineNum);"
1005 \ ])
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001006
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +01001007 if s:settings.dynamic_folds
1008 call extend(s:lines, [
1009 \ "",
1010 \ " /* navigate upwards in the DOM tree to open all folds containing the line */",
1011 \ " var node = lineElem;",
fritzophrenic86cfb392023-09-08 12:20:01 -05001012 \ " while (node && node.id != 'vimCodeElement"..s:settings.id_suffix.."')",
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +01001013 \ " {",
1014 \ " if (node.className == 'closed-fold')",
1015 \ " {",
1016 \ " node.className = 'open-fold';",
1017 \ " }",
1018 \ " node = node.parentNode;",
1019 \ " }",
1020 \ ])
1021 endif
1022 call extend(s:lines, [
1023 \ " /* Always jump to new location even if the line was hidden inside a fold, or",
1024 \ " * we corrected the raw number to a line ID.",
1025 \ " */",
1026 \ " if (lineElem) {",
1027 \ " lineElem.scrollIntoView(true);",
1028 \ " }",
1029 \ " return true;",
1030 \ "}",
1031 \ "if ('onhashchange' in window) {",
1032 \ " window.onhashchange = JumpToLine;",
1033 \ "}"
1034 \ ])
1035 endif
1036
1037 " insert script closing tag if needed
1038 if s:uses_script
1039 call extend(s:lines, [
1040 \ '',
1041 \ s:settings.use_xhtml ? '//]]>' : '-->',
1042 \ "</script>"
1043 \ ])
1044 endif
1045
1046 call extend(s:lines, ["</head>",
fritzophrenic86cfb392023-09-08 12:20:01 -05001047 \ "<body"..(s:settings.line_ids ? " onload='JumpToLine();'" : "")..">"])
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +01001048endif
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001049
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001050if s:settings.no_pre
1051 " if we're not using CSS we use a font tag which can't have a div inside
1052 if s:settings.use_css
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +01001053 call extend(s:lines, ["<div id='vimCodeElement" .. s:settings.id_suffix .. "'>"])
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001054 endif
1055else
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +01001056 call extend(s:lines, ["<pre id='vimCodeElement" .. s:settings.id_suffix .. "'>"])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001057endif
1058
fritzophrenic86cfb392023-09-08 12:20:01 -05001059exe s:orgwin .. "wincmd w"
Bram Moolenaar071d4272004-06-13 20:20:40 +00001060
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001061" caches of style data
1062" initialize to include line numbers if using them
1063if s:settings.number_lines
fritzophrenic86cfb392023-09-08 12:20:01 -05001064 let s:stylelist = { s:LINENR_ID : ".LineNr { " .. s:CSS1( s:LINENR_ID ) .. "}" }
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001065else
1066 let s:stylelist = {}
1067endif
1068let s:diffstylelist = {
fritzophrenic86cfb392023-09-08 12:20:01 -05001069 \ s:DIFF_A_ID : ".DiffAdd { " .. s:CSS1( s:DIFF_A_ID ) .. "}",
1070 \ s:DIFF_C_ID : ".DiffChange { " .. s:CSS1( s:DIFF_C_ID ) .. "}",
1071 \ s:DIFF_D_ID : ".DiffDelete { " .. s:CSS1( s:DIFF_D_ID ) .. "}",
1072 \ s:DIFF_T_ID : ".DiffText { " .. s:CSS1( s:DIFF_T_ID ) .. "}"
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001073 \ }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001074
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001075" set up progress bar in the status line
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001076if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001077 " ProgressBar Indicator
1078 let s:progressbar={}
1079
Bram Moolenaar6c391a72021-09-09 21:55:11 +02001080 " Progressbar specific functions
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001081
1082 func! s:SetProgbarColor()
1083 if hlID("TOhtmlProgress") != 0
1084 hi! link TOhtmlProgress_auto TOhtmlProgress
1085 elseif hlID("TOhtmlProgress_auto")==0 ||
1086 \ !exists("s:last_colors_name") || !exists("g:colors_name") ||
1087 \ g:colors_name != s:last_colors_name
1088 let s:last_colors_name = exists("g:colors_name") ? g:colors_name : "none"
1089
fritzophrenic86cfb392023-09-08 12:20:01 -05001090 let l:diffatr = synIDattr(hlID("DiffDelete")->synIDtrans(), "reverse", s:whatterm) ? "fg#" : "bg#"
1091 let l:stlatr = synIDattr(hlID("StatusLine")->synIDtrans(), "reverse", s:whatterm) ? "fg#" : "bg#"
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001092
fritzophrenic86cfb392023-09-08 12:20:01 -05001093 let l:progbar_color = synIDattr(hlID("DiffDelete")->synIDtrans(), l:diffatr, s:whatterm)
1094 let l:stl_color = synIDattr(hlID("StatusLine")->synIDtrans(), l:stlatr, s:whatterm)
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001095
1096 if "" == l:progbar_color
fritzophrenic86cfb392023-09-08 12:20:01 -05001097 let l:progbar_color = synIDattr(hlID("DiffDelete")->synIDtrans(), "reverse", s:whatterm) ? s:fgc : s:bgc
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001098 endif
1099 if "" == l:stl_color
fritzophrenic86cfb392023-09-08 12:20:01 -05001100 let l:stl_color = synIDattr(hlID("StatusLine")->synIDtrans(), "reverse", s:whatterm) ? s:fgc : s:bgc
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001101 endif
1102
1103 if l:progbar_color == l:stl_color
1104 if s:whatterm == 'cterm'
1105 if l:progbar_color >= (&t_Co/2)
1106 let l:progbar_color-=1
1107 else
1108 let l:progbar_color+=1
1109 endif
1110 else
1111 let l:rgb = map(matchlist(l:progbar_color, '#\zs\x\x\ze\(\x\x\)\(\x\x\)')[:2], 'str2nr(v:val, 16)')
1112 let l:avg = (l:rgb[0] + l:rgb[1] + l:rgb[2])/3
1113 if l:avg >= 128
1114 let l:avg_new = l:avg
1115 while l:avg - l:avg_new < 0x15
1116 let l:rgb = map(l:rgb, 'v:val * 3 / 4')
1117 let l:avg_new = (l:rgb[0] + l:rgb[1] + l:rgb[2])/3
1118 endwhile
1119 else
1120 let l:avg_new = l:avg
1121 while l:avg_new - l:avg < 0x15
1122 let l:rgb = map(l:rgb, 'min([max([v:val, 4]) * 5 / 4, 255])')
1123 let l:avg_new = (l:rgb[0] + l:rgb[1] + l:rgb[2])/3
1124 endwhile
1125 endif
1126 let l:progbar_color = printf("#%02x%02x%02x", l:rgb[0], l:rgb[1], l:rgb[2])
1127 endif
1128 echomsg "diff detected progbar color set to" l:progbar_color
1129 endif
fritzophrenic86cfb392023-09-08 12:20:01 -05001130 exe "hi TOhtmlProgress_auto" s:whatterm.."bg="..l:progbar_color
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001131 endif
1132 endfun
1133
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001134 func! s:ProgressBar(title, max_value, winnr)
1135 let pgb=copy(s:progressbar)
fritzophrenic86cfb392023-09-08 12:20:01 -05001136 let pgb.title = a:title..' '
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001137 let pgb.max_value = a:max_value
1138 let pgb.winnr = a:winnr
1139 let pgb.cur_value = 0
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001140
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001141 let pgb.items = { 'title' : { 'color' : 'Statusline' },
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001142 \'bar' : { 'color' : 'Statusline' , 'fillcolor' : 'TOhtmlProgress_auto' , 'bg' : 'Statusline' } ,
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001143 \'counter' : { 'color' : 'Statusline' } }
1144 let pgb.last_value = 0
1145 let pgb.needs_redraw = 0
1146 " Note that you must use len(split) instead of len() if you want to use
1147 " unicode in title.
1148 "
1149 " Subtract 3 for spacing around the title.
1150 " Subtract 4 for the percentage display.
1151 " Subtract 2 for spacing before this.
1152 " Subtract 2 more for the '|' on either side of the progress bar
1153 let pgb.subtractedlen=len(split(pgb.title, '\zs'))+3+4+2+2
1154 let pgb.max_len = 0
1155 set laststatus=2
1156 return pgb
1157 endfun
1158
1159 " Function: progressbar.calculate_ticks() {{{1
1160 func! s:progressbar.calculate_ticks(pb_len)
1161 if a:pb_len<=0
1162 let pb_len = 100
1163 else
1164 let pb_len = a:pb_len
1165 endif
1166 let self.progress_ticks = map(range(pb_len+1), "v:val * self.max_value / pb_len")
1167 endfun
1168
1169 "Function: progressbar.paint()
1170 func! s:progressbar.paint()
1171 " Recalculate widths.
1172 let max_len = winwidth(self.winnr)
1173 let pb_len = 0
1174 " always true on first call because of initial value of self.max_len
1175 if max_len != self.max_len
1176 let self.max_len = max_len
1177
1178 " Progressbar length
1179 let pb_len = max_len - self.subtractedlen
1180
1181 call self.calculate_ticks(pb_len)
1182
1183 let self.needs_redraw = 1
1184 let cur_value = 0
1185 let self.pb_len = pb_len
1186 else
1187 " start searching at the last found index to make the search for the
1188 " appropriate tick value normally take 0 or 1 comparisons
1189 let cur_value = self.last_value
1190 let pb_len = self.pb_len
1191 endif
1192
1193 let cur_val_max = pb_len > 0 ? pb_len : 100
1194
1195 " find the current progress bar position based on precalculated thresholds
1196 while cur_value < cur_val_max && self.cur_value > self.progress_ticks[cur_value]
1197 let cur_value += 1
1198 endwhile
1199
1200 " update progress bar
1201 if self.last_value != cur_value || self.needs_redraw || self.cur_value == self.max_value
1202 let self.needs_redraw = 1
1203 let self.last_value = cur_value
1204
1205 let t_color = self.items.title.color
1206 let b_fcolor = self.items.bar.fillcolor
1207 let b_color = self.items.bar.color
1208 let c_color = self.items.counter.color
1209
1210 let stl = "%#".t_color."#%-( ".self.title." %)".
1211 \"%#".b_color."#".
1212 \(pb_len>0 ?
1213 \ ('|%#'.b_fcolor."#%-(".repeat(" ",cur_value)."%)".
1214 \ '%#'.b_color."#".repeat(" ",pb_len-cur_value)."|"):
1215 \ ('')).
1216 \"%=%#".c_color."#%( ".printf("%3.d ",100*self.cur_value/self.max_value)."%% %)"
1217 call setwinvar(self.winnr, '&stl', stl)
1218 endif
1219 endfun
1220
1221 func! s:progressbar.incr( ... )
1222 let self.cur_value += (a:0 ? a:1 : 1)
1223 " if we were making a general-purpose progress bar, we'd need to limit to a
1224 " lower limit as well, but since we always increment with a positive value
1225 " in this script, we only need limit the upper value
1226 let self.cur_value = (self.cur_value > self.max_value ? self.max_value : self.cur_value)
1227 call self.paint()
1228 endfun
1229 " }}}
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001230 if s:settings.dynamic_folds
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001231 " to process folds we make two passes through each line
1232 let s:pgb = s:ProgressBar("Processing folds:", line('$')*2, s:orgwin)
1233 endif
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001234
1235 call s:SetProgbarColor()
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001236endif
1237
fritzophrenic86cfb392023-09-08 12:20:01 -05001238let s:build_fun_lines = []
1239call add(s:build_fun_lines, [])
1240let s:build_fun_lines[-1] =<< trim ENDLET
1241 func! s:Add_diff_fill(lnum)
1242 let l:filler = diff_filler(a:lnum)
1243 if l:filler > 0
1244 let l:to_insert = l:filler
1245 while l:to_insert > 0
1246 let l:new = repeat(s:difffillchar, 3)
1247
1248 if l:to_insert > 2 && l:to_insert < l:filler && !s:settings.whole_filler
1249 let l:new = l:new .. " " .. l:filler .. " inserted lines "
1250 let l:to_insert = 2
1251 endif
1252ENDLET
1253call add(s:build_fun_lines, [])
1254if !s:settings.no_pre
1255 let s:build_fun_lines[-1] =<< trim ENDLET
1256 " HTML line wrapping is off--go ahead and fill to the margin
1257 " TODO: what about when CSS wrapping is turned on?
1258 let l:new = l:new .. repeat(s:difffillchar, &columns - strlen(l:new) - s:margin)
1259 ENDLET
1260else
1261 let s:build_fun_lines[-1] =<< trim ENDLET
1262 let l:new = l:new .. repeat(s:difffillchar, 3)
1263 ENDLET
1264endif
1265call add(s:build_fun_lines, [])
1266let s:build_fun_lines[-1] =<< trim ENDLET
1267 let l:new = s:HtmlFormat_d(l:new, s:DIFF_D_ID, 0)
1268ENDLET
1269if s:settings.number_lines
1270 call add(s:build_fun_lines, [])
1271 let s:build_fun_lines[-1] =<< trim ENDLET
1272 " Indent if line numbering is on. Indent gets style of line number
1273 " column.
1274 let l:new = s:HtmlFormat_n(repeat(' ', s:margin), s:LINENR_ID, 0, 0) .. l:new
1275 ENDLET
1276endif
1277if s:settings.dynamic_folds && !s:settings.no_foldcolumn
1278 call add(s:build_fun_lines, [])
1279 let s:build_fun_lines[-1] =<< trim ENDLET
1280 if s:foldcolumn > 0
1281 " Indent for foldcolumn if there is one. Assume it's empty, there should
1282 " not be a fold for deleted lines in diff mode.
1283 let l:new = s:FoldColumn_fill() .. l:new
1284 endif
1285 ENDLET
1286endif
1287" Ignore this comment, just bypassing a highlighting issue: if
1288call add(s:build_fun_lines, [])
1289let s:build_fun_lines[-1] =<< trim ENDLET
1290 call add(s:lines, l:new..s:HtmlEndline)
1291 let l:to_insert = l:to_insert - 1
1292 endwhile
1293 endif
1294 endfun
1295ENDLET
1296exec join(flatten(s:build_fun_lines), "\n")
1297
Bram Moolenaar5c736222010-01-06 20:54:52 +01001298" First do some preprocessing for dynamic folding. Do this for the entire file
1299" so we don't accidentally start within a closed fold or something.
1300let s:allfolds = []
1301
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001302if s:settings.dynamic_folds
Bram Moolenaar5c736222010-01-06 20:54:52 +01001303 let s:lnum = 1
1304 let s:end = line('$')
1305 " save the fold text and set it to the default so we can find fold levels
1306 let s:foldtext_save = &foldtext
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001307 setlocal foldtext&
Bram Moolenaar5c736222010-01-06 20:54:52 +01001308
1309 " we will set the foldcolumn in the html to the greater of the maximum fold
1310 " level and the current foldcolumn setting
1311 let s:foldcolumn = &foldcolumn
1312
1313 " get all info needed to describe currently closed folds
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001314 while s:lnum <= s:end
Bram Moolenaar5c736222010-01-06 20:54:52 +01001315 if foldclosed(s:lnum) == s:lnum
1316 " default fold text has '+-' and then a number of dashes equal to fold
1317 " level, so subtract 2 from index of first non-dash after the dashes
1318 " in order to get the fold level of the current fold
1319 let s:level = match(foldtextresult(s:lnum), '+-*\zs[^-]') - 2
Bram Moolenaar5c736222010-01-06 20:54:52 +01001320 " store fold info for later use
1321 let s:newfold = {'firstline': s:lnum, 'lastline': foldclosedend(s:lnum), 'level': s:level,'type': "closed-fold"}
1322 call add(s:allfolds, s:newfold)
1323 " open the fold so we can find any contained folds
fritzophrenic86cfb392023-09-08 12:20:01 -05001324 execute s:lnum.."foldopen"
Bram Moolenaar5c736222010-01-06 20:54:52 +01001325 else
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001326 if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001327 call s:pgb.incr()
1328 if s:pgb.needs_redraw
1329 redrawstatus
1330 let s:pgb.needs_redraw = 0
1331 endif
1332 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +01001333 let s:lnum = s:lnum + 1
1334 endif
1335 endwhile
1336
1337 " close all folds to get info for originally open folds
1338 silent! %foldclose!
1339 let s:lnum = 1
1340
1341 " the originally open folds will be all folds we encounter that aren't
1342 " already in the list of closed folds
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001343 while s:lnum <= s:end
Bram Moolenaar5c736222010-01-06 20:54:52 +01001344 if foldclosed(s:lnum) == s:lnum
1345 " default fold text has '+-' and then a number of dashes equal to fold
1346 " level, so subtract 2 from index of first non-dash after the dashes
1347 " in order to get the fold level of the current fold
1348 let s:level = match(foldtextresult(s:lnum), '+-*\zs[^-]') - 2
Bram Moolenaar5c736222010-01-06 20:54:52 +01001349 let s:newfold = {'firstline': s:lnum, 'lastline': foldclosedend(s:lnum), 'level': s:level,'type': "closed-fold"}
1350 " only add the fold if we don't already have it
1351 if empty(s:allfolds) || index(s:allfolds, s:newfold) == -1
1352 let s:newfold.type = "open-fold"
1353 call add(s:allfolds, s:newfold)
1354 endif
1355 " open the fold so we can find any contained folds
fritzophrenic86cfb392023-09-08 12:20:01 -05001356 execute s:lnum.."foldopen"
Bram Moolenaar5c736222010-01-06 20:54:52 +01001357 else
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001358 if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001359 call s:pgb.incr()
1360 if s:pgb.needs_redraw
1361 redrawstatus
1362 let s:pgb.needs_redraw = 0
1363 endif
1364 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +01001365 let s:lnum = s:lnum + 1
1366 endif
1367 endwhile
1368
1369 " sort the folds so that we only ever need to look at the first item in the
1370 " list of folds
1371 call sort(s:allfolds, "s:FoldCompare")
1372
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001373 let &l:foldtext = s:foldtext_save
Bram Moolenaar5c736222010-01-06 20:54:52 +01001374 unlet s:foldtext_save
1375
1376 " close all folds again so we can get the fold text as we go
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001377 silent! %foldclose!
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001378
Bram Moolenaar251e1912011-06-19 05:09:16 +02001379 " Go through and remove folds we don't need to (or cannot) process in the
1380 " current conversion range
1381 "
1382 " If a fold is removed which contains other folds, which are included, we need
1383 " to adjust the level of the included folds as used by the conversion logic
1384 " (avoiding special cases is good)
1385 "
1386 " Note any time we remove a fold, either all of the included folds are in it,
1387 " or none of them, because we only remove a fold if neither its start nor its
1388 " end are within the conversion range.
1389 let leveladjust = 0
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001390 for afold in s:allfolds
1391 let removed = 0
1392 if exists("g:html_start_line") && exists("g:html_end_line")
1393 if afold.firstline < g:html_start_line
Bram Moolenaar251e1912011-06-19 05:09:16 +02001394 if afold.lastline <= g:html_end_line && afold.lastline >= g:html_start_line
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001395 " if a fold starts before the range to convert but stops within the
1396 " range, we need to include it. Make it start on the first converted
1397 " line.
1398 let afold.firstline = g:html_start_line
1399 else
1400 " if the fold lies outside the range or the start and stop enclose
1401 " the entire range, don't bother parsing it
1402 call remove(s:allfolds, index(s:allfolds, afold))
1403 let removed = 1
Bram Moolenaar251e1912011-06-19 05:09:16 +02001404 if afold.lastline > g:html_end_line
1405 let leveladjust += 1
1406 endif
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001407 endif
1408 elseif afold.firstline > g:html_end_line
1409 " If the entire fold lies outside the range we need to remove it.
1410 call remove(s:allfolds, index(s:allfolds, afold))
1411 let removed = 1
1412 endif
1413 elseif exists("g:html_start_line")
1414 if afold.firstline < g:html_start_line
1415 " if there is no last line, but there is a first line, the end of the
1416 " fold will always lie within the region of interest, so keep it
1417 let afold.firstline = g:html_start_line
1418 endif
1419 elseif exists("g:html_end_line")
1420 " if there is no first line we default to the first line in the buffer so
1421 " the fold start will always be included if the fold itself is included.
1422 " If however the entire fold lies outside the range we need to remove it.
1423 if afold.firstline > g:html_end_line
1424 call remove(s:allfolds, index(s:allfolds, afold))
1425 let removed = 1
1426 endif
1427 endif
1428 if !removed
Bram Moolenaar251e1912011-06-19 05:09:16 +02001429 let afold.level -= leveladjust
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001430 if afold.level+1 > s:foldcolumn
1431 let s:foldcolumn = afold.level+1
1432 endif
1433 endif
1434 endfor
Bram Moolenaar251e1912011-06-19 05:09:16 +02001435
1436 " if we've removed folds containing the conversion range from processing,
1437 " getting foldtext as we go won't know to open the removed folds, so the
1438 " foldtext would be wrong; open them now.
1439 "
1440 " Note that only when a start and an end line is specified will a fold
1441 " containing the current range ever be removed.
1442 while leveladjust > 0
fritzophrenic86cfb392023-09-08 12:20:01 -05001443 exe g:html_start_line.."foldopen"
Bram Moolenaar251e1912011-06-19 05:09:16 +02001444 let leveladjust -= 1
1445 endwhile
Bram Moolenaar5c736222010-01-06 20:54:52 +01001446endif
1447
1448" Now loop over all lines in the original text to convert to html.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001449" Use html_start_line and html_end_line if they are set.
Bram Moolenaarb02cbe32010-07-11 22:38:52 +02001450if exists("g:html_start_line")
Bram Moolenaar071d4272004-06-13 20:20:40 +00001451 let s:lnum = html_start_line
1452 if s:lnum < 1 || s:lnum > line("$")
1453 let s:lnum = 1
1454 endif
1455else
1456 let s:lnum = 1
1457endif
Bram Moolenaarb02cbe32010-07-11 22:38:52 +02001458if exists("g:html_end_line")
Bram Moolenaar071d4272004-06-13 20:20:40 +00001459 let s:end = html_end_line
1460 if s:end < s:lnum || s:end > line("$")
1461 let s:end = line("$")
1462 endif
1463else
1464 let s:end = line("$")
1465endif
1466
Bram Moolenaar5c736222010-01-06 20:54:52 +01001467" stack to keep track of all the folds containing the current line
1468let s:foldstack = []
1469
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001470if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001471 let s:pgb = s:ProgressBar("Processing lines:", s:end - s:lnum + 1, s:orgwin)
1472endif
1473
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001474if s:settings.number_lines
Bram Moolenaar5c736222010-01-06 20:54:52 +01001475 let s:margin = strlen(s:end) + 1
1476else
1477 let s:margin = 0
1478endif
1479
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001480if has('folding') && !s:settings.ignore_folding
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001481 let s:foldfillchar = &fillchars[matchend(&fillchars, 'fold:')]
1482 if s:foldfillchar == ''
1483 let s:foldfillchar = '-'
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001484 endif
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001485endif
1486let s:difffillchar = &fillchars[matchend(&fillchars, 'diff:')]
1487if s:difffillchar == ''
1488 let s:difffillchar = '-'
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001489endif
1490
Bram Moolenaar5c736222010-01-06 20:54:52 +01001491let s:foldId = 0
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001492
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001493if !s:settings.expand_tabs
1494 " If keeping tabs, add them to printable characters so we keep them when
1495 " formatting text (strtrans() doesn't replace printable chars)
1496 let s:old_isprint = &isprint
1497 setlocal isprint+=9
1498endif
1499
Bram Moolenaar071d4272004-06-13 20:20:40 +00001500while s:lnum <= s:end
1501
Bram Moolenaar47136d72004-10-12 20:02:24 +00001502 " If there are filler lines for diff mode, show these above the line.
fritzophrenic86cfb392023-09-08 12:20:01 -05001503 call s:Add_diff_fill(s:lnum)
Bram Moolenaar47136d72004-10-12 20:02:24 +00001504
1505 " Start the line with the line number.
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001506 if s:settings.number_lines
fritzophrenic86cfb392023-09-08 12:20:01 -05001507 let s:numcol = repeat(' ', s:margin - 1 - strlen(s:lnum)) .. s:lnum .. ' '
Bram Moolenaar47136d72004-10-12 20:02:24 +00001508 endif
1509
Bram Moolenaar5c736222010-01-06 20:54:52 +01001510 let s:new = ""
1511
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001512 if has('folding') && !s:settings.ignore_folding && foldclosed(s:lnum) > -1 && !s:settings.dynamic_folds
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001513 "
Bram Moolenaar5c736222010-01-06 20:54:52 +01001514 " This is the beginning of a folded block (with no dynamic folding)
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001515 let s:new = foldtextresult(s:lnum)
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001516 if !s:settings.no_pre
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001517 " HTML line wrapping is off--go ahead and fill to the margin
fritzophrenic86cfb392023-09-08 12:20:01 -05001518 let s:new = s:new .. repeat(s:foldfillchar, &columns - strlen(s:new))
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001519 endif
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00001520
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001521 " put numcol in a separate group for sake of unselectable text
fritzophrenic86cfb392023-09-08 12:20:01 -05001522 let s:new = (s:settings.number_lines ? s:HtmlFormat_n(s:numcol, s:FOLDED_ID, 0, s:lnum): "") .. s:HtmlFormat_t(s:new, s:FOLDED_ID, 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001523
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00001524 " Skip to the end of the fold
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001525 let s:new_lnum = foldclosedend(s:lnum)
1526
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001527 if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001528 call s:pgb.incr(s:new_lnum - s:lnum)
1529 endif
1530
1531 let s:lnum = s:new_lnum
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001532
1533 else
1534 "
Bram Moolenaar5c736222010-01-06 20:54:52 +01001535 " A line that is not folded, or doing dynamic folding.
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001536 "
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001537 let s:line = getline(s:lnum)
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001538 let s:len = strlen(s:line)
1539
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001540 if s:settings.dynamic_folds
Bram Moolenaar5c736222010-01-06 20:54:52 +01001541 " First insert a closing for any open folds that end on this line
1542 while !empty(s:foldstack) && get(s:foldstack,0).lastline == s:lnum-1
fritzophrenic86cfb392023-09-08 12:20:01 -05001543 let s:new = s:new.."</span></span>"
Bram Moolenaar5c736222010-01-06 20:54:52 +01001544 call remove(s:foldstack, 0)
1545 endwhile
1546
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001547 " Now insert an opening for any new folds that start on this line
Bram Moolenaar5c736222010-01-06 20:54:52 +01001548 let s:firstfold = 1
1549 while !empty(s:allfolds) && get(s:allfolds,0).firstline == s:lnum
1550 let s:foldId = s:foldId + 1
fritzophrenic86cfb392023-09-08 12:20:01 -05001551 let s:new ..= "<span id='"
1552 let s:new ..= (exists('g:html_diff_win_num') ? "win"..g:html_diff_win_num : "")
1553 let s:new ..= "fold"..s:foldId..s:settings.id_suffix.."' class='"..s:allfolds[0].type.."'>"
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001554
Bram Moolenaar5c736222010-01-06 20:54:52 +01001555
1556 " Unless disabled, add a fold column for the opening line of a fold.
1557 "
1558 " Note that dynamic folds require using css so we just use css to take
1559 " care of the leading spaces rather than using &nbsp; in the case of
1560 " html_no_pre to make it easier
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001561 if !s:settings.no_foldcolumn
Bram Moolenaar5c736222010-01-06 20:54:52 +01001562 " add fold column that can open the new fold
1563 if s:allfolds[0].level > 1 && s:firstfold
fritzophrenic86cfb392023-09-08 12:20:01 -05001564 let s:new = s:new .. s:FoldColumn_build('|', s:allfolds[0].level - 1, 0, "",
1565 \ 'toggle-open FoldColumn','javascript:toggleFold("fold'..s:foldstack[0].id..s:settings.id_suffix..'");')
Bram Moolenaar5c736222010-01-06 20:54:52 +01001566 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001567 " add the filler spaces separately from the '+' char so that it can be
1568 " shown/hidden separately during a hover unfold
fritzophrenic86cfb392023-09-08 12:20:01 -05001569 let s:new = s:new .. s:FoldColumn_build("+", 1, 0, "",
1570 \ 'toggle-open FoldColumn', 'javascript:toggleFold("fold'..s:foldId..s:settings.id_suffix..'");')
Bram Moolenaar5c736222010-01-06 20:54:52 +01001571 " If this is not the last fold we're opening on this line, we need
1572 " to keep the filler spaces hidden if the fold is opened by mouse
1573 " hover. If it is the last fold to open in the line, we shouldn't hide
1574 " them, so don't apply the toggle-filler class.
fritzophrenic86cfb392023-09-08 12:20:01 -05001575 let s:new = s:new .. s:FoldColumn_build(" ", 1, s:foldcolumn - s:allfolds[0].level - 1, "",
1576 \ 'toggle-open FoldColumn'.. (get(s:allfolds, 1, {'firstline': 0}).firstline == s:lnum ?" toggle-filler" :""),
1577 \ 'javascript:toggleFold("fold'..s:foldId..s:settings.id_suffix..'");')
Bram Moolenaar5c736222010-01-06 20:54:52 +01001578
1579 " add fold column that can close the new fold
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001580 " only add extra blank space if we aren't opening another fold on the
1581 " same line
Bram Moolenaar5c736222010-01-06 20:54:52 +01001582 if get(s:allfolds, 1, {'firstline': 0}).firstline != s:lnum
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001583 let s:extra_space = s:foldcolumn - s:allfolds[0].level
1584 else
1585 let s:extra_space = 0
Bram Moolenaar5c736222010-01-06 20:54:52 +01001586 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001587 if s:firstfold
1588 " the first fold in a line has '|' characters from folds opened in
1589 " previous lines, before the '-' for this fold
fritzophrenic86cfb392023-09-08 12:20:01 -05001590 let s:new ..= s:FoldColumn_build('|', s:allfolds[0].level - 1, s:extra_space, '-',
1591 \ 'toggle-closed FoldColumn', 'javascript:toggleFold("fold'..s:foldId..s:settings.id_suffix..'");')
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001592 else
1593 " any subsequent folds in the line only add a single '-'
fritzophrenic86cfb392023-09-08 12:20:01 -05001594 let s:new = s:new .. s:FoldColumn_build("-", 1, s:extra_space, "",
1595 \ 'toggle-closed FoldColumn', 'javascript:toggleFold("fold'..s:foldId..s:settings.id_suffix..'");')
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001596 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +01001597 let s:firstfold = 0
1598 endif
1599
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001600 " Add fold text, moving the span ending to the next line so collapsing
1601 " of folds works correctly.
1602 " Put numcol in a separate group for sake of unselectable text.
fritzophrenic86cfb392023-09-08 12:20:01 -05001603 let s:new = s:new .. (s:settings.number_lines ? s:HtmlFormat_n(s:numcol, s:FOLDED_ID, 0, 0) : "") .. substitute(s:HtmlFormat_t(foldtextresult(s:lnum), s:FOLDED_ID, 0), '</span>', s:HtmlEndline..'\n\0', '')
1604 let s:new = s:new .. "<span class='fulltext'>"
Bram Moolenaar5c736222010-01-06 20:54:52 +01001605
1606 " open the fold now that we have the fold text to allow retrieval of
1607 " fold text for subsequent folds
fritzophrenic86cfb392023-09-08 12:20:01 -05001608 execute s:lnum.."foldopen"
Bram Moolenaar5c736222010-01-06 20:54:52 +01001609 call insert(s:foldstack, remove(s:allfolds,0))
1610 let s:foldstack[0].id = s:foldId
1611 endwhile
1612
1613 " Unless disabled, add a fold column for other lines.
1614 "
1615 " Note that dynamic folds require using css so we just use css to take
1616 " care of the leading spaces rather than using &nbsp; in the case of
1617 " html_no_pre to make it easier
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001618 if !s:settings.no_foldcolumn
Bram Moolenaar5c736222010-01-06 20:54:52 +01001619 if empty(s:foldstack)
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001620 " add the empty foldcolumn for unfolded lines if there is a fold
1621 " column at all
1622 if s:foldcolumn > 0
fritzophrenic86cfb392023-09-08 12:20:01 -05001623 let s:new = s:new .. s:FoldColumn_fill()
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001624 endif
Bram Moolenaar5c736222010-01-06 20:54:52 +01001625 else
1626 " add the fold column for folds not on the opening line
1627 if get(s:foldstack, 0).firstline < s:lnum
fritzophrenic86cfb392023-09-08 12:20:01 -05001628 let s:new = s:new .. s:FoldColumn_build('|', s:foldstack[0].level, s:foldcolumn - s:foldstack[0].level, "",
1629 \ 'FoldColumn', 'javascript:toggleFold("fold'..s:foldstack[0].id..s:settings.id_suffix..'");')
Bram Moolenaar5c736222010-01-06 20:54:52 +01001630 endif
1631 endif
1632 endif
1633 endif
1634
1635 " Now continue with the unfolded line text
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001636 if s:settings.number_lines
fritzophrenic86cfb392023-09-08 12:20:01 -05001637 let s:new = s:new .. s:HtmlFormat_n(s:numcol, s:LINENR_ID, 0, s:lnum)
Bram Moolenaar31c31672013-06-26 13:28:14 +02001638 elseif s:settings.line_ids
fritzophrenic86cfb392023-09-08 12:20:01 -05001639 let s:new = s:new .. s:HtmlFormat_n("", s:LINENR_ID, 0, s:lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001640 endif
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001641
Bram Moolenaar47136d72004-10-12 20:02:24 +00001642 " Get the diff attribute, if any.
1643 let s:diffattr = diff_hlID(s:lnum, 1)
1644
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001645 " initialize conceal info to act like not concealed, just in case
1646 let s:concealinfo = [0, '']
1647
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001648 " Loop over each character in the line
1649 let s:col = 1
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001650
1651 " most of the time we won't use the diff_id, initialize to zero
1652 let s:diff_id = 0
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001653
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001654 while s:col <= s:len || (s:col == 1 && s:diffattr)
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001655 let s:startcol = s:col " The start column for processing text
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001656 if !s:settings.ignore_conceal && has('conceal')
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001657 let s:concealinfo = synconcealed(s:lnum, s:col)
1658 endif
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001659 if !s:settings.ignore_conceal && s:concealinfo[0]
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001660 let s:col = s:col + 1
1661 " Speed loop (it's small - that's the trick)
1662 " Go along till we find a change in the match sequence number (ending
1663 " the specific concealed region) or until there are no more concealed
1664 " characters.
1665 while s:col <= s:len && s:concealinfo == synconcealed(s:lnum, s:col) | let s:col = s:col + 1 | endwhile
1666 elseif s:diffattr
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001667 let s:diff_id = diff_hlID(s:lnum, s:col)
1668 let s:id = synID(s:lnum, s:col, 1)
Bram Moolenaar47136d72004-10-12 20:02:24 +00001669 let s:col = s:col + 1
1670 " Speed loop (it's small - that's the trick)
1671 " Go along till we find a change in hlID
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001672 while s:col <= s:len && s:id == synID(s:lnum, s:col, 1)
1673 \ && s:diff_id == diff_hlID(s:lnum, s:col) |
1674 \ let s:col = s:col + 1 |
1675 \ endwhile
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001676 if s:len < &columns && !s:settings.no_pre
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001677 " Add spaces at the end of the raw text line to extend the changed
1678 " line to the full width.
fritzophrenic86cfb392023-09-08 12:20:01 -05001679 let s:line = s:line .. repeat(' ', &columns - virtcol([s:lnum, s:len]) - s:margin)
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00001680 let s:len = &columns
1681 endif
Bram Moolenaar47136d72004-10-12 20:02:24 +00001682 else
1683 let s:id = synID(s:lnum, s:col, 1)
1684 let s:col = s:col + 1
1685 " Speed loop (it's small - that's the trick)
1686 " Go along till we find a change in synID
1687 while s:col <= s:len && s:id == synID(s:lnum, s:col, 1) | let s:col = s:col + 1 | endwhile
1688 endif
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001689
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001690 if s:settings.ignore_conceal || !s:concealinfo[0]
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001691 " Expand tabs if needed
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001692 let s:expandedtab = strpart(s:line, s:startcol - 1, s:col - s:startcol)
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001693 if s:settings.expand_tabs
1694 let s:offset = 0
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001695 let s:idx = stridx(s:expandedtab, "\t")
Bram Moolenaarf0d58ef2018-11-16 16:13:44 +01001696 let s:tablist = split(&vts,',')
1697 if empty(s:tablist)
1698 let s:tablist = [ &ts ]
1699 endif
1700 let s:tabidx = 0
1701 let s:tabwidth = 0
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001702 while s:idx >= 0
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001703 if s:startcol + s:idx == 1
1704 let s:i = s:tablist[0]
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001705 else
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001706 " Get the character, which could be multiple bytes, which falls
1707 " immediately before the found tab. Extract it by matching a
1708 " character just prior to the column where the tab matches.
1709 " We'll use this to get the byte index of the character
1710 " immediately preceding the tab, so we can then look up the
1711 " virtual column that character appears in, to determine how
1712 " much of the current tabstop has been used up.
1713 if s:idx == 0
1714 " if the found tab is the first character in the text being
1715 " processed, we need to get the character prior to the text,
1716 " given by startcol.
fritzophrenic86cfb392023-09-08 12:20:01 -05001717 let s:prevc = matchstr(s:line, '.\%' .. (s:startcol + s:offset) .. 'c')
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001718 else
1719 " Otherwise, the byte index of the tab into s:expandedtab is
1720 " given by s:idx.
fritzophrenic86cfb392023-09-08 12:20:01 -05001721 let s:prevc = matchstr(s:expandedtab, '.\%' .. (s:idx + 1) .. 'c')
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001722 endif
1723 let s:vcol = virtcol([s:lnum, s:startcol + s:idx + s:offset - len(s:prevc)])
1724
1725 " find the tabstop interval to use for the tab we just found. Keep
1726 " adding tabstops (which could be variable) until we would exceed
1727 " the virtual screen position of the start of the found tab.
1728 while s:vcol >= s:tabwidth + s:tablist[s:tabidx]
1729 let s:tabwidth += s:tablist[s:tabidx]
1730 if s:tabidx < len(s:tablist)-1
1731 let s:tabidx = s:tabidx+1
1732 endif
1733 endwhile
1734 let s:i = s:tablist[s:tabidx] - (s:vcol - s:tabwidth)
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001735 endif
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001736 " update offset to keep the index within the line corresponding to
1737 " actual tab characters instead of replaced spaces; s:idx reflects
1738 " replaced spaces in s:expandedtab, s:offset cancels out all but
1739 " the tab character itself.
1740 let s:offset -= s:i - 1
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001741 let s:expandedtab = substitute(s:expandedtab, '\t', repeat(' ', s:i), '')
1742 let s:idx = stridx(s:expandedtab, "\t")
1743 endwhile
1744 end
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00001745
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001746 " get the highlight group name to use
1747 let s:id = synIDtrans(s:id)
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001748 else
1749 " use Conceal highlighting for concealed text
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001750 let s:id = s:CONCEAL_ID
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001751 let s:expandedtab = s:concealinfo[1]
1752 endif
1753
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001754 " Output the text with the same synID, with class set to the highlight ID
1755 " name, unless it has been concealed completely.
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001756 if strlen(s:expandedtab) > 0
fritzophrenic86cfb392023-09-08 12:20:01 -05001757 let s:new = s:new .. s:HtmlFormat(s:expandedtab, s:id, s:diff_id, "", 0)
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001758 endif
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001759 endwhile
1760 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001761
fritzophrenic86cfb392023-09-08 12:20:01 -05001762 call extend(s:lines, split(s:new..s:HtmlEndline, '\n', 1))
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001763 if !s:settings.no_progress && s:pgb.needs_redraw
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001764 redrawstatus
1765 let s:pgb.needs_redraw = 0
1766 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001767 let s:lnum = s:lnum + 1
Bram Moolenaar313b7232007-05-05 17:56:55 +00001768
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001769 if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001770 call s:pgb.incr()
1771 endif
1772endwhile
1773
fritzophrenic86cfb392023-09-08 12:20:01 -05001774" Diff filler is returned based on what needs inserting *before* the given line.
1775" So to get diff filler at the end of the buffer, we need to use last line + 1
1776call s:Add_diff_fill(s:end+1)
1777
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001778if s:settings.dynamic_folds
Bram Moolenaar5c736222010-01-06 20:54:52 +01001779 " finish off any open folds
1780 while !empty(s:foldstack)
fritzophrenic86cfb392023-09-08 12:20:01 -05001781 let s:lines[-1]..="</span></span>"
Bram Moolenaar5c736222010-01-06 20:54:52 +01001782 call remove(s:foldstack, 0)
1783 endwhile
1784
1785 " add fold column to the style list if not already there
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001786 let s:id = s:FOLD_C_ID
1787 if !has_key(s:stylelist, s:id)
fritzophrenic86cfb392023-09-08 12:20:01 -05001788 let s:stylelist[s:id] = '.FoldColumn { ' .. s:CSS1(s:id) .. '}'
Bram Moolenaar5c736222010-01-06 20:54:52 +01001789 endif
1790endif
1791
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001792if s:settings.no_pre
1793 if !s:settings.use_css
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001794 " Close off the font tag that encapsulates the whole <body>
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +01001795 call extend(s:lines, ["</font>"])
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001796 else
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +01001797 call extend(s:lines, ["</div>"])
Bram Moolenaar8ada2cc2010-07-29 20:43:36 +02001798 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001799else
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +01001800 call extend(s:lines, ["</pre>"])
1801endif
1802if !s:settings.no_doc
1803 call extend(s:lines, ["</body>", "</html>"])
Bram Moolenaar071d4272004-06-13 20:20:40 +00001804endif
1805
fritzophrenic86cfb392023-09-08 12:20:01 -05001806exe s:newwin .. "wincmd w"
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02001807call setline(1, s:lines)
1808unlet s:lines
Bram Moolenaar071d4272004-06-13 20:20:40 +00001809
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001810" Mangle modelines so Vim doesn't try to use HTML text as a modeline if editing
1811" this file in the future; need to do this after generating all the text in case
1812" the modeline text has different highlight groups which all turn out to be
1813" stripped from the final output.
Bram Moolenaardd007ed2013-07-09 15:44:17 +02001814%s!\v(%(^|\s+)%([Vv]i%(m%([<=>]?\d+)?)?|ex)):!\1\&#0058;!ge
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001815
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001816" The generated HTML is admittedly ugly and takes a LONG time to fold.
1817" Make sure the user doesn't do syntax folding when loading a generated file,
1818" using a modeline.
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +01001819if !s:settings.no_modeline
1820 call append(line('$'), "<!-- vim: set foldmethod=manual : -->")
Bram Moolenaar071d4272004-06-13 20:20:40 +00001821endif
1822
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +01001823" Now, when we finally know which, we define the colors and styles
1824if s:settings.use_css && !s:settings.no_doc
1825 1;/<style\>/+1
1826
1827 " Normal/global attributes
Bram Moolenaar076e8b22010-08-05 21:54:00 +02001828 if s:settings.no_pre
fritzophrenic86cfb392023-09-08 12:20:01 -05001829 call append('.', "body { color: " .. s:fgc .. "; background-color: " .. s:bgc .. "; font-family: ".. s:htmlfont .."; }")
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001830 +
Bram Moolenaar071d4272004-06-13 20:20:40 +00001831 else
fritzophrenic86cfb392023-09-08 12:20:01 -05001832 call append('.', "pre { " .. s:whitespace .. "font-family: ".. s:htmlfont .."; color: " .. s:fgc .. "; background-color: " .. s:bgc .. "; }")
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001833 +
Bram Moolenaar071d4272004-06-13 20:20:40 +00001834 yank
1835 put
1836 execute "normal! ^cwbody\e"
Bram Moolenaar8e5af3e2011-04-28 19:02:44 +02001837 " body should not have the wrap formatting, only the pre section
1838 if s:whitespace != ''
fritzophrenic86cfb392023-09-08 12:20:01 -05001839 exec 's#'..s:whitespace
Bram Moolenaar8e5af3e2011-04-28 19:02:44 +02001840 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001841 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001842 " fix browser inconsistencies (sometimes within the same browser) of different
1843 " default font size for different elements
1844 call append('.', '* { font-size: 1em; }')
1845 +
1846 " if we use any input elements for unselectable content, make sure they look
1847 " like normal text
1848 if !empty(s:settings.prevent_copy)
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001849 if s:settings.use_input_for_pc !=# "none"
fritzophrenic86cfb392023-09-08 12:20:01 -05001850 call append('.', 'input { border: none; margin: 0; padding: 0; font-family: '..s:htmlfont..'; }')
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001851 +
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001852 " ch units for browsers which support them, em units for a somewhat
1853 " reasonable fallback.
1854 for w in range(1, 20, 1)
1855 call append('.', [
fritzophrenic86cfb392023-09-08 12:20:01 -05001856 \ "input[size='"..w.."'] { width: "..w.."em; width: "..w.."ch; }"
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001857 \ ])
1858 +
1859 endfor
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001860 endif
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001861
1862 if s:settings.use_input_for_pc !=# 'all'
1863 let s:unselectable_styles = []
1864 if s:settings.prevent_copy =~# 'f'
1865 call add(s:unselectable_styles, 'FoldColumn')
1866 endif
1867 if s:settings.prevent_copy =~# 'n'
1868 call add(s:unselectable_styles, 'LineNr')
1869 endif
1870 if s:settings.prevent_copy =~# 't' && !s:settings.ignore_folding
1871 call add(s:unselectable_styles, 'Folded')
1872 endif
1873 if s:settings.prevent_copy =~# 'd'
1874 call add(s:unselectable_styles, 'DiffDelete')
1875 endif
1876 if s:settings.use_input_for_pc !=# 'none'
1877 call append('.', [
1878 \ '/* Note: IE does not support @supports conditionals, but also does not fully support',
1879 \ ' "content:" with custom content, so we *want* the check to fail */',
1880 \ '@supports ( content: attr(data-custom-content) ) {'
1881 \ ])
1882 +3
1883 endif
1884 " The line number column inside the foldtext is styled just like the fold
1885 " text in Vim, but it should use the prevent_copy settings of line number
1886 " rather than fold text. Apply the prevent_copy styles to foldtext
1887 " specifically for line numbers, which always come after the fold column,
1888 " or at the beginning of the line.
1889 if s:settings.prevent_copy =~# 'n' && !s:settings.ignore_folding
1890 call append('.', [
1891 \ ' .FoldColumn + .Folded, .Folded:first-child { user-select: none; }',
1892 \ ' .FoldColumn + [data-Folded-content]::before, [data-Folded-content]:first-child::before { content: attr(data-Folded-content); }',
1893 \ ' .FoldColumn + [data-Folded-content]::before, [data-Folded-content]:first-child::before { padding-bottom: 1px; display: inline-block; /* match the 1-px padding of standard items with background */ }',
1894 \ ' .FoldColumn + span[data-Folded-content]::before, [data-Folded-content]:first-child::before { cursor: default; }',
1895 \ ])
1896 +4
1897 endif
1898 for s:style_name in s:unselectable_styles
1899 call append('.', [
fritzophrenic86cfb392023-09-08 12:20:01 -05001900 \ ' .'..s:style_name..' { user-select: none; }',
1901 \ ' [data-'..s:style_name..'-content]::before { content: attr(data-'..s:style_name..'-content); }',
1902 \ ' [data-'..s:style_name..'-content]::before { padding-bottom: 1px; display: inline-block; /* match the 1-px padding of standard items with background */ }',
1903 \ ' span[data-'..s:style_name..'-content]::before { cursor: default; }',
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001904 \ ])
1905 +4
1906 endfor
1907 if s:settings.use_input_for_pc !=# 'none'
fritzophrenic86cfb392023-09-08 12:20:01 -05001908 " Note, the extra '}' is to match the "@supports" above
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001909 call append('.', [
1910 \ ' input { display: none; }',
1911 \ '}'
1912 \ ])
1913 +2
1914 endif
1915 unlet s:unselectable_styles
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001916 endif
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001917
1918 " Fix mouse cursor shape for the fallback <input> method of uncopyable text
1919 if s:settings.use_input_for_pc !=# 'none'
1920 if s:settings.prevent_copy =~# 'f'
1921 " Make the cursor show active fold columns as active areas, and empty fold
1922 " columns as not interactive.
1923 call append('.', ['input.FoldColumn { cursor: pointer; }',
fritzophrenic86cfb392023-09-08 12:20:01 -05001924 \ 'input.FoldColumn[value="'..repeat(' ', s:foldcolumn)..'"] { cursor: default; }'
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001925 \ ])
1926 +2
1927 if s:settings.use_input_for_pc !=# 'all'
1928 call append('.', [
fritzophrenic86cfb392023-09-08 12:20:01 -05001929 \ 'a[data-FoldColumn-content="'..repeat(' ', s:foldcolumn)..'"] { cursor: default; }'
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001930 \ ])
1931 +1
1932 end
1933 endif
1934 " make line number column show as non-interactive if not selectable
1935 if s:settings.prevent_copy =~# 'n'
1936 call append('.', 'input.LineNr { cursor: default; }')
1937 +
1938 endif
1939 " make fold text and line number column within fold text show as
1940 " non-interactive if not selectable
1941 if (s:settings.prevent_copy =~# 'n' || s:settings.prevent_copy =~# 't') && !s:settings.ignore_folding
1942 call append('.', 'input.Folded { cursor: default; }')
1943 +
1944 endif
1945 " make diff filler show as non-interactive if not selectable
1946 if s:settings.prevent_copy =~# 'd'
1947 call append('.', 'input.DiffDelete { cursor: default; }')
1948 +
1949 endif
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001950 endif
1951 endif
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +01001952endif
1953
Bram Moolenaarf1dcd142022-12-31 15:30:45 +00001954if !s:settings.use_css && !s:settings.no_doc
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01001955 " For Netscape 4, set <body> attributes too, though, strictly speaking, it's
1956 " incorrect.
fritzophrenic86cfb392023-09-08 12:20:01 -05001957 execute '%s:<body\([^>]*\):<body bgcolor="' .. s:bgc .. '" text="' .. s:fgc .. '"\1>\r<font face="'.. s:htmlfont ..'"'
Bram Moolenaar071d4272004-06-13 20:20:40 +00001958endif
1959
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001960" Gather attributes for all other classes. Do diff first so that normal
1961" highlight groups are inserted before it.
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +01001962if s:settings.use_css && !s:settings.no_doc
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02001963 if s:diff_mode
1964 call append('.', filter(map(keys(s:diffstylelist), "s:diffstylelist[v:val]"), 'v:val != ""'))
1965 endif
1966 if !empty(s:stylelist)
1967 call append('.', filter(map(keys(s:stylelist), "s:stylelist[v:val]"), 'v:val != ""'))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001968 endif
1969endif
1970
Bram Moolenaar071d4272004-06-13 20:20:40 +00001971" Add hyperlinks
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +01001972if !s:settings.no_links
1973 %s+\(https\=://\S\{-}\)\(\([.,;:}]\=\(\s\|$\)\)\|[\\"'<>]\|&gt;\|&lt;\|&quot;\)+<a href="\1">\1</a>\2+ge
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001974endif
1975
Bram Moolenaar6ebe4f92022-10-28 20:47:54 +01001976" The DTD
1977if !s:settings.no_doc
1978 if s:settings.use_xhtml
1979 exe "normal! gg$a\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
1980 elseif s:html5
1981 exe "normal! gg0i<!DOCTYPE html>\n"
1982 else
1983 exe "normal! gg0i<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"
1984 endif
1985endif
1986
1987if s:settings.use_xhtml && !s:settings.no_doc
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001988 exe "normal! gg/<html/e\na xmlns=\"http://www.w3.org/1999/xhtml\"\e"
Bram Moolenaar071d4272004-06-13 20:20:40 +00001989endif
1990
1991" Cleanup
1992%s:\s\+$::e
1993
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01001994" Restore old settings (new window first)
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02001995"
1996" Don't bother restoring foldmethod in case it was syntax because the markup is
1997" so weirdly formatted it can take a LONG time.
Bram Moolenaar8df7f882010-08-13 11:30:02 +02001998let &l:foldenable = s:old_fen
Bram Moolenaar071d4272004-06-13 20:20:40 +00001999let &report = s:old_report
2000let &title = s:old_title
2001let &icon = s:old_icon
2002let &paste = s:old_paste
2003let &magic = s:old_magic
2004let @/ = s:old_search
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02002005let &more = s:old_more
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01002006
2007" switch to original window to restore those settings
fritzophrenic86cfb392023-09-08 12:20:01 -05002008exe s:orgwin .. "wincmd w"
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01002009
2010if !s:settings.expand_tabs
2011 let &l:isprint = s:old_isprint
2012endif
Bram Moolenaar166af9b2010-11-16 20:34:40 +01002013let &l:stl = s:origwin_stl
Bram Moolenaar071d4272004-06-13 20:20:40 +00002014let &l:et = s:old_et
Bram Moolenaar8df7f882010-08-13 11:30:02 +02002015let &l:scrollbind = s:old_bind
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01002016
2017" and back to the new window again to end there
fritzophrenic86cfb392023-09-08 12:20:01 -05002018exe s:newwin .. "wincmd w"
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01002019
Bram Moolenaar166af9b2010-11-16 20:34:40 +01002020let &l:stl = s:newwin_stl
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02002021exec 'resize' s:old_winheight
2022let &l:winfixheight = s:old_winfixheight
Bram Moolenaar071d4272004-06-13 20:20:40 +00002023
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02002024let &ls=s:ls
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01002025let &eventignore=s:ei_sav
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02002026
Bram Moolenaar071d4272004-06-13 20:20:40 +00002027" Save a little bit of memory (worth doing?)
Bram Moolenaar8e5af3e2011-04-28 19:02:44 +02002028unlet s:htmlfont s:whitespace
Bram Moolenaar8df7f882010-08-13 11:30:02 +02002029unlet s:old_et s:old_paste s:old_icon s:old_report s:old_title s:old_search
Bram Moolenaar543b7ef2013-06-01 14:50:56 +02002030unlet s:old_magic s:old_more s:old_fen s:old_winheight
Bram Moolenaar2a8a3ec2011-01-08 16:06:37 +01002031unlet! s:old_isprint
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02002032unlet s:whatterm s:stylelist s:diffstylelist s:lnum s:end s:margin s:fgc s:bgc s:old_winfixheight
2033unlet! s:col s:id s:attr s:len s:line s:new s:expandedtab s:concealinfo s:diff_mode
Bram Moolenaar0c0734d2019-11-26 21:44:46 +01002034unlet! s:orgwin s:newwin s:orgbufnr s:idx s:i s:offset s:ls s:ei_sav s:origwin_stl
Bram Moolenaar8df7f882010-08-13 11:30:02 +02002035unlet! s:newwin_stl s:current_syntax
Bram Moolenaar05159a02005-02-26 23:04:13 +00002036if !v:profiling
2037 delfunc s:HtmlColor
2038 delfunc s:HtmlFormat
2039 delfunc s:CSS1
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02002040 delfunc s:BuildStyleWrapper
Bram Moolenaar076e8b22010-08-05 21:54:00 +02002041 if !s:settings.use_css
Bram Moolenaar05159a02005-02-26 23:04:13 +00002042 delfunc s:HtmlOpening
2043 delfunc s:HtmlClosing
2044 endif
Bram Moolenaar076e8b22010-08-05 21:54:00 +02002045 if s:settings.dynamic_folds
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02002046 delfunc s:FoldCompare
2047 endif
2048
Bram Moolenaar076e8b22010-08-05 21:54:00 +02002049 if !s:settings.no_progress
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02002050 delfunc s:ProgressBar
2051 delfunc s:progressbar.paint
2052 delfunc s:progressbar.incr
2053 unlet s:pgb s:progressbar
2054 endif
fritzophrenic86cfb392023-09-08 12:20:01 -05002055
2056 delfunc s:Add_diff_fill
Bram Moolenaar071d4272004-06-13 20:20:40 +00002057endif
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02002058
fritzophrenic86cfb392023-09-08 12:20:01 -05002059unlet! s:new_lnum s:diffattr s:difffillchar s:foldfillchar s:HtmlSpace s:diffstyle
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02002060unlet! s:LeadingSpace s:HtmlEndline s:firstfold s:numcol s:foldcolumn
fritzophrenic86cfb392023-09-08 12:20:01 -05002061unlet! s:wrapperfunc_lines s:build_fun_lines
Bram Moolenaar6c35bea2012-07-25 17:49:10 +02002062unlet s:foldstack s:allfolds s:foldId s:settings
Bram Moolenaar5c736222010-01-06 20:54:52 +01002063
2064let &cpo = s:cpo_sav
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02002065unlet! s:cpo_sav
Bram Moolenaar5c736222010-01-06 20:54:52 +01002066
Bram Moolenaar349b2fb2010-07-16 20:35:36 +02002067" Make sure any patches will probably use consistent indent
Bram Moolenaar7c86f4c2010-07-18 14:07:22 +02002068" vim: ts=8 sw=2 sts=2 noet