1<xsl:stylesheet version="1.0" 2xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 3xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 4xmlns:dc="http://purl.org/dc/elements/1.1/" 5xmlns:dcq="http://purl.org/dc/qualifiers/1.0/" 6xmlns:fo="http://www.w3.org/1999/XSL/Format" 7xmlns:fn="http://www.w3.org/2005/xpath-functions"> 8 <xsl:output method="html"/> 9 <!-- Set to 1 to show explanations by default. Set to 0 to hide them --> 10 <xsl:variable name="show_explanation_default" select="0" /> 11 <!-- The characters within the Webdings font that show the triangles --> 12 <xsl:variable name="show_button_text" select="'▶'" /> 13 <xsl:variable name="hide_button_text" select="'▽'" /> 14 <!-- The suffix for names --> 15 <xsl:variable name="button_suffix" select="'__button'"/> 16 <xsl:variable name="body_suffix" select="'__body'"/> 17 <!-- For easy reference, the name of the button --> 18 <xsl:variable name="show_hide_all_button" select="'show_hide_all_button'"/> 19 20 <!-- The top-level element --> 21 <xsl:template match="GUIDE"> 22 <HTML> 23 <HEAD> 24 <TITLE><xsl:value-of select="@title"/></TITLE> 25 <META http-equiv="Content-Type" content="text/html; charset=utf-8"/> 26 <LINK HREF="https://www.google.com/favicon.ico" type="image/x-icon" 27 rel="shortcut icon"/> 28 <LINK HREF="styleguide.css" 29 type="text/css" rel="stylesheet"/> 30 31 <SCRIPT language="javascript" type="text/javascript"> 32 33 function GetElementsByName(name) { 34 // Workaround a bug on old versions of opera. 35 if (document.getElementsByName) { 36 return document.getElementsByName(name); 37 } else { 38 return [document.getElementById(name)]; 39 } 40 } 41 42 /** 43 * @param {string} namePrefix The prefix of the body name. 44 * @param {function(boolean): boolean} getVisibility Computes the new 45 * visibility state, given the current one. 46 */ 47 function ChangeVisibility(namePrefix, getVisibility) { 48 var bodyName = namePrefix + '<xsl:value-of select="$body_suffix"/>'; 49 var buttonName = namePrefix + '<xsl:value-of select="$button_suffix"/>'; 50 var bodyElements = GetElementsByName(bodyName); 51 var linkElement = GetElementsByName('link-' + buttonName)[0]; 52 if (bodyElements.length != 1) { 53 throw Error('ShowHideByName() got the wrong number of bodyElements: ' + 54 bodyElements.length); 55 } else { 56 var bodyElement = bodyElements[0]; 57 var buttonElement = GetElementsByName(buttonName)[0]; 58 var isVisible = bodyElement.style.display != "none"; 59 if (getVisibility(isVisible)) { 60 bodyElement.style.display = "inline"; 61 linkElement.style.display = "block"; 62 buttonElement.innerHTML = '<xsl:value-of select="$hide_button_text"/>'; 63 } else { 64 bodyElement.style.display = "none"; 65 linkElement.style.display = "none"; 66 buttonElement.innerHTML = '<xsl:value-of select="$show_button_text"/>'; 67 } 68 } 69 } 70 71 function ShowHideByName(namePrefix) { 72 ChangeVisibility(namePrefix, function(old) { return !old; }); 73 } 74 75 function ShowByName(namePrefix) { 76 ChangeVisibility(namePrefix, function() { return true; }); 77 } 78 79 function ShowHideAll() { 80 var allButton = GetElementsByName("show_hide_all_button")[0]; 81 if (allButton.innerHTML == '<xsl:value-of select="$hide_button_text"/>') { 82 allButton.innerHTML = '<xsl:value-of select="$show_button_text"/>'; 83 SetHiddenState(document.getElementsByTagName("body")[0].childNodes, "none", '<xsl:value-of select="$show_button_text"/>'); 84 } else { 85 allButton.innerHTML = '<xsl:value-of select="$hide_button_text"/>'; 86 SetHiddenState(document.getElementsByTagName("body")[0].childNodes, "inline", '<xsl:value-of select="$hide_button_text"/>'); 87 } 88 } 89 90 // Recursively sets state of all children 91 // of a particular node. 92 function SetHiddenState(root, newState, newButton) { 93 for (var i = 0; i != root.length; i++) { 94 SetHiddenState(root[i].childNodes, newState, newButton); 95 if (root[i].className == 'showhide_button') { 96 root[i].innerHTML = newButton; 97 } 98 if (root[i].className == 'stylepoint_body' || 99 root[i].className == 'link_button') { 100 root[i].style.display = newState; 101 } 102 } 103 } 104 105 106 function EndsWith(str, suffix) { 107 var l = str.length - suffix.length; 108 return l >= 0 && str.indexOf(suffix, l) == l; 109 } 110 111 function RefreshVisibilityFromHashParam() { 112 var hashRegexp = new RegExp('#([^&#]*)$'); 113 var hashMatch = hashRegexp.exec(window.location.href); 114 var anchor = hashMatch && GetElementsByName(hashMatch[1])[0]; 115 var node = anchor; 116 var suffix = '<xsl:value-of select="$body_suffix"/>'; 117 while (node) { 118 var id = node.id; 119 var matched = id && EndsWith(id, suffix); 120 if (matched) { 121 var len = id.length - suffix.length; 122 ShowByName(id.substring(0, len)); 123 if (anchor.scrollIntoView) { 124 anchor.scrollIntoView(); 125 } 126 127 return; 128 } 129 node = node.parentNode; 130 } 131 } 132 133 window.onhashchange = RefreshVisibilityFromHashParam; 134 135 window.onload = function() { 136 // if the URL contains "?showall=y", expand the details of all children 137 var showHideAllRegex = new RegExp("[\\?&](showall)=([^&#]*)"); 138 var showHideAllValue = showHideAllRegex.exec(window.location.href); 139 if (showHideAllValue != null) { 140 if (showHideAllValue[2] == "y") { 141 SetHiddenState(document.getElementsByTagName("body")[0].childNodes, 142 "inline", '<xsl:value-of select="$hide_button_text"/>'); 143 } else { 144 SetHiddenState(document.getElementsByTagName("body")[0].childNodes, 145 "none", '<xsl:value-of select="$show_button_text"/>'); 146 } 147 } 148 var showOneRegex = new RegExp("[\\?&](showone)=([^&#]*)"); 149 var showOneValue = showOneRegex.exec(window.location.href); 150 if (showOneValue) { 151 ShowHideByName(showOneValue[2]); 152 } 153 154 155 RefreshVisibilityFromHashParam(); 156 } 157 </SCRIPT> 158 </HEAD> 159 <BODY> 160 <H1><xsl:value-of select="@title"/></H1> 161 <xsl:apply-templates/> 162 </BODY> 163 </HTML> 164 </xsl:template> 165 166 <xsl:template match="OVERVIEW"> 167 <xsl:variable name="button_text"> 168 <xsl:choose> 169 <xsl:when test="$show_explanation_default"> 170 <xsl:value-of select="$hide_button_text"/> 171 </xsl:when> 172 <xsl:otherwise> 173 <xsl:value-of select="$show_button_text"/> 174 </xsl:otherwise> 175 </xsl:choose> 176 </xsl:variable> 177 <DIV style="margin-left: 50%; font-size: 75%;"> 178 <P> 179 Each style point has a summary for which additional information is available 180 by toggling the accompanying arrow button that looks this way: 181 <SPAN class="showhide_button" style="margin-left: 0; float: none"> 182 <xsl:value-of select="$show_button_text"/></SPAN>. 183 You may toggle all summaries with the big arrow button: 184 </P> 185 <DIV style=" font-size: larger; margin-left: +2em;"> 186 <SPAN class="showhide_button" style="font-size: 180%; float: none"> 187 <xsl:attribute name="onclick"><xsl:value-of select="'javascript:ShowHideAll()'"/></xsl:attribute> 188 <xsl:attribute name="name"><xsl:value-of select="$show_hide_all_button"/></xsl:attribute> 189 <xsl:attribute name="id"><xsl:value-of select="$show_hide_all_button"/></xsl:attribute> 190 <xsl:value-of select="$button_text"/> 191 </SPAN> 192 Toggle all summaries 193 </DIV> 194 </DIV> 195 <xsl:call-template name="TOC"> 196 <xsl:with-param name="root" select=".."/> 197 </xsl:call-template> 198 <xsl:apply-templates/> 199 </xsl:template> 200 201 <xsl:template match="PARTING_WORDS"> 202 <H2>Parting Words</H2> 203 <xsl:apply-templates/> 204 </xsl:template> 205 206 <xsl:template match="CATEGORY"> 207 <DIV> 208 <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute> 209 <H2> 210 <xsl:variable name="category_name"> 211 <xsl:call-template name="anchorname"> 212 <xsl:with-param name="sectionname" select="@title"/> 213 </xsl:call-template> 214 </xsl:variable> 215 <xsl:attribute name="name"><xsl:value-of select="$category_name"/></xsl:attribute> 216 <xsl:attribute name="id"><xsl:value-of select="$category_name"/></xsl:attribute> 217 <xsl:value-of select="@title"/> 218 </H2> 219 <xsl:apply-templates/> 220 </DIV> 221 </xsl:template> 222 223 <xsl:template match="STYLEPOINT"> 224 <DIV> 225 <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute> 226 <xsl:variable name="stylepoint_name"> 227 <xsl:call-template name="anchorname"> 228 <xsl:with-param name="sectionname" select="@title"/> 229 </xsl:call-template> 230 </xsl:variable> 231 <xsl:variable name="button_text"> 232 <xsl:choose> 233 <xsl:when test="$show_explanation_default"> 234 <xsl:value-of select="$hide_button_text"/> 235 </xsl:when> 236 <xsl:otherwise> 237 <xsl:value-of select="$show_button_text"/> 238 </xsl:otherwise> 239 </xsl:choose> 240 </xsl:variable> 241 <H3> 242 <A> 243 <xsl:attribute name="name"><xsl:value-of select="$stylepoint_name"/></xsl:attribute> 244 <xsl:attribute name="id"><xsl:value-of select="$stylepoint_name"/></xsl:attribute> 245 <xsl:value-of select="@title"/> 246 </A> 247 </H3> 248 <xsl:variable name="buttonName"> 249 <xsl:value-of select="$stylepoint_name"/><xsl:value-of select="$button_suffix"/> 250 </xsl:variable> 251 <xsl:variable name="onclick_definition"> 252 <xsl:text>javascript:ShowHideByName('</xsl:text> 253 <xsl:value-of select="$stylepoint_name"/> 254 <xsl:text>')</xsl:text> 255 </xsl:variable> 256 <SPAN class="link_button" id="link-{$buttonName}" name="link-{$buttonName}"> 257 <A> 258 <xsl:attribute name="href">?showone=<xsl:value-of select="$stylepoint_name"/>#<xsl:value-of select="$stylepoint_name"/></xsl:attribute> 259 link 260 </A> 261 </SPAN> 262 <SPAN class="showhide_button"> 263 <xsl:attribute name="onclick"><xsl:value-of select="$onclick_definition"/></xsl:attribute> 264 <xsl:attribute name="name"><xsl:value-of select="$buttonName"/></xsl:attribute> 265 <xsl:attribute name="id"><xsl:value-of select="$buttonName"/></xsl:attribute> 266 <xsl:value-of select="$button_text"/> 267 </SPAN> 268 <xsl:apply-templates> 269 <xsl:with-param name="anchor_prefix" select="$stylepoint_name" /> 270 </xsl:apply-templates> 271 </DIV> 272 </xsl:template> 273 274 <xsl:template match="SUMMARY"> 275 <xsl:param name="anchor_prefix" /> 276 <DIV style="display:inline;"> 277 <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute> 278 <xsl:apply-templates/> 279 </DIV> 280 </xsl:template> 281 282 <xsl:template match="BODY"> 283 <xsl:param name="anchor_prefix" /> 284 <DIV> 285 <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute> 286 <DIV class="stylepoint_body"> 287 <xsl:attribute name="name"><xsl:value-of select="$anchor_prefix"/><xsl:value-of select="$body_suffix"/></xsl:attribute> 288 <xsl:attribute name="id"><xsl:value-of select="$anchor_prefix"/><xsl:value-of select="$body_suffix"/></xsl:attribute> 289 <xsl:attribute name="style"> 290 <xsl:choose> 291 <xsl:when test="$show_explanation_default">display: inline</xsl:when> 292 <xsl:otherwise>display: none</xsl:otherwise> 293 </xsl:choose> 294 </xsl:attribute> 295 <xsl:apply-templates/> 296 </DIV> 297 </DIV> 298 </xsl:template> 299 300 <xsl:template match="DEFINITION"> 301 <P> 302 <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute> 303 <SPAN class="stylepoint_section">Definition: </SPAN> 304 <xsl:apply-templates/> 305 </P> 306 </xsl:template> 307 308 <xsl:template match="PROS"> 309 <P> 310 <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute> 311 <SPAN class="stylepoint_section">Pros: </SPAN> 312 <xsl:apply-templates/> 313 </P> 314 </xsl:template> 315 316 <xsl:template match="CONS"> 317 <P> 318 <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute> 319 <SPAN class="stylepoint_section">Cons: </SPAN> 320 <xsl:apply-templates/> 321 </P> 322 </xsl:template> 323 324 <xsl:template match="DECISION"> 325 <P> 326 <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute> 327 <SPAN class="stylepoint_section">Decision: </SPAN> 328 <xsl:apply-templates/> 329 </P> 330 </xsl:template> 331 332 <xsl:template match="TODO"> 333 <P> 334 <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute> 335 <DIV style="font-size: 150%;">TODO: 336 <xsl:apply-templates/> 337 </DIV> 338 </P> 339 </xsl:template> 340 341 <xsl:template match="SUBSECTION"> 342 <P> 343 <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute> 344 <SPAN class="stylepoint_subsection"><xsl:value-of select="@title"/> </SPAN> 345 <xsl:apply-templates/> 346 </P> 347 </xsl:template> 348 349 <xsl:template match="SUBSUBSECTION"> 350 <P> 351 <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute> 352 <SPAN class="stylepoint_subsubsection"><xsl:value-of select="@title"/> </SPAN> 353 <xsl:apply-templates/> 354 </P> 355 </xsl:template> 356 357 <xsl:template match="CODE_SNIPPET"> 358 <DIV> 359 <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute> 360 <PRE><xsl:call-template name="print_without_leading_chars"> 361 <xsl:with-param name="text" select="."/> 362 <xsl:with-param name="strip" select="1"/> 363 <xsl:with-param name="is_firstline" select="1"/> 364 <xsl:with-param name="trim_count"> 365 <xsl:call-template name="num_leading_spaces"> 366 <xsl:with-param name="text" select="."/> 367 <xsl:with-param name="max_so_far" select="1000"/> 368 </xsl:call-template> 369 </xsl:with-param> 370 </xsl:call-template></PRE> 371 </DIV> 372 </xsl:template> 373 374 <xsl:template match="BAD_CODE_SNIPPET"> 375 <DIV> 376 <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute> 377 <PRE class="badcode"><xsl:call-template name="print_without_leading_chars"> 378 <xsl:with-param name="text" select="."/> 379 <xsl:with-param name="strip" select="1"/> 380 <xsl:with-param name="is_firstline" select="1"/> 381 <xsl:with-param name="trim_count"> 382 <xsl:call-template name="num_leading_spaces"> 383 <xsl:with-param name="text" select="."/> 384 <xsl:with-param name="max_so_far" select="1000"/> 385 </xsl:call-template> 386 </xsl:with-param> 387 </xsl:call-template></PRE> 388 </DIV> 389 </xsl:template> 390 391 <xsl:template match="PY_CODE_SNIPPET"> 392 <DIV> 393 <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute> 394 <PRE><xsl:call-template name="print_python_code"> 395 <xsl:with-param name="text" select="."/> 396 </xsl:call-template></PRE> 397 </DIV> 398 </xsl:template> 399 400 <xsl:template match="BAD_PY_CODE_SNIPPET"> 401 <DIV> 402 <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute> 403 <PRE class="badcode"><xsl:call-template name="print_python_code"> 404 <xsl:with-param name="text" select="."/> 405 </xsl:call-template></PRE> 406 </DIV> 407 </xsl:template> 408 409 <xsl:template match="FUNCTION"> 410 <xsl:call-template name="print_function_name"> 411 <xsl:with-param name="text" select="."/> 412 </xsl:call-template> 413 </xsl:template> 414 415 <xsl:template match="SYNTAX"> 416 <I> 417 <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute> 418 <xsl:apply-templates/> 419 </I> 420 </xsl:template> 421 422 423 <!-- This passes through any HTML elements that the 424 XML doc uses for minor formatting --> 425 <xsl:template match="a|address|blockquote|br|center|cite|code|dd|div|dl|dt|em|hr|i|img|li|ol|p|pre|span|table|td|th|tr|ul|var|A|ADDRESS|BLOCKQUOTE|BR|CENTER|CITE|CODE|DD|DIV|DL|DT|EM|HR|I|LI|OL|P|PRE|SPAN|TABLE|TD|TH|TR|UL|VAR"> 426 <xsl:element name="{local-name()}"> 427 <xsl:copy-of select="@*"/> 428 <xsl:apply-templates/> 429 </xsl:element> 430 </xsl:template> 431 432 <!-- Builds the table of contents --> 433 <xsl:template name="TOC"> 434 <xsl:param name="root"/> 435 <DIV class="toc"> 436 <DIV class="toc_title">Table of Contents</DIV> 437 <TABLE> 438 <xsl:for-each select="$root/CATEGORY"> 439 <TR valign="top"> 440 <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute> 441 <TD> 442 <DIV class="toc_category"> 443 <A> 444 <xsl:attribute name="href"> 445 <xsl:text>#</xsl:text> 446 <xsl:call-template name="anchorname"> 447 <xsl:with-param name="sectionname" select="@title"/> 448 </xsl:call-template> 449 </xsl:attribute> 450 <xsl:value-of select="@title"/> 451 </A> 452 </DIV> 453 </TD><TD> 454 <DIV class="toc_stylepoint"> 455 <xsl:for-each select="./STYLEPOINT"> 456 <SPAN style="padding-right: 1em; white-space:nowrap;"> 457 <xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute> 458 <A> 459 <xsl:attribute name="href"> 460 <xsl:text>#</xsl:text> 461 <xsl:call-template name="anchorname"> 462 <xsl:with-param name="sectionname" select="@title"/> 463 </xsl:call-template> 464 </xsl:attribute> 465 <xsl:value-of select="@title"/> 466 </A> 467 </SPAN> 468 <xsl:text> </xsl:text> 469 </xsl:for-each> 470 </DIV> 471 </TD> 472 </TR> 473 </xsl:for-each> 474 </TABLE> 475 </DIV> 476 </xsl:template> 477 478 <xsl:template name="TOC_one_stylepoint"> 479 <xsl:param name="stylepoint"/> 480 </xsl:template> 481 482 <!-- Creates a standard anchor given any text. 483 Substitutes underscore for characters unsuitable for URLs --> 484 <xsl:template name="anchorname"> 485 <xsl:param name="sectionname"/> 486 <!-- strange quoting necessary to strip apostrophes --> 487 <xsl:variable name="bad_characters" select="" ()#'""/> 488 <xsl:value-of select="translate($sectionname,$bad_characters,'_____')"/> 489 </xsl:template> 490 491 <!-- Given text, evaluates to the number of leading spaces. --> 492 <!-- TODO(csilvers): deal well with leading tabs (treat as 8 spaces?) --> 493 <xsl:template name="num_leading_spaces_one_line"> 494 <xsl:param name="text"/> 495 <xsl:param name="current_count"/> 496 <xsl:choose> 497 <xsl:when test="starts-with($text, ' ')"> 498 <xsl:call-template name="num_leading_spaces_one_line"> 499 <xsl:with-param name="text" select="substring($text, 2)"/> 500 <xsl:with-param name="current_count" select="$current_count + 1"/> 501 </xsl:call-template> 502 </xsl:when> 503 <xsl:otherwise> 504 <xsl:value-of select="$current_count"/> 505 </xsl:otherwise> 506 </xsl:choose> 507 </xsl:template> 508 509 <!-- Given a block of text, each line terminated by \n, evaluates to 510 the indentation-level of that text; that is, the largest number 511 n such that every non-blank line starts with at least n spaces. --> 512 <xsl:template name="num_leading_spaces"> 513 <xsl:param name="text"/> 514 <xsl:param name="max_so_far"/> 515 <!-- TODO(csilvers): deal with case text doesn't end in a newline --> 516 <xsl:variable name="line" select="substring-before($text, '
')"/> 517 <xsl:variable name="rest" select="substring-after($text, '
')"/> 518 <xsl:variable name="num_spaces_this_line"> 519 <xsl:choose> 520 <xsl:when test="$line=''"> 521 <xsl:value-of select="$max_so_far"/> 522 </xsl:when> 523 <xsl:otherwise> 524 <xsl:call-template name="num_leading_spaces_one_line"> 525 <xsl:with-param name="text" select="$line"/> 526 <xsl:with-param name="current_count" select="0"/> 527 </xsl:call-template> 528 </xsl:otherwise> 529 </xsl:choose> 530 </xsl:variable> 531 <xsl:variable name="new_max_so_far"> 532 <xsl:choose> 533 <xsl:when test="$num_spaces_this_line < $max_so_far"> 534 <xsl:value-of select="$num_spaces_this_line"/> 535 </xsl:when> 536 <xsl:otherwise> 537 <xsl:value-of select="$max_so_far"/> 538 </xsl:otherwise> 539 </xsl:choose> 540 </xsl:variable> 541 <!-- now check if we're on the last line, and if not, recurse --> 542 <xsl:if test="$rest=''"> 543 <xsl:value-of select="$new_max_so_far"/> 544 </xsl:if> 545 <xsl:if test="not($rest='')"> 546 <xsl:call-template name="num_leading_spaces"> 547 <xsl:with-param name="text" select="$rest"/> 548 <xsl:with-param name="max_so_far" select="$new_max_so_far"/> 549 </xsl:call-template> 550 </xsl:if> 551 </xsl:template> 552 553 <!-- Given text, determine the starting position of code. 554 This similar to num_leading_spaces_one_line but treats "Yes:" and "No:" 555 as spaces. Also, if there is no code on the first line, it searches 556 subsequent lines until a non-empty line is found. 557 Used to find the start of code in snippets like: 558 Yes: if(foo): 559 No : if(foo): 560 As well as: 561 Yes: 562 if (foo): 563 --> 564 <xsl:template name="code_start_index"> 565 <xsl:param name="text"/> 566 <xsl:param name="current_count"/> 567 <xsl:choose> 568 <xsl:when test="starts-with($text, ' ')"> 569 <xsl:call-template name="code_start_index"> 570 <xsl:with-param name="text" select="substring($text, 2)"/> 571 <xsl:with-param name="current_count" select="$current_count + 1"/> 572 </xsl:call-template> 573 </xsl:when> 574 <xsl:when test="starts-with($text, 'Yes:')"> 575 <xsl:call-template name="code_start_index"> 576 <xsl:with-param name="text" select="substring($text, 5)"/> 577 <xsl:with-param name="current_count" select="$current_count + 4"/> 578 </xsl:call-template> 579 </xsl:when> 580 <xsl:when test="starts-with($text, 'No:')"> 581 <xsl:call-template name="code_start_index"> 582 <xsl:with-param name="text" select="substring($text, 4)"/> 583 <xsl:with-param name="current_count" select="$current_count + 3"/> 584 </xsl:call-template> 585 </xsl:when> 586 <!-- This is only reached if the first line is entirely whitespace or 587 contains nothing but "Yes:" or "No:"--> 588 <xsl:when test="starts-with($text, '
')"> 589 <xsl:call-template name="code_start_index"> 590 <xsl:with-param name="text" select="substring($text, 2)"/> 591 <xsl:with-param name="current_count" select="0"/> 592 </xsl:call-template> 593 </xsl:when> 594 <xsl:otherwise> 595 <xsl:value-of select="$current_count"/> 596 </xsl:otherwise> 597 </xsl:choose> 598 </xsl:template> 599 600 <!-- Helper for ends_with_colon. Determine whether the given line is nothing 601 but spaces and python-style comments. --> 602 <xsl:template name="is_blank_or_comment"> 603 <xsl:param name="line"/> 604 <xsl:choose> 605 <xsl:when test="$line = ''"> 606 <xsl:value-of select="1"/> 607 </xsl:when> 608 <xsl:when test="starts-with($line, '
')"> 609 <xsl:value-of select="1"/> 610 </xsl:when> 611 <xsl:when test="starts-with($line, '#')"> 612 <xsl:value-of select="1"/> 613 </xsl:when> 614 <xsl:when test="starts-with($line, ' ')"> 615 <xsl:call-template name="is_blank_or_comment"> 616 <xsl:with-param name="line" select="substring($line, 2)"/> 617 </xsl:call-template> 618 </xsl:when> 619 <xsl:otherwise> 620 <xsl:value-of select="0"/> 621 </xsl:otherwise> 622 </xsl:choose> 623 </xsl:template> 624 625 <!-- Determine whether the given line ends with a colon. Note that Python 626 style comments are ignored so the following lines return True: 627 - def foo(): 628 - def foo(): # Bar 629 - if(foo): 630 631 But some code may confuse this function. For example the following are 632 also consider to "end_with_colon" even though they don't for Python 633 - foo(": #") 634 - foo() # No need for : 635 --> 636 <xsl:template name="ends_with_colon"> 637 <xsl:param name="line"/> 638 <xsl:param name="found_colon"/> 639 <xsl:choose> 640 <xsl:when test="$line = ''"> 641 <xsl:value-of select="$found_colon"/> 642 </xsl:when> 643 <xsl:when test="starts-with($line, '
')"> 644 <xsl:value-of select="$found_colon"/> 645 </xsl:when> 646 <xsl:when test="starts-with($line, ' ')"> 647 <xsl:call-template name="ends_with_colon"> 648 <xsl:with-param name="line" select="substring($line, 2)"/> 649 <xsl:with-param name="found_colon" select="$found_colon"/> 650 </xsl:call-template> 651 </xsl:when> 652 <xsl:when test="starts-with($line, ':')"> 653 <xsl:variable name="rest_is_comment"> 654 <xsl:call-template name="is_blank_or_comment"> 655 <xsl:with-param name="line" select="substring($line, 2)"/> 656 </xsl:call-template> 657 </xsl:variable> 658 <xsl:choose> 659 <xsl:when test="$rest_is_comment = '1'"> 660 <xsl:value-of select="1"/> 661 </xsl:when> 662 <xsl:otherwise> 663 <xsl:call-template name="ends_with_colon"> 664 <xsl:with-param name="line" select="substring($line, 2)"/> 665 <xsl:with-param name="found_colon" select="0"/> 666 </xsl:call-template> 667 </xsl:otherwise> 668 </xsl:choose> 669 </xsl:when> 670 <xsl:otherwise> 671 <xsl:call-template name="ends_with_colon"> 672 <xsl:with-param name="line" select="substring($line, 2)"/> 673 <xsl:with-param name="found_colon" select="0"/> 674 </xsl:call-template> 675 </xsl:otherwise> 676 </xsl:choose> 677 </xsl:template> 678 679 <!-- Prints one line of python code with proper indent and calls itself 680 recursively for the rest of the text. 681 This template uses "a", "b", "c" and "d" to refer to four key column 682 numbers. They are: 683 - a: the indentation common to all lines in a code snippet. This is 684 stripped out to allow for cleaner code in the xml. 685 - b: the indentation of the most out-dented line of code. This is 686 different from "a" when code is labelled with "Yes:" or "No:" 687 - c: the indentation of the current python block, in other words, the 688 indentation of the first line of this block, which is the 689 indentation of the last line we saw that ended with a colon. 690 - d: the "total" indentation of the line, ignorng possible "Yes:" or 691 "No:" text on the line. 692 693 For example, for the last line of the following code snippet, the 694 positions of a, b, c and d are indicated below: 695 Yes: def Foo(): 696 if bar(): 697 a += 1 698 baz() 699 a b c d 700 701 The algorithm is: 702 1) Split the text into first line and the rest. Note that the 703 substring-before function is supposed to handle the case where the 704 character is not present in the string but does not so we 705 automatically ignore the last line of the snippet which is always 706 empty (the closing snippet tag). This is consistent with the 707 behavior or print_without_leading_chars. 708 2) If the current is empty (only whitespace), print newline and call 709 itself recursively on the rest of the text with the rest of the 710 parameters unchanged. 711 3) Otherwise, measure "d" 712 4) Measure "c" by taking: 713 - the value of "d" if the previous line ended with a colon or the 714 current line is outdented compare to the previous line 715 - the indent of the previous line otherwise 716 5) Print line[a:c] (Note that we ignore line[0:a]) 717 6) Print line[b:c] in an external span (in order to double the block 718 indent in external code). 719 7) Print line[c:<end>] with function names processed to produce both 720 internal and external names. 721 8) If there are more lines, recurse. 722 --> 723 <xsl:template name="print_python_line_recursively"> 724 <xsl:param name="text"/> 725 <xsl:param name="a"/> 726 <xsl:param name="b"/> 727 <xsl:param name="previous_indent"/> 728 <xsl:param name="previous_ends_with_colon"/> 729 <xsl:param name="is_first_line"/> 730 <xsl:variable name="line" select="substring-before($text, '
')"/> 731 <xsl:variable name="rest" select="substring-after($text, '
')"/> 732 <xsl:choose> 733 <xsl:when test="substring($line, $b) = '' and not($rest = '')"> 734 <xsl:if test="not($is_first_line = '1')"> 735 <xsl:text>
</xsl:text> 736 </xsl:if> 737 <xsl:call-template name="print_python_line_recursively"> 738 <xsl:with-param name="text" select="$rest"/> 739 <xsl:with-param name="a" select="$a"/> 740 <xsl:with-param name="b" select="$b"/> 741 <xsl:with-param name="previous_indent" select="$previous_indent"/> 742 <xsl:with-param name="previous_ends_with_colon" 743 select="$previous_ends_with_colon"/> 744 <xsl:with-param name="is_first_line" select="0"/> 745 </xsl:call-template> 746 </xsl:when> 747 <xsl:otherwise> 748 <xsl:variable name="indent_after_b"> 749 <xsl:call-template name="num_leading_spaces_one_line"> 750 <xsl:with-param name="text" select="substring($line, $b + 1)"/> 751 <xsl:with-param name="current_count" select="0"/> 752 </xsl:call-template> 753 </xsl:variable> 754 <xsl:variable name="d" select="$b + $indent_after_b"/> 755 <xsl:variable name="c"> 756 <xsl:choose> 757 <xsl:when test="$previous_ends_with_colon = '1' or 758 $previous_indent > $d"> 759 <xsl:value-of select="$d"/> 760 </xsl:when> 761 <xsl:otherwise> 762 <xsl:value-of select="$previous_indent"/> 763 </xsl:otherwise> 764 </xsl:choose> 765 </xsl:variable> 766 767 <xsl:value-of select="substring($line, $a + 1, $c - $a)"/> 768 <span class="external"> 769 <xsl:value-of select="substring($line, $b + 1, $c - $b)"/> 770 </span> 771 <xsl:call-template name="munge_function_names_in_text"> 772 <xsl:with-param name="stripped_line" 773 select="substring($line, $c + 1)"/> 774 </xsl:call-template> 775 <xsl:if test="not(substring($rest, $a) = '')"> 776 <xsl:text>
</xsl:text> 777 <xsl:call-template name="print_python_line_recursively"> 778 <xsl:with-param name="text" select="$rest"/> 779 <xsl:with-param name="a" select="$a"/> 780 <xsl:with-param name="b" select="$b"/> 781 <xsl:with-param name="previous_indent" select="$c"/> 782 <xsl:with-param name="previous_ends_with_colon"> 783 <xsl:call-template name="ends_with_colon"> 784 <xsl:with-param name="line" select="$line"/> 785 <xsl:with-param name="found_colon" select="0"/> 786 </xsl:call-template> 787 </xsl:with-param> 788 <xsl:with-param name="is_first_line" select="0"/> 789 </xsl:call-template> 790 </xsl:if> 791 </xsl:otherwise> 792 </xsl:choose> 793 </xsl:template> 794 795 <!-- Print python code with internal and external styles. 796 In order to conform with PEP-8 externally, we identify 2-space indents 797 and an external-only 4-space indent. 798 Function names that are marked with $$FunctionName/$$ have an external 799 lower_with_underscore version added. --> 800 <xsl:template name="print_python_code"> 801 <xsl:param name="text"/> 802 803 <xsl:variable name="a"> 804 <xsl:call-template name="num_leading_spaces"> 805 <xsl:with-param name="text" select="."/> 806 <xsl:with-param name="max_so_far" select="1000"/> 807 </xsl:call-template> 808 </xsl:variable> 809 810 <xsl:variable name="b"> 811 <xsl:call-template name="code_start_index"> 812 <xsl:with-param name="text" select="$text"/> 813 <xsl:with-param name="current_count" select="0"/> 814 </xsl:call-template> 815 </xsl:variable> 816 817 <xsl:call-template name="print_python_line_recursively"> 818 <xsl:with-param name="text" select="$text"/> 819 <xsl:with-param name="a" select="$a"/> 820 <xsl:with-param name="b" select="$b"/> 821 <xsl:with-param name="previous_indent" select="$b"/> 822 <xsl:with-param name="previous_ends_with_colon" select="0"/> 823 <xsl:with-param name="is_first_line" select="1"/> 824 </xsl:call-template> 825 </xsl:template> 826 827 <!-- Given a block of text, each line terminated by \n, and a number n, 828 emits the text with the first n characters of each line 829 deleted. If strip==1, then we omit blank lines at the beginning 830 and end of the text (but not the middle!) --> 831 <!-- TODO(csilvers): deal well with leading tabs (treat as 8 spaces?) --> 832 <xsl:template name="print_without_leading_chars"> 833 <xsl:param name="text"/> 834 <xsl:param name="trim_count"/> 835 <xsl:param name="strip"/> 836 <xsl:param name="is_firstline"/> 837 <!-- TODO(csilvers): deal with case text doesn't end in a newline --> 838 <xsl:variable name="line" select="substring-before($text, '
')"/> 839 <xsl:variable name="rest" select="substring-after($text, '
')"/> 840 <xsl:variable name="stripped_line" select="substring($line, $trim_count+1)"/> 841 <xsl:choose> 842 <!-- $line (or $rest) is considered empty if we'd trim the entire line --> 843 <xsl:when test="($strip = '1') and ($is_firstline = '1') and 844 (string-length($line) <= $trim_count)"> 845 </xsl:when> 846 <xsl:when test="($strip = '1') and 847 (string-length($rest) <= $trim_count)"> 848 <xsl:value-of select="$stripped_line"/> 849 </xsl:when> 850 <xsl:otherwise> 851 <xsl:value-of select="$stripped_line"/> 852 <xsl:text>
</xsl:text> 853 </xsl:otherwise> 854 </xsl:choose> 855 <xsl:if test="not($rest='')"> 856 <xsl:call-template name="print_without_leading_chars"> 857 <xsl:with-param name="text" select="$rest"/> 858 <xsl:with-param name="trim_count" select="$trim_count"/> 859 <xsl:with-param name="strip" select="$strip"/> 860 <xsl:with-param name="is_firstline" select="0"/> 861 </xsl:call-template> 862 </xsl:if> 863 </xsl:template> 864 865 <!-- Given a line of code, find function names that are marked with $$ /$$ and 866 print out the line with the internal and external versions of the 867 function names.--> 868 <xsl:template name="munge_function_names_in_text"> 869 <xsl:param name="stripped_line"/> 870 <xsl:choose> 871 <xsl:when test="contains($stripped_line, '$$')"> 872 <xsl:value-of select="substring-before($stripped_line, '$$')"/> 873 <xsl:call-template name="print_function_name"> 874 <xsl:with-param name="text" select="substring-after(substring-before($stripped_line, '/$$'), '$$')"/> 875 </xsl:call-template> 876 <xsl:call-template name="munge_function_names_in_text"> 877 <xsl:with-param name="stripped_line" select="substring-after($stripped_line, '/$$')"/> 878 </xsl:call-template> 879 </xsl:when> 880 <xsl:otherwise> 881 <xsl:value-of select="$stripped_line"/> 882 </xsl:otherwise> 883 </xsl:choose> 884 </xsl:template> 885 886 <!-- Given a function name, print out both the internal and external version 887 of the function name in their respective spans.--> 888 <xsl:template name="print_function_name"> 889 <xsl:param name="text"/> 890 <xsl:call-template name="convert_camel_case_to_lowercase_with_under"> 891 <xsl:with-param name="text" select="$text"/> 892 </xsl:call-template> 893 </xsl:template> 894 895 <!-- Given a single word of text convert it from CamelCase to 896 lower_with_under. 897 This means replacing each uppercase character with _ followed by the 898 lowercase version except for the first character which is replaced 899 without adding the _.--> 900 <xsl:template name="convert_camel_case_to_lowercase_with_under"> 901 <xsl:param name="text"/> 902 <xsl:param name="is_recursive_call"/> 903 <xsl:variable name="first_char" select="substring($text, 1, 1)"/> 904 <xsl:variable name="rest" select="substring($text, 2)"/> 905 <xsl:choose> 906 <xsl:when test="contains('ABCDEFGHIJKLMNOPQRSTUVWXYZ', $first_char)"> 907 <xsl:if test="$is_recursive_call='1'"> 908 <xsl:text>_</xsl:text> 909 </xsl:if> 910 <xsl:value-of select="translate($first_char, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/> 911 </xsl:when> 912 <xsl:otherwise> 913 <xsl:value-of select="$first_char" /> 914 </xsl:otherwise> 915 </xsl:choose> 916 <xsl:if test="not($rest='')"> 917 <xsl:call-template name="convert_camel_case_to_lowercase_with_under"> 918 <xsl:with-param name="text" select="$rest"/> 919 <xsl:with-param name="is_recursive_call" select="1"/> 920 </xsl:call-template> 921 </xsl:if> 922 </xsl:template> 923</xsl:stylesheet> 924 925