├── src ├── prettify.css ├── lang-proto.js ├── lang-lua.js ├── lang-ml.js ├── lang-sql.js ├── lang-lisp.js └── prettify.js ├── CHANGES.html ├── README.html ├── COPYING └── tests └── prettify_test.html /src/prettify.css: -------------------------------------------------------------------------------- 1 | /* Pretty printing styles. Used with prettify.js. */ 2 | 3 | .str { color: #080; } 4 | .kwd { color: #008; } 5 | .com { color: #800; } 6 | .typ { color: #606; } 7 | .lit { color: #066; } 8 | .pun { color: #660; } 9 | .pln { color: #000; } 10 | .tag { color: #008; } 11 | .atn { color: #606; } 12 | .atv { color: #080; } 13 | .dec { color: #606; } 14 | pre.prettyprint { padding: 2px; border: 1px solid #888; } 15 | 16 | @media print { 17 | .str { color: #060; } 18 | .kwd { color: #006; font-weight: bold; } 19 | .com { color: #600; font-style: italic; } 20 | .typ { color: #404; font-weight: bold; } 21 | .lit { color: #044; } 22 | .pun { color: #440; } 23 | .pln { color: #000; } 24 | .tag { color: #006; font-weight: bold; } 25 | .atn { color: #404; } 26 | .atv { color: #060; } 27 | } 28 | -------------------------------------------------------------------------------- /src/lang-proto.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2006 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | 16 | /** 17 | * @fileoverview 18 | * Registers a language handler for Protocol Buffers as described at 19 | * http://code.google.com/p/protobuf/. 20 | * 21 | * Based on the lexical grammar at 22 | * http://research.microsoft.com/fsharp/manual/spec2.aspx#_Toc202383715 23 | * 24 | * @author mikesamuel@gmail.com 25 | */ 26 | 27 | PR.registerLangHandler(PR.sourceDecorator({ 28 | keywords: ( 29 | 'bool bytes default double enum extend extensions false fixed32 ' 30 | + 'fixed64 float group import int32 int64 max message option ' 31 | + 'optional package repeated required returns rpc service ' 32 | + 'sfixed32 sfixed64 sint32 sint64 string syntax to true uint32 ' 33 | + 'uint64'), 34 | cStyleComments: true 35 | }), ['proto']); 36 | -------------------------------------------------------------------------------- /src/lang-lua.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2008 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | 16 | 17 | /** 18 | * @fileoverview 19 | * Registers a language handler for LUA. 20 | * 21 | * 22 | * To use, include prettify.js and this file in your HTML page. 23 | * Then put your code in an HTML tag like 24 | *
(my LUA code)
25 | * 26 | * 27 | * I used http://www.lua.org/manual/5.1/manual.html#2.1 28 | * Because of the long-bracket concept used in strings and comments, LUA does 29 | * not have a regular lexical grammar, but luckily it fits within the space 30 | * of irregular grammars supported by javascript regular expressions. 31 | * 32 | * @author mikesamuel@gmail.com 33 | */ 34 | 35 | PR.registerLangHandler( 36 | PR.createSimpleLexer( 37 | [ 38 | // Whitespace 39 | [PR.PR_PLAIN, /^[\t\n\r \xA0]+/, null, '\t\n\r \xA0'], 40 | // A double or single quoted, possibly multi-line, string. 41 | [PR.PR_STRING, /^(?:\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\]|\\[\s\S])*(?:\'|$))/, null, '"\''] 42 | ], 43 | [ 44 | // A comment is either a line comment that starts with two dashes, or 45 | // two dashes preceding a long bracketed block. 46 | [PR.PR_COMMENT, /^--(?:\[(=*)\[[\s\S]*?(?:\]\1\]|$)|[^\r\n]*)/], 47 | // A long bracketed block not preceded by -- is a string. 48 | [PR.PR_STRING, /^\[(=*)\[[\s\S]*?(?:\]\1\]|$)/], 49 | [PR.PR_KEYWORD, /^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/, null], 50 | // A number is a hex integer literal, a decimal real literal, or in 51 | // scientific notation. 52 | [PR.PR_LITERAL, 53 | /^[+-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i], 54 | // An identifier 55 | [PR.PR_PLAIN, /^[a-z_]\w*/i], 56 | // A run of punctuation 57 | [PR.PR_PUNCTUATION, /^[^\w\t\n\r \xA0]+/] 58 | ]), 59 | ['lua']); 60 | -------------------------------------------------------------------------------- /CHANGES.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Change Log 5 | 6 | 7 | README 8 |

Change Log

9 |

29 March 2007

10 | 41 |

4 Jul 2008

42 | 48 |

5 Jul 2008

49 | 52 |

14 Jul 2008

53 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/lang-ml.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2008 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | 16 | 17 | /** 18 | * @fileoverview 19 | * Registers a language handler for OCaml, SML, F# and similar languages. 20 | * 21 | * Based on the lexical grammar at 22 | * http://research.microsoft.com/fsharp/manual/spec2.aspx#_Toc202383715 23 | * 24 | * @author mikesamuel@gmail.com 25 | */ 26 | 27 | PR.registerLangHandler( 28 | PR.createSimpleLexer( 29 | [ 30 | // Whitespace is made up of spaces, tabs and newline characters. 31 | [PR.PR_PLAIN, /^[\t\n\r \xA0]+/, null, '\t\n\r \xA0'], 32 | // #if ident/#else/#endif directives delimit conditional compilation 33 | // sections 34 | [PR.PR_COMMENT, 35 | /^#(?:if[\t\n\r \xA0]+(?:[a-z_$][\w\']*|``[^\r\n\t`]*(?:``|$))|else|endif|light)/i, 36 | null, '#'], 37 | // A double or single quoted, possibly multi-line, string. 38 | // F# allows escaped newlines in strings. 39 | [PR.PR_STRING, /^(?:\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\]|\\[\s\S])*(?:\'|$))/, null, '"\''] 40 | ], 41 | [ 42 | // Block comments are delimited by (* and *) and may be 43 | // nested. Single-line comments begin with // and extend to 44 | // the end of a line. 45 | // TODO: (*...*) comments can be nested. This does not handle that. 46 | [PR.PR_COMMENT, /^(?:\/\/[^\r\n]*|\(\*[\s\S]*?\*\))/], 47 | [PR.PR_KEYWORD, /^(?:abstract|and|as|assert|begin|class|default|delegate|do|done|downcast|downto|elif|else|end|exception|extern|false|finally|for|fun|function|if|in|inherit|inline|interface|internal|lazy|let|match|member|module|mutable|namespace|new|null|of|open|or|override|private|public|rec|return|static|struct|then|to|true|try|type|upcast|use|val|void|when|while|with|yield|asr|land|lor|lsl|lsr|lxor|mod|sig|atomic|break|checked|component|const|constraint|constructor|continue|eager|event|external|fixed|functor|global|include|method|mixin|object|parallel|process|protected|pure|sealed|trait|virtual|volatile)\b/], 48 | // A number is a hex integer literal, a decimal real literal, or in 49 | // scientific notation. 50 | [PR.PR_LITERAL, 51 | /^[+\-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i], 52 | [PR.PR_PLAIN, /^(?:[a-z_]\w*[!?#]?|``[^\r\n\t`]*(?:``|$))/i], 53 | // A printable non-space non-special character 54 | [PR.PR_PUNCTUATION, /^[^\t\n\r \xA0\"\'\w]+/] 55 | ]), 56 | ['fs', 'ml']); 57 | -------------------------------------------------------------------------------- /src/lang-sql.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2008 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | 16 | 17 | /** 18 | * @fileoverview 19 | * Registers a language handler for SQL. 20 | * 21 | * 22 | * To use, include prettify.js and this file in your HTML page. 23 | * Then put your code in an HTML tag like 24 | *
(my SQL code)
25 | * 26 | * 27 | * http://savage.net.au/SQL/sql-99.bnf.html is the basis for the grammar, and 28 | * http://msdn.microsoft.com/en-us/library/aa238507(SQL.80).aspx as the basis 29 | * for the keyword list. 30 | * 31 | * @author mikesamuel@gmail.com 32 | */ 33 | 34 | PR.registerLangHandler( 35 | PR.createSimpleLexer( 36 | [ 37 | // Whitespace 38 | [PR.PR_PLAIN, /^[\t\n\r \xA0]+/, null, '\t\n\r \xA0'], 39 | // A double or single quoted, possibly multi-line, string. 40 | [PR.PR_STRING, /^(?:"(?:[^\"\\]|\\.)*"|'(?:[^\'\\]|\\.)*')/, null, 41 | '"\''] 42 | ], 43 | [ 44 | // A comment is either a line comment that starts with two dashes, or 45 | // two dashes preceding a long bracketed block. 46 | [PR.PR_COMMENT, /^(?:--[^\r\n]*|\/\*[\s\S]*?(?:\*\/|$))/], 47 | [PR.PR_KEYWORD, /^(?:ADD|ALL|ALTER|AND|ANY|AS|ASC|AUTHORIZATION|BACKUP|BEGIN|BETWEEN|BREAK|BROWSE|BULK|BY|CASCADE|CASE|CHECK|CHECKPOINT|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMN|COMMIT|COMPUTE|CONSTRAINT|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CREATE|CROSS|CURRENT|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DBCC|DEALLOCATE|DECLARE|DEFAULT|DELETE|DENY|DESC|DISK|DISTINCT|DISTRIBUTED|DOUBLE|DROP|DUMMY|DUMP|ELSE|END|ERRLVL|ESCAPE|EXCEPT|EXEC|EXECUTE|EXISTS|EXIT|FETCH|FILE|FILLFACTOR|FOR|FOREIGN|FREETEXT|FREETEXTTABLE|FROM|FULL|FUNCTION|GOTO|GRANT|GROUP|HAVING|HOLDLOCK|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|IF|IN|INDEX|INNER|INSERT|INTERSECT|INTO|IS|JOIN|KEY|KILL|LEFT|LIKE|LIMIT|LINENO|LOAD|NATIONAL|NOCHECK|NONCLUSTERED|NOT|NULL|NULLIF|OF|OFF|OFFSETS|ON|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OPTION|OR|ORDER|OUTER|OVER|PERCENT|PLAN|PRECISION|PRIMARY|PRINT|PROC|PROCEDURE|PUBLIC|RAISERROR|READ|READTEXT|RECONFIGURE|REFERENCES|REPLICATION|RESTORE|RESTRICT|RETURN|REVOKE|RIGHT|ROLLBACK|ROWCOUNT|ROWGUIDCOL|RULE|SAVE|SCHEMA|SELECT|SESSION_USER|SET|SETUSER|SHUTDOWN|SOME|STATISTICS|SYSTEM_USER|TABLE|TEXTSIZE|THEN|TO|TOP|TRAN|TRANSACTION|TRIGGER|TRUNCATE|TSEQUAL|UNION|UNIQUE|UPDATE|UPDATETEXT|USE|USER|VALUES|VARYING|VIEW|WAITFOR|WHEN|WHERE|WHILE|WITH|WRITETEXT)(?=[^\w-]|$)/i, null], 48 | // A number is a hex integer literal, a decimal real literal, or in 49 | // scientific notation. 50 | [PR.PR_LITERAL, 51 | /^[+-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i], 52 | // An identifier 53 | [PR.PR_PLAIN, /^[a-z_][\w-]*/i], 54 | // A run of punctuation 55 | [PR.PR_PUNCTUATION, /^[^\w\t\n\r \xA0]+/] 56 | ]), 57 | ['sql']); 58 | -------------------------------------------------------------------------------- /src/lang-lisp.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2008 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | 16 | 17 | /** 18 | * @fileoverview 19 | * Registers a language handler for Common Lisp and related languages. 20 | * 21 | * 22 | * To use, include prettify.js and this file in your HTML page. 23 | * Then put your code in an HTML tag like 24 | *
(my lisp code)
25 | * The lang-cl class identifies the language as common lisp. 26 | * This file supports the following language extensions: 27 | * lang-cl - Common Lisp 28 | * lang-el - Emacs Lisp 29 | * lang-lisp - Lisp 30 | * lang-scm - Scheme 31 | * 32 | * 33 | * I used http://www.devincook.com/goldparser/doc/meta-language/grammar-LISP.htm 34 | * as the basis, but added line comments that start with ; and changed the atom 35 | * production to disallow unquoted semicolons. 36 | * 37 | * "Name" = 'LISP' 38 | * "Author" = 'John McCarthy' 39 | * "Version" = 'Minimal' 40 | * "About" = 'LISP is an abstract language that organizes ALL' 41 | * | 'data around "lists".' 42 | * 43 | * "Start Symbol" = [s-Expression] 44 | * 45 | * {Atom Char} = {Printable} - {Whitespace} - [()"\''] 46 | * 47 | * Atom = ( {Atom Char} | '\'{Printable} )+ 48 | * 49 | * [s-Expression] ::= [Quote] Atom 50 | * | [Quote] '(' [Series] ')' 51 | * | [Quote] '(' [s-Expression] '.' [s-Expression] ')' 52 | * 53 | * [Series] ::= [s-Expression] [Series] 54 | * | 55 | * 56 | * [Quote] ::= '' !Quote = do not evaluate 57 | * | 58 | * 59 | * 60 | * I used Practical Common Lisp as 61 | * the basis for the reserved word list. 62 | * 63 | * 64 | * @author mikesamuel@gmail.com 65 | */ 66 | 67 | PR.registerLangHandler( 68 | PR.createSimpleLexer( 69 | [ 70 | ['opn', /^\(/, null, '('], 71 | ['clo', /^\)/, null, ')'], 72 | // A line comment that starts with ; 73 | [PR.PR_COMMENT, /^;[^\r\n]*/, null, ';'], 74 | // Whitespace 75 | [PR.PR_PLAIN, /^[\t\n\r \xA0]+/, null, '\t\n\r \xA0'], 76 | // A double quoted, possibly multi-line, string. 77 | [PR.PR_STRING, /^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/, null, '"'] 78 | ], 79 | [ 80 | [PR.PR_KEYWORD, /^(?:block|c[ad]+r|catch|cons|defun|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/, null], 81 | [PR.PR_LITERAL, 82 | /^[+\-]?(?:\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[eEdD][+\-]?\d+)?)/], 83 | // A single quote possibly followed by a word that optionally ends with 84 | // = ! or ?. 85 | [PR.PR_LITERAL, 86 | /^\'(?:-*(?:\w|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?)?/], 87 | // A word that optionally ends with = ! or ?. 88 | [PR.PR_PLAIN, 89 | /^-*(?:[a-z_]|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?/i], 90 | // A printable non-space non-special character 91 | [PR.PR_PUNCTUATION, /^[^\w\t\n\r \xA0()\"\\\';]+/] 92 | ]), 93 | ['cl', 'el', 'lisp', 'scm']); 94 | -------------------------------------------------------------------------------- /README.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Javascript code prettifier 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 18 | 19 |

Javascript code prettifier

20 | 21 |

Setup

22 |
    23 |
  1. Download a distribution 24 |
  2. Include the script and stylesheets in your document 25 | (you will need to make sure the css and js file are on your server, and 26 | adjust the paths in the script and link tag) 27 |
     28 | <link href="prettify.css" type="text/css" rel="stylesheet" />
     29 | <script type="text/javascript" src="prettify.js"></script>
    30 |
  3. Add onload="prettyPrint()" to your 31 | document's body tag. 32 |
  4. Modify the stylesheet to get the coloring you prefer
  5. 33 |
34 | 35 |

Usage

36 |

Put code snippets in 37 | <pre class="prettyprint">...</pre> 38 | or <code class="prettyprint">...</code> 39 | and it will automatically be pretty printed. 40 | 41 | 42 | 43 | 46 |
The original 44 | Prettier 45 |
class Voila {
 48 | public:
 49 |   // Voila
 50 |   static const string VOILA = "Voila";
 51 | 
 52 |   // will not interfere with embedded tags.
 53 | }
54 | 55 |
class Voila {
 56 | public:
 57 |   // Voila
 58 |   static const string VOILA = "Voila";
 59 | 
 60 |   // will not interfere with embedded tags.
 61 | }
62 |
63 | 64 |

FAQ

65 |

Which languages does it work for?

66 |

The comments in prettify.js are authoritative but the lexer 67 | should work on a number of languages including C and friends, 68 | Java, Python, Bash, SQL, HTML, XML, CSS, Javascript, and Makefiles. 69 | It works passably on Ruby, PHP and Awk and a decent subset of Perl, but, 70 | because of commenting conventions, doesn't work on Smalltalk, or 71 | CAML-like languages.

72 | 73 |

LISPy languages are supported via an extension: 74 | lang-lisp.js.

76 |

And similarly for LUA, OCAML, SML, F#, 79 | SQL, and 81 | Protocol Buffers. 83 | 84 |

If you'd like to add an extension for your favorite language, please 85 | look at lang-lisp.js and file an 86 | issue including your language extension, and a testcase.

88 | 89 |

How do I specify which language my code is in?

90 |

You don't need to specify the language since prettyprint() 91 | will guess. You can specify a language by specifying the language extension 92 | along with the prettyprint class like so:

93 |
<pre class="prettyprint lang-html">
 95 |   The lang-* class specifies the language file extensions.
 96 |   Supported file extensions include
 97 |     "c", "cc", "cpp", "cs", "cyc", "java", "bsh", "csh", "sh",
 98 |     "cv", "py", "perl", "pl", "pm", "rb", "js",
 99 |     "html", "html", "xhtml", "xml", "xsl".
100 | </pre>
101 | 102 |

It doesn't work on <obfuscated code sample>?

103 |

Yes. Prettifying obfuscated code is like putting lipstick on a pig 104 | — i.e. outside the scope of this tool.

105 | 106 |

Which browsers does it work with?

107 |

It's been tested with IE 6, Firefox 1.5 & 2, and Safari 2.0.4. 108 | Look at the test page to see if it 109 | works in your browser.

110 | 111 |

What's changed?

112 |

See the change log

113 | 114 |

Why doesn't Prettyprinting of strings work on WordPress?

115 |

Apparently wordpress does "smart quoting" which changes close quotes. 116 | This causes end quotes to not match up with open quotes. 117 |

This breaks prettifying as well as copying and pasting of code samples. 118 | See 119 | WordPress's help center for info on how to stop smart quoting of code 121 | snippets.

122 | 123 |

How do I put line numbers in my code?

124 |

You can use the nocode class to identify a span of markup 125 | that is not code. 126 |

<pre class=prettyprint>
127 | <span class="nocode">1:</span> /* This is line 1 of my code
128 | <span class="nocode">2:</span>  * and here's line 2 */
129 | <span class="nocode">3:</span> print("I'm line number 3");
130 | </pre>
131 | produces 132 |
133 | 1: /* This is line 1 of my code
134 | 2:  * and here's line 2 */
135 | 3: print("I'm line number 3");
136 | 
137 | 138 |

For a more complete example see the issue22 139 | testcase.

140 | 141 |


142 | 143 | 144 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/prettify.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2006 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | 16 | /** 17 | * @fileoverview 18 | * some functions for browser-side pretty printing of code contained in html. 19 | * 20 | * The lexer should work on a number of languages including C and friends, 21 | * Java, Python, Bash, SQL, HTML, XML, CSS, Javascript, and Makefiles. 22 | * It works passably on Ruby, PHP and Awk and a decent subset of Perl, but, 23 | * because of commenting conventions, doesn't work on Smalltalk, Lisp-like, or 24 | * CAML-like languages. 25 | * 26 | * If there's a language not mentioned here, then I don't know it, and don't 27 | * know whether it works. If it has a C-like, Bash-like, or XML-like syntax 28 | * then it should work passably. 29 | * 30 | * Usage: 31 | * 1) include this source file in an html page via 32 | * 33 | * 2) define style rules. See the example page for examples. 34 | * 3) mark the
 and  tags in your source with class=prettyprint.
  35 |  *    You can also use the (html deprecated)  tag, but the pretty printer
  36 |  *    needs to do more substantial DOM manipulations to support that, so some
  37 |  *    css styles may not be preserved.
  38 |  * That's it.  I wanted to keep the API as simple as possible, so there's no
  39 |  * need to specify which language the code is in.
  40 |  *
  41 |  * Change log:
  42 |  * cbeust, 2006/08/22
  43 |  *   Java annotations (start with "@") are now captured as literals ("lit")
  44 |  */
  45 | 
  46 | // JSLint declarations
  47 | /*global console, document, navigator, setTimeout, window */
  48 | 
  49 | /**
  50 |  * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
  51 |  * UI events.
  52 |  * If set to {@code false}, {@code prettyPrint()} is synchronous.
  53 |  */
  54 | var PR_SHOULD_USE_CONTINUATION = true;
  55 | 
  56 | /** the number of characters between tab columns */
  57 | var PR_TAB_WIDTH = 8;
  58 | 
  59 | /** Walks the DOM returning a properly escaped version of innerHTML.
  60 |   * @param {Node} node
  61 |   * @param {Array.<string>} out output buffer that receives chunks of HTML.
  62 |   */
  63 | var PR_normalizedHtml;
  64 | 
  65 | /** Contains functions for creating and registering new language handlers.
  66 |   * @type {Object}
  67 |   */
  68 | var PR;
  69 | 
  70 | /** Pretty print a chunk of code.
  71 |   *
  72 |   * @param {string} sourceCodeHtml code as html
  73 |   * @return {string} code as html, but prettier
  74 |   */
  75 | var prettyPrintOne;
  76 | /** find all the < pre > and < code > tags in the DOM with class=prettyprint
  77 |   * and prettify them.
  78 |   * @param {Function} opt_whenDone if specified, called when the last entry
  79 |   *     has been finished.
  80 |   */
  81 | var prettyPrint;
  82 | 
  83 | /** browser detection. @extern */
  84 | function _pr_isIE6() {
  85 |   var isIE6 = navigator && navigator.userAgent &&
  86 |       /\bMSIE 6\./.test(navigator.userAgent);
  87 |   _pr_isIE6 = function () { return isIE6; };
  88 |   return isIE6;
  89 | }
  90 | 
  91 | 
  92 | (function () {
  93 |   /** Splits input on space and returns an Object mapping each non-empty part to
  94 |     * true.
  95 |     */
  96 |   function wordSet(words) {
  97 |     words = words.split(/ /g);
  98 |     var set = {};
  99 |     for (var i = words.length; --i >= 0;) {
 100 |       var w = words[i];
 101 |       if (w) { set[w] = null; }
 102 |     }
 103 |     return set;
 104 |   }
 105 | 
 106 |   // Keyword lists for various languages.
 107 |   var FLOW_CONTROL_KEYWORDS =
 108 |       "break continue do else for if return while ";
 109 |   var C_KEYWORDS = FLOW_CONTROL_KEYWORDS + "auto case char const default " +
 110 |       "double enum extern float goto int long register short signed sizeof " +
 111 |       "static struct switch typedef union unsigned void volatile ";
 112 |   var COMMON_KEYWORDS = C_KEYWORDS + "catch class delete false import " +
 113 |       "new operator private protected public this throw true try ";
 114 |   var CPP_KEYWORDS = COMMON_KEYWORDS + "alignof align_union asm axiom bool " +
 115 |       "concept concept_map const_cast constexpr decltype " +
 116 |       "dynamic_cast explicit export friend inline late_check " +
 117 |       "mutable namespace nullptr reinterpret_cast static_assert static_cast " +
 118 |       "template typeid typename typeof using virtual wchar_t where ";
 119 |   var JAVA_KEYWORDS = COMMON_KEYWORDS +
 120 |       "boolean byte extends final finally implements import instanceof null " +
 121 |       "native package strictfp super synchronized throws transient ";
 122 |   var CSHARP_KEYWORDS = JAVA_KEYWORDS +
 123 |       "as base by checked decimal delegate descending event " +
 124 |       "fixed foreach from group implicit in interface internal into is lock " +
 125 |       "object out override orderby params readonly ref sbyte sealed " +
 126 |       "stackalloc string select uint ulong unchecked unsafe ushort var ";
 127 |   var JSCRIPT_KEYWORDS = COMMON_KEYWORDS +
 128 |       "debugger eval export function get null set undefined var with " +
 129 |       "Infinity NaN ";
 130 |   var PERL_KEYWORDS = "caller delete die do dump elsif eval exit foreach for " +
 131 |       "goto if import last local my next no our print package redo require " +
 132 |       "sub undef unless until use wantarray while BEGIN END ";
 133 |   var PYTHON_KEYWORDS = FLOW_CONTROL_KEYWORDS + "and as assert class def del " +
 134 |       "elif except exec finally from global import in is lambda " +
 135 |       "nonlocal not or pass print raise try with yield " +
 136 |       "False True None ";
 137 |   var RUBY_KEYWORDS = FLOW_CONTROL_KEYWORDS + "alias and begin case class def" +
 138 |       " defined elsif end ensure false in module next nil not or redo rescue " +
 139 |       "retry self super then true undef unless until when yield BEGIN END ";
 140 |   var SH_KEYWORDS = FLOW_CONTROL_KEYWORDS + "case done elif esac eval fi " +
 141 |       "function in local set then until ";
 142 |   var ALL_KEYWORDS = (
 143 |       CPP_KEYWORDS + CSHARP_KEYWORDS + JSCRIPT_KEYWORDS + PERL_KEYWORDS +
 144 |       PYTHON_KEYWORDS + RUBY_KEYWORDS + SH_KEYWORDS);
 145 | 
 146 |   // token style names.  correspond to css classes
 147 |   /** token style for a string literal */
 148 |   var PR_STRING = 'str';
 149 |   /** token style for a keyword */
 150 |   var PR_KEYWORD = 'kwd';
 151 |   /** token style for a comment */
 152 |   var PR_COMMENT = 'com';
 153 |   /** token style for a type */
 154 |   var PR_TYPE = 'typ';
 155 |   /** token style for a literal value.  e.g. 1, null, true. */
 156 |   var PR_LITERAL = 'lit';
 157 |   /** token style for a punctuation string. */
 158 |   var PR_PUNCTUATION = 'pun';
 159 |   /** token style for a punctuation string. */
 160 |   var PR_PLAIN = 'pln';
 161 | 
 162 |   /** token style for an sgml tag. */
 163 |   var PR_TAG = 'tag';
 164 |   /** token style for a markup declaration such as a DOCTYPE. */
 165 |   var PR_DECLARATION = 'dec';
 166 |   /** token style for embedded source. */
 167 |   var PR_SOURCE = 'src';
 168 |   /** token style for an sgml attribute name. */
 169 |   var PR_ATTRIB_NAME = 'atn';
 170 |   /** token style for an sgml attribute value. */
 171 |   var PR_ATTRIB_VALUE = 'atv';
 172 | 
 173 |   /**
 174 |    * A class that indicates a section of markup that is not code, e.g. to allow
 175 |    * embedding of line numbers within code listings.
 176 |    */
 177 |   var PR_NOCODE = 'nocode';
 178 | 
 179 |   function isWordChar(ch) {
 180 |     return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
 181 |   }
 182 | 
 183 |   /** Splice one array into another.
 184 |     * Like the python <code>
 185 |     * container[containerPosition:containerPosition + countReplaced] = inserted
 186 |     * </code>
 187 |     * @param {Array} inserted
 188 |     * @param {Array} container modified in place
 189 |     * @param {Number} containerPosition
 190 |     * @param {Number} countReplaced
 191 |     */
 192 |   function spliceArrayInto(
 193 |       inserted, container, containerPosition, countReplaced) {
 194 |     inserted.unshift(containerPosition, countReplaced || 0);
 195 |     try {
 196 |       container.splice.apply(container, inserted);
 197 |     } finally {
 198 |       inserted.splice(0, 2);
 199 |     }
 200 |   }
 201 | 
 202 |   /** A set of tokens that can precede a regular expression literal in
 203 |     * javascript.
 204 |     * http://www.mozilla.org/js/language/js20/rationale/syntax.html has the full
 205 |     * list, but I've removed ones that might be problematic when seen in
 206 |     * languages that don't support regular expression literals.
 207 |     *
 208 |     * <p>Specifically, I've removed any keywords that can't precede a regexp
 209 |     * literal in a syntactically legal javascript program, and I've removed the
 210 |     * "in" keyword since it's not a keyword in many languages, and might be used
 211 |     * as a count of inches.
 212 |     * @private
 213 |     */
 214 |   var REGEXP_PRECEDER_PATTERN = function () {
 215 |       var preceders = [
 216 |           "!", "!=", "!==", "#", "%", "%=", "&", "&&", "&&=",
 217 |           "&=", "(", "*", "*=", /* "+", */ "+=", ",", /* "-", */ "-=",
 218 |           "->", /*".", "..", "...", handled below */ "/", "/=", ":", "::", ";",
 219 |           "<", "<<", "<<=", "<=", "=", "==", "===", ">",
 220 |           ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "[",
 221 |           "^", "^=", "^^", "^^=", "{", "|", "|=", "||",
 222 |           "||=", "~" /* handles =~ and !~ */,
 223 |           "break", "case", "continue", "delete",
 224 |           "do", "else", "finally", "instanceof",
 225 |           "return", "throw", "try", "typeof"
 226 |           ];
 227 |       var pattern = '(?:' +
 228 |           '(?:(?:^|[^0-9.])\\.{1,3})|' +  // a dot that's not part of a number
 229 |           '(?:(?:^|[^\\+])\\+)|' +  // allow + but not ++
 230 |           '(?:(?:^|[^\\-])-)';  // allow - but not --
 231 |       for (var i = 0; i < preceders.length; ++i) {
 232 |         var preceder = preceders[i];
 233 |         if (isWordChar(preceder.charAt(0))) {
 234 |           pattern += '|\\b' + preceder;
 235 |         } else {
 236 |           pattern += '|' + preceder.replace(/([^=<>:&])/g, '\\$1');
 237 |         }
 238 |       }
 239 |       pattern += '|^)\\s*$';  // matches at end, and matches empty string
 240 |       return new RegExp(pattern);
 241 |       // CAVEAT: this does not properly handle the case where a regular
 242 |       // expression immediately follows another since a regular expression may
 243 |       // have flags for case-sensitivity and the like.  Having regexp tokens
 244 |       // adjacent is not
 245 |       // valid in any language I'm aware of, so I'm punting.
 246 |       // TODO: maybe style special characters inside a regexp as punctuation.
 247 |     }();
 248 | 
 249 |   // Define regexps here so that the interpreter doesn't have to create an
 250 |   // object each time the function containing them is called.
 251 |   // The language spec requires a new object created even if you don't access
 252 |   // the $1 members.
 253 |   var pr_amp = /&/g;
 254 |   var pr_lt = /</g;
 255 |   var pr_gt = />/g;
 256 |   var pr_quot = /\"/g;
 257 |   /** like textToHtml but escapes double quotes to be attribute safe. */
 258 |   function attribToHtml(str) {
 259 |     return str.replace(pr_amp, '&amp;')
 260 |         .replace(pr_lt, '&lt;')
 261 |         .replace(pr_gt, '&gt;')
 262 |         .replace(pr_quot, '&quot;');
 263 |   }
 264 | 
 265 |   /** escapest html special characters to html. */
 266 |   function textToHtml(str) {
 267 |     return str.replace(pr_amp, '&amp;')
 268 |         .replace(pr_lt, '&lt;')
 269 |         .replace(pr_gt, '&gt;');
 270 |   }
 271 | 
 272 | 
 273 |   var pr_ltEnt = /&lt;/g;
 274 |   var pr_gtEnt = /&gt;/g;
 275 |   var pr_aposEnt = /&apos;/g;
 276 |   var pr_quotEnt = /&quot;/g;
 277 |   var pr_ampEnt = /&amp;/g;
 278 |   var pr_nbspEnt = /&nbsp;/g;
 279 |   /** unescapes html to plain text. */
 280 |   function htmlToText(html) {
 281 |     var pos = html.indexOf('&');
 282 |     if (pos < 0) { return html; }
 283 |     // Handle numeric entities specially.  We can't use functional substitution
 284 |     // since that doesn't work in older versions of Safari.
 285 |     // These should be rare since most browsers convert them to normal chars.
 286 |     for (--pos; (pos = html.indexOf('&#', pos + 1)) >= 0;) {
 287 |       var end = html.indexOf(';', pos);
 288 |       if (end >= 0) {
 289 |         var num = html.substring(pos + 3, end);
 290 |         var radix = 10;
 291 |         if (num && num.charAt(0) === 'x') {
 292 |           num = num.substring(1);
 293 |           radix = 16;
 294 |         }
 295 |         var codePoint = parseInt(num, radix);
 296 |         if (!isNaN(codePoint)) {
 297 |           html = (html.substring(0, pos) + String.fromCharCode(codePoint) +
 298 |                   html.substring(end + 1));
 299 |         }
 300 |       }
 301 |     }
 302 | 
 303 |     return html.replace(pr_ltEnt, '<')
 304 |         .replace(pr_gtEnt, '>')
 305 |         .replace(pr_aposEnt, "'")
 306 |         .replace(pr_quotEnt, '"')
 307 |         .replace(pr_ampEnt, '&')
 308 |         .replace(pr_nbspEnt, ' ');
 309 |   }
 310 | 
 311 |   /** is the given node's innerHTML normally unescaped? */
 312 |   function isRawContent(node) {
 313 |     return 'XMP' === node.tagName;
 314 |   }
 315 | 
 316 |   function normalizedHtml(node, out) {
 317 |     switch (node.nodeType) {
 318 |       case 1:  // an element
 319 |         var name = node.tagName.toLowerCase();
 320 |         out.push('<', name);
 321 |         for (var i = 0; i < node.attributes.length; ++i) {
 322 |           var attr = node.attributes[i];
 323 |           if (!attr.specified) { continue; }
 324 |           out.push(' ');
 325 |           normalizedHtml(attr, out);
 326 |         }
 327 |         out.push('>');
 328 |         for (var child = node.firstChild; child; child = child.nextSibling) {
 329 |           normalizedHtml(child, out);
 330 |         }
 331 |         if (node.firstChild || !/^(?:br|link|img)$/.test(name)) {
 332 |           out.push('<\/', name, '>');
 333 |         }
 334 |         break;
 335 |       case 2: // an attribute
 336 |         out.push(node.name.toLowerCase(), '="', attribToHtml(node.value), '"');
 337 |         break;
 338 |       case 3: case 4: // text
 339 |         out.push(textToHtml(node.nodeValue));
 340 |         break;
 341 |     }
 342 |   }
 343 | 
 344 |   var PR_innerHtmlWorks = null;
 345 |   function getInnerHtml(node) {
 346 |     // inner html is hopelessly broken in Safari 2.0.4 when the content is
 347 |     // an html description of well formed XML and the containing tag is a PRE
 348 |     // tag, so we detect that case and emulate innerHTML.
 349 |     if (null === PR_innerHtmlWorks) {
 350 |       var testNode = document.createElement('PRE');
 351 |       testNode.appendChild(
 352 |           document.createTextNode('<!DOCTYPE foo PUBLIC "foo bar">\n<foo />'));
 353 |       PR_innerHtmlWorks = !/</.test(testNode.innerHTML);
 354 |     }
 355 | 
 356 |     if (PR_innerHtmlWorks) {
 357 |       var content = node.innerHTML;
 358 |       // XMP tags contain unescaped entities so require special handling.
 359 |       if (isRawContent(node)) {
 360 |         content = textToHtml(content);
 361 |       }
 362 |       return content;
 363 |     }
 364 | 
 365 |     var out = [];
 366 |     for (var child = node.firstChild; child; child = child.nextSibling) {
 367 |       normalizedHtml(child, out);
 368 |     }
 369 |     return out.join('');
 370 |   }
 371 | 
 372 |   /** returns a function that expand tabs to spaces.  This function can be fed
 373 |     * successive chunks of text, and will maintain its own internal state to
 374 |     * keep track of how tabs are expanded.
 375 |     * @return {function (string) : string} a function that takes
 376 |     *   plain text and return the text with tabs expanded.
 377 |     * @private
 378 |     */
 379 |   function makeTabExpander(tabWidth) {
 380 |     var SPACES = '                ';
 381 |     var charInLine = 0;
 382 | 
 383 |     return function (plainText) {
 384 |       // walk over each character looking for tabs and newlines.
 385 |       // On tabs, expand them.  On newlines, reset charInLine.
 386 |       // Otherwise increment charInLine
 387 |       var out = null;
 388 |       var pos = 0;
 389 |       for (var i = 0, n = plainText.length; i < n; ++i) {
 390 |         var ch = plainText.charAt(i);
 391 | 
 392 |         switch (ch) {
 393 |           case '\t':
 394 |             if (!out) { out = []; }
 395 |             out.push(plainText.substring(pos, i));
 396 |             // calculate how much space we need in front of this part
 397 |             // nSpaces is the amount of padding -- the number of spaces needed
 398 |             // to move us to the next column, where columns occur at factors of
 399 |             // tabWidth.
 400 |             var nSpaces = tabWidth - (charInLine % tabWidth);
 401 |             charInLine += nSpaces;
 402 |             for (; nSpaces >= 0; nSpaces -= SPACES.length) {
 403 |               out.push(SPACES.substring(0, nSpaces));
 404 |             }
 405 |             pos = i + 1;
 406 |             break;
 407 |           case '\n':
 408 |             charInLine = 0;
 409 |             break;
 410 |           default:
 411 |             ++charInLine;
 412 |         }
 413 |       }
 414 |       if (!out) { return plainText; }
 415 |       out.push(plainText.substring(pos));
 416 |       return out.join('');
 417 |     };
 418 |   }
 419 | 
 420 |   // The below pattern matches one of the following
 421 |   // (1) /[^<]+/ : A run of characters other than '<'
 422 |   // (2) /<!--.*?-->/: an HTML comment
 423 |   // (3) /<!\[CDATA\[.*?\]\]>/: a cdata section
 424 |   // (3) /<\/?[a-zA-Z][^>]*>/ : A probably tag that should not be highlighted
 425 |   // (4) /</ : A '<' that does not begin a larger chunk.  Treated as 1
 426 |   var pr_chunkPattern =
 427 |   /(?:[^<]+|<!--[\s\S]*?-->|<!\[CDATA\[([\s\S]*?)\]\]>|<\/?[a-zA-Z][^>]*>|<)/g;
 428 |   var pr_commentPrefix = /^<!--/;
 429 |   var pr_cdataPrefix = /^<\[CDATA\[/;
 430 |   var pr_brPrefix = /^<br\b/i;
 431 |   var pr_tagNameRe = /^<(\/?)([a-zA-Z]+)/;
 432 | 
 433 |   /** split markup into chunks of html tags (style null) and
 434 |     * plain text (style {@link #PR_PLAIN}), converting tags which are
 435 |     * significant for tokenization (<br>) into their textual equivalent.
 436 |     *
 437 |     * @param {string} s html where whitespace is considered significant.
 438 |     * @return {Object} source code and extracted tags.
 439 |     * @private
 440 |     */
 441 |   function extractTags(s) {
 442 |     // since the pattern has the 'g' modifier and defines no capturing groups,
 443 |     // this will return a list of all chunks which we then classify and wrap as
 444 |     // PR_Tokens
 445 |     var matches = s.match(pr_chunkPattern);
 446 |     var sourceBuf = [];
 447 |     var sourceBufLen = 0;
 448 |     var extractedTags = [];
 449 |     if (matches) {
 450 |       for (var i = 0, n = matches.length; i < n; ++i) {
 451 |         var match = matches[i];
 452 |         if (match.length > 1 && match.charAt(0) === '<') {
 453 |           if (pr_commentPrefix.test(match)) { continue; }
 454 |           if (pr_cdataPrefix.test(match)) {
 455 |             // strip CDATA prefix and suffix.  Don't unescape since it's CDATA
 456 |             sourceBuf.push(match.substring(9, match.length - 3));
 457 |             sourceBufLen += match.length - 12;
 458 |           } else if (pr_brPrefix.test(match)) {
 459 |             // <br> tags are lexically significant so convert them to text.
 460 |             // This is undone later.
 461 |             sourceBuf.push('\n');
 462 |             ++sourceBufLen;
 463 |           } else {
 464 |             if (match.indexOf(PR_NOCODE) >= 0 && isNoCodeTag(match)) {
 465 |               // A <span class="nocode"> will start a section that should be
 466 |               // ignored.  Continue walking the list until we see a matching end
 467 |               // tag.
 468 |               var name = match.match(pr_tagNameRe)[2];
 469 |               var depth = 1;
 470 |               end_tag_loop:
 471 |               for (var j = i + 1; j < n; ++j) {
 472 |                 var name2 = matches[j].match(pr_tagNameRe);
 473 |                 if (name2 && name2[2] === name) {
 474 |                   if (name2[1] === '/') {
 475 |                     if (--depth === 0) { break end_tag_loop; }
 476 |                   } else {
 477 |                     ++depth;
 478 |                   }
 479 |                 }
 480 |               }
 481 |               if (j < n) {
 482 |                 extractedTags.push(
 483 |                     sourceBufLen, matches.slice(i, j + 1).join(''));
 484 |                 i = j;
 485 |               } else {  // Ignore unclosed sections.
 486 |                 extractedTags.push(sourceBufLen, match);
 487 |               }
 488 |             } else {
 489 |               extractedTags.push(sourceBufLen, match);
 490 |             }
 491 |           }
 492 |         } else {
 493 |           var literalText = htmlToText(match);
 494 |           sourceBuf.push(literalText);
 495 |           sourceBufLen += literalText.length;
 496 |         }
 497 |       }
 498 |     }
 499 |     return { source: sourceBuf.join(''), tags: extractedTags };
 500 |   }
 501 | 
 502 |   /** True if the given tag contains a class attribute with the nocode class. */
 503 |   function isNoCodeTag(tag) {
 504 |     return !!tag
 505 |         // First canonicalize the representation of attributes
 506 |         .replace(/\s(\w+)\s*=\s*(?:\"([^\"]*)\"|'([^\']*)'|(\S+))/g,
 507 |                  ' $1="$2$3$4"')
 508 |         // Then look for the attribute we want.
 509 |         .match(/[cC][lL][aA][sS][sS]=\"[^\"]*\bnocode\b/);
 510 |   }
 511 | 
 512 |   /** Given triples of [style, pattern, context] returns a lexing function,
 513 |     * The lexing function interprets the patterns to find token boundaries and
 514 |     * returns a decoration list of the form
 515 |     * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
 516 |     * where index_n is an index into the sourceCode, and style_n is a style
 517 |     * constant like PR_PLAIN.  index_n-1 <= index_n, and style_n-1 applies to
 518 |     * all characters in sourceCode[index_n-1:index_n].
 519 |     *
 520 |     * The stylePatterns is a list whose elements have the form
 521 |     * [style : string, pattern : RegExp, context : RegExp, shortcut : string].
 522 |     &
 523 |     * Style is a style constant like PR_PLAIN.
 524 |     *
 525 |     * Pattern must only match prefixes, and if it matches a prefix and context
 526 |     * is null or matches the last non-comment token parsed, then that match is
 527 |     * considered a token with the same style.
 528 |     *
 529 |     * Context is applied to the last non-whitespace, non-comment token
 530 |     * recognized.
 531 |     *
 532 |     * Shortcut is an optional string of characters, any of which, if the first
 533 |     * character, gurantee that this pattern and only this pattern matches.
 534 |     *
 535 |     * @param {Array} shortcutStylePatterns patterns that always start with
 536 |     *   a known character.  Must have a shortcut string.
 537 |     * @param {Array} fallthroughStylePatterns patterns that will be tried in
 538 |     *   order if the shortcut ones fail.  May have shortcuts.
 539 |     *
 540 |     * @return {function (string, number?) : Array.<number|string>} a
 541 |     *   function that takes source code and returns a list of decorations.
 542 |     */
 543 |   function createSimpleLexer(shortcutStylePatterns,
 544 |                              fallthroughStylePatterns) {
 545 |     var shortcuts = {};
 546 |     (function () {
 547 |       var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
 548 |       for (var i = allPatterns.length; --i >= 0;) {
 549 |         var patternParts = allPatterns[i];
 550 |         var shortcutChars = patternParts[3];
 551 |         if (shortcutChars) {
 552 |           for (var c = shortcutChars.length; --c >= 0;) {
 553 |             shortcuts[shortcutChars.charAt(c)] = patternParts;
 554 |           }
 555 |         }
 556 |       }
 557 |     })();
 558 | 
 559 |     var nPatterns = fallthroughStylePatterns.length;
 560 |     var notWs = /\S/;
 561 | 
 562 |     return function (sourceCode, opt_basePos) {
 563 |       opt_basePos = opt_basePos || 0;
 564 |       var decorations = [opt_basePos, PR_PLAIN];
 565 |       var lastToken = '';
 566 |       var pos = 0;  // index into sourceCode
 567 |       var tail = sourceCode;
 568 | 
 569 |       while (tail.length) {
 570 |         var style;
 571 |         var token = null;
 572 |         var match;
 573 | 
 574 |         var patternParts = shortcuts[tail.charAt(0)];
 575 |         if (patternParts) {
 576 |           match = tail.match(patternParts[1]);
 577 |           token = match[0];
 578 |           style = patternParts[0];
 579 |         } else {
 580 |           for (var i = 0; i < nPatterns; ++i) {
 581 |             patternParts = fallthroughStylePatterns[i];
 582 |             var contextPattern = patternParts[2];
 583 |             if (contextPattern && !contextPattern.test(lastToken)) {
 584 |               // rule can't be used
 585 |               continue;
 586 |             }
 587 |             match = tail.match(patternParts[1]);
 588 |             if (match) {
 589 |               token = match[0];
 590 |               style = patternParts[0];
 591 |               break;
 592 |             }
 593 |           }
 594 | 
 595 |           if (!token) {  // make sure that we make progress
 596 |             style = PR_PLAIN;
 597 |             token = tail.substring(0, 1);
 598 |           }
 599 |         }
 600 | 
 601 |         decorations.push(opt_basePos + pos, style);
 602 |         pos += token.length;
 603 |         tail = tail.substring(token.length);
 604 |         if (style !== PR_COMMENT && notWs.test(token)) { lastToken = token; }
 605 |       }
 606 |       return decorations;
 607 |     };
 608 |   }
 609 | 
 610 |   var PR_MARKUP_LEXER = createSimpleLexer([], [
 611 |       [PR_PLAIN,       /^[^<]+/, null],
 612 |       [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/, null],
 613 |       [PR_COMMENT,     /^<!--[\s\S]*?(?:-->|$)/, null],
 614 |       [PR_SOURCE,      /^<\?[\s\S]*?(?:\?>|$)/, null],
 615 |       [PR_SOURCE,      /^<%[\s\S]*?(?:%>|$)/, null],
 616 |       [PR_SOURCE,
 617 |        // Tags whose content is not escaped, and which contain source code.
 618 |        /^<(script|style|xmp)\b[^>]*>[\s\S]*?<\/\1\b[^>]*>/i, null],
 619 |       [PR_TAG,         /^<\/?\w[^<>]*>/, null]
 620 |       ]);
 621 |   // Splits any of the source|style|xmp entries above into a start tag,
 622 |   // source content, and end tag.
 623 |   var PR_SOURCE_CHUNK_PARTS = /^(<[^>]*>)([\s\S]*)(<\/[^>]*>)$/;
 624 |   /** split markup on tags, comments, application directives, and other top
 625 |     * level constructs.  Tags are returned as a single token - attributes are
 626 |     * not yet broken out.
 627 |     * @private
 628 |     */
 629 |   function tokenizeMarkup(source) {
 630 |     var decorations = PR_MARKUP_LEXER(source);
 631 |     for (var i = 0; i < decorations.length; i += 2) {
 632 |       if (decorations[i + 1] === PR_SOURCE) {
 633 |         var start, end;
 634 |         start = decorations[i];
 635 |         end = i + 2 < decorations.length ? decorations[i + 2] : source.length;
 636 |         // Split out start and end script tags as actual tags, and leave the
 637 |         // body with style SCRIPT.
 638 |         var sourceChunk = source.substring(start, end);
 639 |         var match = sourceChunk.match(PR_SOURCE_CHUNK_PARTS);
 640 |         if (match) {
 641 |           decorations.splice(
 642 |               i, 2,
 643 |               start, PR_TAG,  // the open chunk
 644 |               start + match[1].length, PR_SOURCE,
 645 |               start + match[1].length + (match[2] || '').length, PR_TAG);
 646 |         }
 647 |       }
 648 |     }
 649 |     return decorations;
 650 |   }
 651 | 
 652 |   var PR_TAG_LEXER = createSimpleLexer([
 653 |       [PR_ATTRIB_VALUE, /^\'[^\']*(?:\'|$)/, null, "'"],
 654 |       [PR_ATTRIB_VALUE, /^\"[^\"]*(?:\"|$)/, null, '"'],
 655 |       [PR_PUNCTUATION,  /^[<>\/=]+/, null, '<>/=']
 656 |       ], [
 657 |       [PR_TAG,          /^[\w:\-]+/, /^</],
 658 |       [PR_ATTRIB_VALUE, /^[\w\-]+/, /^=/],
 659 |       [PR_ATTRIB_NAME,  /^[\w:\-]+/, null],
 660 |       [PR_PLAIN,        /^\s+/, null, ' \t\r\n']
 661 |       ]);
 662 |   /** split tags attributes and their values out from the tag name, and
 663 |     * recursively lex source chunks.
 664 |     * @private
 665 |     */
 666 |   function splitTagAttributes(source, decorations) {
 667 |     for (var i = 0; i < decorations.length; i += 2) {
 668 |       var style = decorations[i + 1];
 669 |       if (style === PR_TAG) {
 670 |         var start, end;
 671 |         start = decorations[i];
 672 |         end = i + 2 < decorations.length ? decorations[i + 2] : source.length;
 673 |         var chunk = source.substring(start, end);
 674 |         var subDecorations = PR_TAG_LEXER(chunk, start);
 675 |         spliceArrayInto(subDecorations, decorations, i, 2);
 676 |         i += subDecorations.length - 2;
 677 |       }
 678 |     }
 679 |     return decorations;
 680 |   }
 681 | 
 682 |   /** returns a function that produces a list of decorations from source text.
 683 |     *
 684 |     * This code treats ", ', and ` as string delimiters, and \ as a string
 685 |     * escape.  It does not recognize perl's qq() style strings.
 686 |     * It has no special handling for double delimiter escapes as in basic, or
 687 |     * the tripled delimiters used in python, but should work on those regardless
 688 |     * although in those cases a single string literal may be broken up into
 689 |     * multiple adjacent string literals.
 690 |     *
 691 |     * It recognizes C, C++, and shell style comments.
 692 |     *
 693 |     * @param {Object} options a set of optional parameters.
 694 |     * @return {function (string) : Array.<string|number>} a
 695 |     *     decorator that takes sourceCode as plain text and that returns a
 696 |     *     decoration list
 697 |     */
 698 |   function sourceDecorator(options) {
 699 |     var shortcutStylePatterns = [], fallthroughStylePatterns = [];
 700 |     if (options.tripleQuotedStrings) {
 701 |       // '''multi-line-string''', 'single-line-string', and double-quoted
 702 |       shortcutStylePatterns.push(
 703 |           [PR_STRING,  /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
 704 |            null, '\'"']);
 705 |     } else if (options.multiLineStrings) {
 706 |       // 'multi-line-string', "multi-line-string"
 707 |       shortcutStylePatterns.push(
 708 |           [PR_STRING,  /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
 709 |            null, '\'"`']);
 710 |     } else {
 711 |       // 'single-line-string', "single-line-string"
 712 |       shortcutStylePatterns.push(
 713 |           [PR_STRING,
 714 |            /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
 715 |            null, '"\'']);
 716 |     }
 717 |     fallthroughStylePatterns.push(
 718 |         [PR_PLAIN,   /^(?:[^\'\"\`\/\#]+)/, null, ' \r\n']);
 719 |     if (options.hashComments) {
 720 |       shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
 721 |     }
 722 |     if (options.cStyleComments) {
 723 |       fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
 724 |       fallthroughStylePatterns.push(
 725 |           [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
 726 |     }
 727 |     if (options.regexLiterals) {
 728 |       var REGEX_LITERAL = (
 729 |           // A regular expression literal starts with a slash that is
 730 |           // not followed by * or / so that it is not confused with
 731 |           // comments.
 732 |           '^/(?=[^/*])'
 733 |           // and then contains any number of raw characters,
 734 |           + '(?:[^/\\x5B\\x5C]'
 735 |           // escape sequences (\x5C),
 736 |           +    '|\\x5C[\\s\\S]'
 737 |           // or non-nesting character sets (\x5B\x5D);
 738 |           +    '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+'
 739 |           // finally closed by a /.
 740 |           + '(?:/|$)');
 741 |       fallthroughStylePatterns.push(
 742 |           [PR_STRING, new RegExp(REGEX_LITERAL), REGEXP_PRECEDER_PATTERN]);
 743 |     }
 744 | 
 745 |     var keywords = wordSet(options.keywords);
 746 | 
 747 |     options = null;
 748 | 
 749 |     /** splits the given string into comment, string, and "other" tokens.
 750 |       * @param {string} sourceCode as plain text
 751 |       * @return {Array.<number|string>} a decoration list.
 752 |       * @private
 753 |       */
 754 |     var splitStringAndCommentTokens = createSimpleLexer(
 755 |         shortcutStylePatterns, fallthroughStylePatterns);
 756 | 
 757 |     var styleLiteralIdentifierPuncRecognizer = createSimpleLexer([], [
 758 |         [PR_PLAIN,       /^\s+/, null, ' \r\n'],
 759 |         // TODO(mikesamuel): recognize non-latin letters and numerals in idents
 760 |         [PR_PLAIN,       /^[a-z_$@][a-z_$@0-9]*/i, null],
 761 |         // A hex number
 762 |         [PR_LITERAL,     /^0x[a-f0-9]+[a-z]/i, null],
 763 |         // An octal or decimal number, possibly in scientific notation
 764 |         [PR_LITERAL,
 765 |          /^(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d+)(?:e[+\-]?\d+)?[a-z]*/i,
 766 |          null, '123456789'],
 767 |         [PR_PUNCTUATION, /^[^\s\w\.$@]+/, null]
 768 |         // Fallback will handle decimal points not adjacent to a digit
 769 |       ]);
 770 | 
 771 |     /** splits plain text tokens into more specific tokens, and then tries to
 772 |       * recognize keywords, and types.
 773 |       * @private
 774 |       */
 775 |     function splitNonStringNonCommentTokens(source, decorations) {
 776 |       for (var i = 0; i < decorations.length; i += 2) {
 777 |         var style = decorations[i + 1];
 778 |         if (style === PR_PLAIN) {
 779 |           var start, end, chunk, subDecs;
 780 |           start = decorations[i];
 781 |           end = i + 2 < decorations.length ? decorations[i + 2] : source.length;
 782 |           chunk = source.substring(start, end);
 783 |           subDecs = styleLiteralIdentifierPuncRecognizer(chunk, start);
 784 |           for (var j = 0, m = subDecs.length; j < m; j += 2) {
 785 |             var subStyle = subDecs[j + 1];
 786 |             if (subStyle === PR_PLAIN) {
 787 |               var subStart = subDecs[j];
 788 |               var subEnd = j + 2 < m ? subDecs[j + 2] : chunk.length;
 789 |               var token = source.substring(subStart, subEnd);
 790 |               if (token === '.') {
 791 |                 subDecs[j + 1] = PR_PUNCTUATION;
 792 |               } else if (token in keywords) {
 793 |                 subDecs[j + 1] = PR_KEYWORD;
 794 |               } else if (/^@?[A-Z][A-Z$]*[a-z][A-Za-z$]*$/.test(token)) {
 795 |                 // classify types and annotations using Java's style conventions
 796 |                 subDecs[j + 1] = token.charAt(0) === '@' ? PR_LITERAL : PR_TYPE;
 797 |               }
 798 |             }
 799 |           }
 800 |           spliceArrayInto(subDecs, decorations, i, 2);
 801 |           i += subDecs.length - 2;
 802 |         }
 803 |       }
 804 |       return decorations;
 805 |     }
 806 | 
 807 |     return function (sourceCode) {
 808 |       // Split into strings, comments, and other.
 809 |       // We do this because strings and comments are easily recognizable and can
 810 |       // contain stuff that looks like other tokens, so we want to mark those
 811 |       // early so we don't recurse into them.
 812 |       var decorations = splitStringAndCommentTokens(sourceCode);
 813 | 
 814 |       // Split non comment|string tokens on whitespace and word boundaries
 815 |       decorations = splitNonStringNonCommentTokens(sourceCode, decorations);
 816 | 
 817 |       return decorations;
 818 |     };
 819 |   }
 820 | 
 821 |   var decorateSource = sourceDecorator({
 822 |         keywords: ALL_KEYWORDS,
 823 |         hashComments: true,
 824 |         cStyleComments: true,
 825 |         multiLineStrings: true,
 826 |         regexLiterals: true
 827 |       });
 828 | 
 829 |   /** identify regions of markup that are really source code, and recursivley
 830 |     * lex them.
 831 |     * @private
 832 |     */
 833 |   function splitSourceNodes(source, decorations) {
 834 |     for (var i = 0; i < decorations.length; i += 2) {
 835 |       var style = decorations[i + 1];
 836 |       if (style === PR_SOURCE) {
 837 |         // Recurse using the non-markup lexer
 838 |         var start, end;
 839 |         start = decorations[i];
 840 |         end = i + 2 < decorations.length ? decorations[i + 2] : source.length;
 841 |         var subDecorations = decorateSource(source.substring(start, end));
 842 |         for (var j = 0, m = subDecorations.length; j < m; j += 2) {
 843 |           subDecorations[j] += start;
 844 |         }
 845 |         spliceArrayInto(subDecorations, decorations, i, 2);
 846 |         i += subDecorations.length - 2;
 847 |       }
 848 |     }
 849 |     return decorations;
 850 |   }
 851 | 
 852 |   /** identify attribute values that really contain source code and recursively
 853 |     * lex them.
 854 |     * @private
 855 |     */
 856 |   function splitSourceAttributes(source, decorations) {
 857 |     var nextValueIsSource = false;
 858 |     for (var i = 0; i < decorations.length; i += 2) {
 859 |       var style = decorations[i + 1];
 860 |       var start, end;
 861 |       if (style === PR_ATTRIB_NAME) {
 862 |         start = decorations[i];
 863 |         end = i + 2 < decorations.length ? decorations[i + 2] : source.length;
 864 |         nextValueIsSource = /^on|^style$/i.test(source.substring(start, end));
 865 |       } else if (style === PR_ATTRIB_VALUE) {
 866 |         if (nextValueIsSource) {
 867 |           start = decorations[i];
 868 |           end = i + 2 < decorations.length ? decorations[i + 2] : source.length;
 869 |           var attribValue = source.substring(start, end);
 870 |           var attribLen = attribValue.length;
 871 |           var quoted =
 872 |               (attribLen >= 2 && /^[\"\']/.test(attribValue) &&
 873 |                attribValue.charAt(0) === attribValue.charAt(attribLen - 1));
 874 | 
 875 |           var attribSource;
 876 |           var attribSourceStart;
 877 |           var attribSourceEnd;
 878 |           if (quoted) {
 879 |             attribSourceStart = start + 1;
 880 |             attribSourceEnd = end - 1;
 881 |             attribSource = attribValue;
 882 |           } else {
 883 |             attribSourceStart = start + 1;
 884 |             attribSourceEnd = end - 1;
 885 |             attribSource = attribValue.substring(1, attribValue.length - 1);
 886 |           }
 887 | 
 888 |           var attribSourceDecorations = decorateSource(attribSource);
 889 |           for (var j = 0, m = attribSourceDecorations.length; j < m; j += 2) {
 890 |             attribSourceDecorations[j] += attribSourceStart;
 891 |           }
 892 | 
 893 |           if (quoted) {
 894 |             attribSourceDecorations.push(attribSourceEnd, PR_ATTRIB_VALUE);
 895 |             spliceArrayInto(attribSourceDecorations, decorations, i + 2, 0);
 896 |           } else {
 897 |             spliceArrayInto(attribSourceDecorations, decorations, i, 2);
 898 |           }
 899 |         }
 900 |         nextValueIsSource = false;
 901 |       }
 902 |     }
 903 |     return decorations;
 904 |   }
 905 | 
 906 |   /** returns a decoration list given a string of markup.
 907 |     *
 908 |     * This code recognizes a number of constructs.
 909 |     * <!-- ... --> comment
 910 |     * <!\w ... >   declaration
 911 |     * <\w ... >    tag
 912 |     * </\w ... >   tag
 913 |     * <?...?>      embedded source
 914 |     * <%...%>      embedded source
 915 |     * &[#\w]...;   entity
 916 |     *
 917 |     * It does not recognizes %foo; doctype entities from  .
 918 |     *
 919 |     * It will recurse into any <style>, <script>, and on* attributes using
 920 |     * PR_lexSource.
 921 |     */
 922 |   function decorateMarkup(sourceCode) {
 923 |     // This function works as follows:
 924 |     // 1) Start by splitting the markup into text and tag chunks
 925 |     //    Input:  string s
 926 |     //    Output: List<PR_Token> where style in (PR_PLAIN, null)
 927 |     // 2) Then split the text chunks further into comments, declarations,
 928 |     //    tags, etc.
 929 |     //    After each split, consider whether the token is the start of an
 930 |     //    embedded source section, i.e. is an open <script> tag.  If it is, find
 931 |     //    the corresponding close token, and don't bother to lex in between.
 932 |     //    Input:  List<string>
 933 |     //    Output: List<PR_Token> with style in
 934 |     //            (PR_TAG, PR_PLAIN, PR_SOURCE, null)
 935 |     // 3) Finally go over each tag token and split out attribute names and
 936 |     //    values.
 937 |     //    Input:  List<PR_Token>
 938 |     //    Output: List<PR_Token> where style in
 939 |     //            (PR_TAG, PR_PLAIN, PR_SOURCE, NAME, VALUE, null)
 940 |     var decorations = tokenizeMarkup(sourceCode);
 941 |     decorations = splitTagAttributes(sourceCode, decorations);
 942 |     decorations = splitSourceNodes(sourceCode, decorations);
 943 |     decorations = splitSourceAttributes(sourceCode, decorations);
 944 |     return decorations;
 945 |   }
 946 | 
 947 |   /**
 948 |     * @param {string} sourceText plain text
 949 |     * @param {Array.<number|string>} extractedTags chunks of raw html preceded
 950 |     *   by their position in sourceText in order.
 951 |     * @param {Array.<number|string>} decorations style classes preceded by their
 952 |     *   position in sourceText in order.
 953 |     * @return {string} html
 954 |     * @private
 955 |     */
 956 |   function recombineTagsAndDecorations(sourceText, extractedTags, decorations) {
 957 |     var html = [];
 958 |     // index past the last char in sourceText written to html
 959 |     var outputIdx = 0;
 960 | 
 961 |     var openDecoration = null;
 962 |     var currentDecoration = null;
 963 |     var tagPos = 0;  // index into extractedTags
 964 |     var decPos = 0;  // index into decorations
 965 |     var tabExpander = makeTabExpander(PR_TAB_WIDTH);
 966 | 
 967 |     var adjacentSpaceRe = /([\r\n ]) /g;
 968 |     var startOrSpaceRe = /(^| ) /gm;
 969 |     var newlineRe = /\r\n?|\n/g;
 970 |     var trailingSpaceRe = /[ \r\n]$/;
 971 |     var lastWasSpace = true;  // the last text chunk emitted ended with a space.
 972 |     
 973 |     // A helper function that is responsible for opening sections of decoration
 974 |     // and outputing properly escaped chunks of source
 975 |     function emitTextUpTo(sourceIdx) {
 976 |       if (sourceIdx > outputIdx) {
 977 |         if (openDecoration && openDecoration !== currentDecoration) {
 978 |           // Close the current decoration
 979 |           html.push('</span>');
 980 |           openDecoration = null;
 981 |         }
 982 |         if (!openDecoration && currentDecoration) {
 983 |           openDecoration = currentDecoration;
 984 |           html.push('<span class="', openDecoration, '">');
 985 |         }
 986 |         // This interacts badly with some wikis which introduces paragraph tags
 987 |         // into pre blocks for some strange reason.
 988 |         // It's necessary for IE though which seems to lose the preformattedness
 989 |         // of <pre> tags when their innerHTML is assigned.
 990 |         // http://stud3.tuwien.ac.at/~e0226430/innerHtmlQuirk.html
 991 |         // and it serves to undo the conversion of <br>s to newlines done in
 992 |         // chunkify.
 993 |         var htmlChunk = textToHtml(
 994 |             tabExpander(sourceText.substring(outputIdx, sourceIdx)))
 995 |             .replace(lastWasSpace
 996 |                      ? startOrSpaceRe
 997 |                      : adjacentSpaceRe, '$1&nbsp;');
 998 |         // Keep track of whether we need to escape space at the beginning of the
 999 |         // next chunk.
1000 |         lastWasSpace = trailingSpaceRe.test(htmlChunk);
1001 |         html.push(htmlChunk.replace(newlineRe, '<br />'));
1002 |         outputIdx = sourceIdx;
1003 |       }
1004 |     }
1005 | 
1006 |     while (true) {
1007 |       // Determine if we're going to consume a tag this time around.  Otherwise
1008 |       // we consume a decoration or exit.
1009 |       var outputTag;
1010 |       if (tagPos < extractedTags.length) {
1011 |         if (decPos < decorations.length) {
1012 |           // Pick one giving preference to extractedTags since we shouldn't open
1013 |           // a new style that we're going to have to immediately close in order
1014 |           // to output a tag.
1015 |           outputTag = extractedTags[tagPos] <= decorations[decPos];
1016 |         } else {
1017 |           outputTag = true;
1018 |         }
1019 |       } else {
1020 |         outputTag = false;
1021 |       }
1022 |       // Consume either a decoration or a tag or exit.
1023 |       if (outputTag) {
1024 |         emitTextUpTo(extractedTags[tagPos]);
1025 |         if (openDecoration) {
1026 |           // Close the current decoration
1027 |           html.push('</span>');
1028 |           openDecoration = null;
1029 |         }
1030 |         html.push(extractedTags[tagPos + 1]);
1031 |         tagPos += 2;
1032 |       } else if (decPos < decorations.length) {
1033 |         emitTextUpTo(decorations[decPos]);
1034 |         currentDecoration = decorations[decPos + 1];
1035 |         decPos += 2;
1036 |       } else {
1037 |         break;
1038 |       }
1039 |     }
1040 |     emitTextUpTo(sourceText.length);
1041 |     if (openDecoration) {
1042 |       html.push('</span>');
1043 |     }
1044 | 
1045 |     return html.join('');
1046 |   }
1047 | 
1048 |   /** Maps language-specific file extensions to handlers. */
1049 |   var langHandlerRegistry = {};
1050 |   /** Register a language handler for the given file extensions.
1051 |     * @param {function (string) : Array.<number|string>} handler
1052 |     *     a function from source code to a list of decorations.
1053 |     * @param {Array.<string>} fileExtensions
1054 |     */
1055 |   function registerLangHandler(handler, fileExtensions) {
1056 |     for (var i = fileExtensions.length; --i >= 0;) {
1057 |       var ext = fileExtensions[i];
1058 |       if (!langHandlerRegistry.hasOwnProperty(ext)) {
1059 |         langHandlerRegistry[ext] = handler;
1060 |       } else if ('console' in window) {
1061 |         console.log('cannot override language handler %s', ext);
1062 |       }
1063 |     }
1064 |   }
1065 |   registerLangHandler(decorateSource, ['default-code']);
1066 |   registerLangHandler(decorateMarkup,
1067 |                       ['default-markup', 'html', 'htm', 'xhtml', 'xml', 'xsl']);
1068 |   registerLangHandler(sourceDecorator({
1069 |           keywords: CPP_KEYWORDS,
1070 |           hashComments: true,
1071 |           cStyleComments: true
1072 |         }), ['c', 'cc', 'cpp', 'cxx', 'cyc']);
1073 |   registerLangHandler(sourceDecorator({
1074 |           keywords: CSHARP_KEYWORDS,
1075 |           hashComments: true,
1076 |           cStyleComments: true
1077 |         }), ['cs']);
1078 |   registerLangHandler(sourceDecorator({
1079 |           keywords: JAVA_KEYWORDS,
1080 |           cStyleComments: true
1081 |         }), ['java']);
1082 |   registerLangHandler(sourceDecorator({
1083 |           keywords: SH_KEYWORDS,
1084 |           hashComments: true,
1085 |           multiLineStrings: true
1086 |         }), ['bsh', 'csh', 'sh']);
1087 |   registerLangHandler(sourceDecorator({
1088 |           keywords: PYTHON_KEYWORDS,
1089 |           hashComments: true,
1090 |           multiLineStrings: true,
1091 |           tripleQuotedStrings: true
1092 |         }), ['cv', 'py']);
1093 |   registerLangHandler(sourceDecorator({
1094 |           keywords: PERL_KEYWORDS,
1095 |           hashComments: true,
1096 |           multiLineStrings: true,
1097 |           regexLiterals: true
1098 |         }), ['perl', 'pl', 'pm']);
1099 |   registerLangHandler(sourceDecorator({
1100 |           keywords: RUBY_KEYWORDS,
1101 |           hashComments: true,
1102 |           multiLineStrings: true,
1103 |           regexLiterals: true
1104 |         }), ['rb']);
1105 |   registerLangHandler(sourceDecorator({
1106 |           keywords: JSCRIPT_KEYWORDS,
1107 |           cStyleComments: true,
1108 |           regexLiterals: true
1109 |         }), ['js']);
1110 | 
1111 |   function prettyPrintOne(sourceCodeHtml, opt_langExtension) {
1112 |     try {
1113 |       // Extract tags, and convert the source code to plain text.
1114 |       var sourceAndExtractedTags = extractTags(sourceCodeHtml);
1115 |       /** Plain text. @type {string} */
1116 |       var source = sourceAndExtractedTags.source;
1117 | 
1118 |       /** Even entries are positions in source in ascending order.  Odd entries
1119 |         * are tags that were extracted at that position.
1120 |         * @type {Array.<number|string>}
1121 |         */
1122 |       var extractedTags = sourceAndExtractedTags.tags;
1123 | 
1124 |       // Pick a lexer and apply it.
1125 |       if (!langHandlerRegistry.hasOwnProperty(opt_langExtension)) {
1126 |         // Treat it as markup if the first non whitespace character is a < and
1127 |         // the last non-whitespace character is a >.
1128 |         opt_langExtension =
1129 |             /^\s*</.test(source) ? 'default-markup' : 'default-code';
1130 |       }
1131 | 
1132 |       /** Even entries are positions in source in ascending order.  Odd enties
1133 |         * are style markers (e.g., PR_COMMENT) that run from that position until
1134 |         * the end.
1135 |         * @type {Array.<number|string>}
1136 |         */
1137 |       var decorations = langHandlerRegistry[opt_langExtension].call({}, source);
1138 | 
1139 |       // Integrate the decorations and tags back into the source code to produce
1140 |       // a decorated html string.
1141 |       return recombineTagsAndDecorations(source, extractedTags, decorations);
1142 |     } catch (e) {
1143 |       if ('console' in window) {
1144 |         console.log(e);
1145 |         console.trace();
1146 |       }
1147 |       return sourceCodeHtml;
1148 |     }
1149 |   }
1150 | 
1151 |   function prettyPrint(opt_whenDone) {
1152 |     var isIE6 = _pr_isIE6();
1153 | 
1154 |     // fetch a list of nodes to rewrite
1155 |     var codeSegments = [
1156 |         document.getElementsByTagName('pre'),
1157 |         document.getElementsByTagName('code'),
1158 |         document.getElementsByTagName('xmp') ];
1159 |     var elements = [];
1160 |     for (var i = 0; i < codeSegments.length; ++i) {
1161 |       for (var j = 0; j < codeSegments[i].length; ++j) {
1162 |         elements.push(codeSegments[i][j]);
1163 |       }
1164 |     }
1165 |     codeSegments = null;
1166 | 
1167 |     // the loop is broken into a series of continuations to make sure that we
1168 |     // don't make the browser unresponsive when rewriting a large page.
1169 |     var k = 0;
1170 | 
1171 |     function doWork() {
1172 |       var endTime = (PR_SHOULD_USE_CONTINUATION ?
1173 |                      new Date().getTime() + 250 /* ms */ :
1174 |                      Infinity);
1175 |       for (; k < elements.length && new Date().getTime() < endTime; k++) {
1176 |         var cs = elements[k];
1177 |         if (cs.className && cs.className.indexOf('prettyprint') >= 0) {
1178 |           // If the classes includes a language extensions, use it.
1179 |           // Language extensions can be specified like
1180 |           //     <pre class="prettyprint lang-cpp">
1181 |           // the language extension "cpp" is used to find a language handler as
1182 |           // passed to PR_registerLangHandler.
1183 |           var langExtension = cs.className.match(/\blang-(\w+)\b/);
1184 |           if (langExtension) { langExtension = langExtension[1]; }
1185 | 
1186 |           // make sure this is not nested in an already prettified element
1187 |           var nested = false;
1188 |           for (var p = cs.parentNode; p; p = p.parentNode) {
1189 |             if ((p.tagName === 'pre' || p.tagName === 'code' ||
1190 |                  p.tagName === 'xmp') &&
1191 |                 p.className && p.className.indexOf('prettyprint') >= 0) {
1192 |               nested = true;
1193 |               break;
1194 |             }
1195 |           }
1196 |           if (!nested) {
1197 |             // fetch the content as a snippet of properly escaped HTML.
1198 |             // Firefox adds newlines at the end.
1199 |             var content = getInnerHtml(cs);
1200 |             content = content.replace(/(?:\r\n?|\n)$/, '');
1201 | 
1202 |             // do the pretty printing
1203 |             var newContent = prettyPrintOne(content, langExtension);
1204 | 
1205 |             // push the prettified html back into the tag.
1206 |             if (!isRawContent(cs)) {
1207 |               // just replace the old html with the new
1208 |               cs.innerHTML = newContent;
1209 |             } else {
1210 |               // we need to change the tag to a <pre> since <xmp>s do not allow
1211 |               // embedded tags such as the span tags used to attach styles to
1212 |               // sections of source code.
1213 |               var pre = document.createElement('PRE');
1214 |               for (var i = 0; i < cs.attributes.length; ++i) {
1215 |                 var a = cs.attributes[i];
1216 |                 if (a.specified) {
1217 |                   var aname = a.name.toLowerCase();
1218 |                   if (aname === 'class') {
1219 |                     pre.className = a.value;  // For IE 6
1220 |                   } else {
1221 |                     pre.setAttribute(a.name, a.value);
1222 |                   }
1223 |                 }
1224 |               }
1225 |               pre.innerHTML = newContent;
1226 | 
1227 |               // remove the old
1228 |               cs.parentNode.replaceChild(pre, cs);
1229 |               cs = pre;
1230 |             }
1231 | 
1232 |             // Replace <br>s with line-feeds so that copying and pasting works
1233 |             // on IE 6.
1234 |             // Doing this on other browsers breaks lots of stuff since \r\n is
1235 |             // treated as two newlines on Firefox, and doing this also slows
1236 |             // down rendering.
1237 |             if (isIE6 && cs.tagName === 'PRE') {
1238 |               var lineBreaks = cs.getElementsByTagName('br');
1239 |               for (var j = lineBreaks.length; --j >= 0;) {
1240 |                 var lineBreak = lineBreaks[j];
1241 |                 lineBreak.parentNode.replaceChild(
1242 |                     document.createTextNode('\r\n'), lineBreak);
1243 |               }
1244 |             }
1245 |           }
1246 |         }
1247 |       }
1248 |       if (k < elements.length) {
1249 |         // finish up in a continuation
1250 |         setTimeout(doWork, 250);
1251 |       } else if (opt_whenDone) {
1252 |         opt_whenDone();
1253 |       }
1254 |     }
1255 | 
1256 |     doWork();
1257 |   }
1258 | 
1259 |   window['PR_normalizedHtml'] = normalizedHtml;
1260 |   window['prettyPrintOne'] = prettyPrintOne;
1261 |   window['prettyPrint'] = prettyPrint;
1262 |   window['PR'] = {
1263 |         'createSimpleLexer': createSimpleLexer,
1264 |         'registerLangHandler': registerLangHandler,
1265 |         'sourceDecorator': sourceDecorator,
1266 |         'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
1267 |         'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
1268 |         'PR_COMMENT': PR_COMMENT,
1269 |         'PR_DECLARATION': PR_DECLARATION,
1270 |         'PR_KEYWORD': PR_KEYWORD,
1271 |         'PR_LITERAL': PR_LITERAL,
1272 |         'PR_NOCODE': PR_NOCODE,
1273 |         'PR_PLAIN': PR_PLAIN,
1274 |         'PR_PUNCTUATION': PR_PUNCTUATION,
1275 |         'PR_SOURCE': PR_SOURCE,
1276 |         'PR_STRING': PR_STRING,
1277 |         'PR_TAG': PR_TAG,
1278 |         'PR_TYPE': PR_TYPE
1279 |       };
1280 | })();
1281 | 


--------------------------------------------------------------------------------
/tests/prettify_test.html:
--------------------------------------------------------------------------------
   1 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
   2 | <html>
   3 | <head>
   4 | <title>Code Prettifier</title>
   5 | <script src="../src/prettify.js" type="text/javascript"
   6 |  onerror="alert('Error: failed to load ' + this.src)"></script>
   7 | <script src="../src/lang-lisp.js" type="text/javascript"
   8 |  onerror="alert('Error: failed to load ' + this.src)"></script>
   9 | <script src="../src/lang-lua.js" type="text/javascript"
  10 |  onerror="alert('Error: failed to load ' + this.src)"></script>
  11 | <script src="../src/lang-ml.js" type="text/javascript"
  12 |  onerror="alert('Error: failed to load ' + this.src)"></script>
  13 | <script src="../src/lang-proto.js" type="text/javascript"
  14 |  onerror="alert('Error: failed to load ' + this.src)"></script>
  15 | <script src="../src/lang-sql.js" type="text/javascript"
  16 |  onerror="alert('Error: failed to load ' + this.src)"></script>
  17 | <script type="text/javascript">
  18 | // get accurate timing
  19 | PR_SHOULD_USE_CONTINUATION = false;
  20 | function _pr_isIE6() { return false; }  // Ensure consistent output.
  21 | </script>
  22 | <link rel="stylesheet" type="text/css" href="../src/prettify.css" />
  23 | <style type="text/css">
  24 | .mismatch { background: #fee; }
  25 | span.annot { margin:2px 2px 2px 3em; border:1px dotted #88f; background:#eef;
  26 |              padding: 0 2px 0 2px }
  27 | .nocode { background: #f8f8f8 }
  28 | </style>
  29 | </head>
  30 | 
  31 | <body
  32 |  onload="startClock(); prettyPrint(function () { stopClock(); runTests(); })"
  33 |  bgcolor="white">
  34 | <div id="timing"></div>
  35 | <div id="errorReport" style="white-space: pre"></div>
  36 | 
  37 | <h1>Bash</h1>
  38 | <pre class="prettyprint" id="bash">#!/bin/bash
  39 | 
  40 | # Fibonacci numbers
  41 | # Writes an infinite series to stdout, one entry per line
  42 | function fib() {
  43 |   local a=1
  44 |   local b=1
  45 |   while true ; do
  46 |     echo $a
  47 |     local tmp=$a
  48 |     a=$(( $a + $b ))
  49 |     b=$tmp
  50 |   done
  51 | }
  52 | 
  53 | # output the 10th element of the series and halt
  54 | fib | head -10 | tail -1
  55 | </pre>
  56 | 
  57 | <h1>Bash w/ language specified</h1>
  58 | <pre class="prettyprint lang-sh" id="bash_lang">#!/bin/bash
  59 | 
  60 | # Fibonacci numbers
  61 | # Writes an infinite series to stdout, one entry per line
  62 | function fib() {
  63 |   local a=1
  64 |   local b=1
  65 |   while true ; do
  66 |     echo $a
  67 |     local tmp=$a
  68 |     a=$(( $a + $b ))
  69 |     b=$tmp
  70 |   done
  71 | }
  72 | 
  73 | # output the 10th element of the series and halt
  74 | fib | /usr/bin/*head -10 | tail -1
  75 | </pre>
  76 | 
  77 | <h1>C</h1>
  78 | 
  79 | <pre class="prettyprint" id="C">
  80 | #include &lt;stdio.h&gt;
  81 | 
  82 | /* the n-th fibonacci number.
  83 |  */
  84 | unsigned int fib(unsigned int n) {
  85 |   unsigned int a = 1, b = 1;
  86 |   unsigned int tmp;
  87 |   while (--n >= 0) {
  88 |     tmp = a;
  89 |     a += b;
  90 |     b = tmp;
  91 |   }
  92 |   return a;
  93 | }
  94 | 
  95 | main() {
  96 |   printf("%u", fib(10));
  97 | }
  98 | </pre>
  99 | 
 100 | <h1>C w/ language specified</h1>
 101 | 
 102 | <pre class="prettyprint lang-c" id="C_lang">
 103 | #include &lt;stdio.h&gt;
 104 | 
 105 | /* the n<sup>th</sup> fibonacci number. */
 106 | unsigned int fib(unsigned int n) {
 107 |   unsigned int a = 1, b = 1;
 108 |   unsigned int tmp;
 109 |   while (--n >= 0) {
 110 |     tmp = a;
 111 |     a += b;
 112 |     b = tmp;
 113 |   }
 114 |   return a;
 115 | }
 116 | 
 117 | void main() {
 118 |   printf("%u", fib(10));
 119 | }
 120 | </pre>
 121 | 
 122 | <h1>C++</h1>
 123 | <pre class="prettyprint" id="Cpp">
 124 | #include &lt;iostream&gt;
 125 | 
 126 | using namespace std;
 127 | 
 128 | //! fibonacci numbers with gratuitous use of templates.
 129 | //! \param n an index into the fibonacci series
 130 | //! \param fib0 element 0 of the series
 131 | //! \return the nth element of the fibonacci series
 132 | template &lt;class T>
 133 | T fib(unsigned int n, const T&amp; fib0) {
 134 |   T a(fib0), b(fib0);
 135 |   for (; n; --n) {
 136 |     T tmp(a);
 137 |     a += b;
 138 |     b = tmp;
 139 |   }
 140 |   return a;
 141 | }
 142 | 
 143 | int main(int argc, char **argv) {
 144 |   cout &lt;&lt; fib(10, 1U);
 145 | }
 146 | </pre>
 147 | 
 148 | <h1>C++ w/ language specified</h1>
 149 | <pre class="prettyprint lang-cc" id="Cpp_lang">
 150 | #include &lt;iostream&gt;
 151 | 
 152 | using namespace std;
 153 | 
 154 | //! fibonacci numbers with gratuitous use of templates.
 155 | //! \param n an index into the fibonacci series
 156 | //! \param fib0 element 0 of the series
 157 | //! \return the nth element of the fibonacci series
 158 | template &lt;class T>
 159 | T fib(int n, const T&amp; fib0) {
 160 |   T a(fib0), b(fib0);
 161 |   while (--n >= 0) {
 162 |     T tmp(a);
 163 |     a += b;
 164 |     b = tmp;
 165 |   }
 166 |   return a;
 167 | }
 168 | 
 169 | int main(int argc, char **argv) {
 170 |   cout &lt;&lt; fib(10, 1U);
 171 | }
 172 | </pre>
 173 | 
 174 | <h1>Java</h1>
 175 | <pre class="prettyprint" id="java">
 176 | package foo;
 177 | 
 178 | import java.util.Iterator;
 179 | 
 180 | /**
 181 |  * the fibonacci series implemented as an Iterable.
 182 |  */
 183 | public final class Fibonacci implements Iterable&lt;Integer> {
 184 |   /** the next and previous members of the series. */
 185 |   private int a = 1, b = 1;
 186 | 
 187 |   @Override
 188 |   public Iterator&lt;Integer> iterator() {
 189 |     return new Iterator&lt;Integer>() {
 190 |       /** the series is infinite. */
 191 |       public boolean hasNext() { return true; }
 192 |       public Integer next() {
 193 |         int tmp = a;
 194 |         a += b;
 195 |         b = tmp;
 196 |         return a;
 197 |       }
 198 |       public void remove() { throw new UnsupportedOperationException(); }
 199 |     };
 200 |   }
 201 | 
 202 |   /**
 203 |    * the n&lt;sup>th&lt;/sup> element of the given series.
 204 |    * @throws NoSuchElementException if there are less than n elements in the
 205 |    *   given Iterable's {@link Iterable#iterator iterator}.
 206 |    */
 207 |   public static &lt;T>
 208 |   T nth(int n, Iterable&lt;T> iterable) {
 209 |     Iterator&lt;? extends T> it = iterable.iterator();
 210 |     while (--n > 0) {
 211 |       it.next();
 212 |     }
 213 |     return it.next();
 214 |   }
 215 | 
 216 |   public static void main(String[] args) {
 217 |     System.out.print(nth(10, new Fibonacci()));
 218 |   }
 219 | }
 220 | </pre>
 221 | 
 222 | <h1>Java w/ language specified</h1>
 223 | <pre class="prettyprint lang-java" id="java_lang">
 224 | package foo;
 225 | 
 226 | import java.util.Iterator;
 227 | 
 228 | /**
 229 |  * the fibonacci series implemented as an Iterable.
 230 |  */
 231 | public final class Fibonacci implements Iterable&lt;Integer> {
 232 |   /** the next and previous members of the series. */
 233 |   private int a = 1, b = 1;
 234 | 
 235 |   @Override
 236 |   public Iterator&lt;Integer> iterator() {
 237 |     return new Iterator&lt;Integer>() {
 238 |       /** the series is infinite. */
 239 |       public boolean hasNext() { return true; }
 240 |       public Integer next() {
 241 |         int tmp = a;
 242 |         a += b;
 243 |         b = tmp;
 244 |         return a;
 245 |       }
 246 |       public void remove() { throw new UnsupportedOperationException(); }
 247 |     };
 248 |   }
 249 | 
 250 |   /**
 251 |    * the n&lt;sup>th&lt;/sup> element of the given series.
 252 |    * @throws NoSuchElementException if there are less than n elements in the
 253 |    *   given Iterable's {@link Iterable#iterator iterator}.
 254 |    */
 255 |   public static &lt;T>
 256 |   T nth(int n, Iterable&lt;T> iterable) {
 257 |     Iterator&lt;? extends T> in = iterable.iterator();
 258 |     while (--n > 0) {
 259 |       in.next();
 260 |     }
 261 |     return in.next();
 262 |   }
 263 | 
 264 |   public static void main(String[] args) {
 265 |     System.out.print(nth(10, new Fibonacci()));
 266 |   }
 267 | }
 268 | 
 269 | # not a java comment
 270 | # not keywords: static_cast and namespace
 271 | </pre>
 272 | 
 273 | <h1>Javascript</h1>
 274 | <pre class="prettyprint" id="javascript">
 275 | /**
 276 |  * nth element in the fibonacci series.
 277 |  * @param n >= 0
 278 |  * @return the nth element, >= 0.
 279 |  */
 280 | function fib(n) {
 281 |   var a = 1, b = 1;
 282 |   var tmp;
 283 |   while (--n >= 0) {
 284 |     tmp = a;
 285 |     a += b;
 286 |     b = tmp;
 287 |   }
 288 |   return a;
 289 | }
 290 | 
 291 | document.write(fib(10));
 292 | </pre>
 293 | 
 294 | <h1>Issue 12 - Javascript Regular Expressions</h1>
 295 | <pre class="prettyprint" id="issue12">
 296 | /foo/;  // a slash starting a line treated as a regexp beginning
 297 | "foo".match(/fo+$/);
 298 | // this line comment not treated as a regular expressions
 299 | "foo /bar/".test(/"baz"/);  // test string and regexp boundaries
 300 | var division = /\b\d+\/\d+/g;  // test char sets and escaping of specials
 301 | var allSpecials = /([^\(\)\[\]\{\}\-\?\+\*\.\^\$\/]+)\\/;
 302 | var slashInCharset = /[^/]/g, notCloseSq = /[^\]]/;
 303 | 
 304 | // test that slash used in numeric context treated as an operator
 305 | 1 / 2;
 306 | 1. / x;
 307 | x / y;
 308 | (x) / y;
 309 | 1 /* foo */ / 2;
 310 | 1 /* foo *// 2;
 311 | 1/2;
 312 | 1./x;
 313 | x/y;
 314 | (x)/y;
 315 | 
 316 | // test split over two lines.  line comment should not fool it
 317 | 1//
 318 | /2;
 319 | 
 320 | x++/y;
 321 | x--/y;
 322 | x[y] / z;
 323 | f() / n;
 324 | 
 325 | // test that slash after non postfix operator is start of regexp
 326 | log('matches = ' + /foo/.test(foo));
 327 | 
 328 | // test keyword preceders
 329 | return /a regexp/;
 330 | division = notreturn / not_a_regexp / 2;  // keyword suffix does not match
 331 | 
 332 | // & not used as prefix operator in javascript but this should still work
 333 | &/foo/;
 334 | 
 335 | extends = /extends/;
 336 | </pre>
 337 | 
 338 | <h1>Issue 12 - Javascript Regular Expressions w/ language specified</h1>
 339 | <pre class="prettyprint lang-js" id="issue12_lang">
 340 | /foo/;  // a slash starting a line treated as a regexp beginning
 341 | "foo".match(/fo+$/);
 342 | // this line comment not treated as a regular expressions
 343 | "foo /bar/".test(/"baz"/);  // test string and regexp boundaries
 344 | var division = /\b\d+\/\d+/g;  // test char sets and escaping of specials
 345 | var allSpecials = /([^\(\)\[\]\{\}\-\?\+\*\.\^\$\/]+)\\/;
 346 | var slashInCharset = /[^/]/g, notCloseSq = /[^\]]/;
 347 | 
 348 | // test that slash used in numeric context treated as an operator
 349 | 1 / 2;
 350 | 1. / x;
 351 | x / y;
 352 | (x) / y;
 353 | 1 /* foo */ / 2;
 354 | 1 /* foo *// 2;
 355 | 1/2;
 356 | 1./x;
 357 | x/y;
 358 | (x)/y;
 359 | 
 360 | // test split over two lines.  line comment should not fool it
 361 | 1//
 362 | /2;
 363 | 
 364 | x++/y;
 365 | x--/y;
 366 | x[y] / z;
 367 | f() / n;
 368 | 
 369 | // test that slash after non postfix operator is start of regexp
 370 | log('matches = ' + /foo/.test(foo));
 371 | 
 372 | // test keyword preceders
 373 | return /a regexp/;
 374 | division = notreturn / not_a_regexp / 2;  // keyword suffix does not match
 375 | 
 376 | // & not used as prefix operator in javascript but this should still work
 377 | &/foo/;
 378 | 
 379 | extends = /extends/;
 380 | </pre>
 381 | 
 382 | <h1>Perl</h1>
 383 | <pre class="prettyprint" id="perl">
 384 | #!/usr/bin/perl
 385 | 
 386 | use strict;
 387 | use integer;
 388 | 
 389 | # the nth element of the fibonacci series
 390 | # param n - an int >= 0
 391 | # return an int >= 0
 392 | sub fib($) {
 393 |   my $n = shift, $a = 1, $b = 1;
 394 |   ($a, $b) = ($a + $b, $a) until (--$n < 0);
 395 |   return $a;
 396 | }
 397 | 
 398 | print fib(10);
 399 | </pre>
 400 | 
 401 | <h1>Python</h1>
 402 | <pre class="prettyprint" id="python">
 403 | #!/usr/bin/python2.4
 404 | 
 405 | def fib():
 406 |   '''
 407 |   a generator that produces the elements of the fibonacci series
 408 |   '''
 409 | 
 410 |   a = 1
 411 |   b = 1
 412 |   while True:
 413 |     a, b = a + b, a
 414 |     yield a
 415 | 
 416 | def nth(series, n):
 417 |   '''
 418 |   returns the nth element of a series,
 419 |   consuming the earlier elements of the series
 420 |   '''
 421 | 
 422 |   for x in series:
 423 |     n = n - 1
 424 |     if n <= 0: return x
 425 | 
 426 | print nth(fib(), 10)
 427 | </pre>
 428 | 
 429 | <h1>Python w/ language specified</h1>
 430 | <pre class="prettyprint lang-py" id="python_lang">
 431 | #!/usr/bin/python2.4
 432 | 
 433 | def fib():
 434 |   '''
 435 |   a generator that produces the fibonacci series's elements
 436 |   '''
 437 | 
 438 |   a = 1
 439 |   b = 1
 440 |   while True:
 441 |     a, b = a + b, a
 442 |     yield a
 443 | 
 444 | def nth(series, n):
 445 |   '''
 446 |   returns the nth element of a series,
 447 |   consuming the earlier elements of the series
 448 |   '''
 449 | 
 450 |   for x in series:
 451 |     n -= 1
 452 |     if n <= 0: return x
 453 | 
 454 | print nth(fib(), 10)
 455 | 
 456 | /* not a comment and not keywords: null char true */
 457 | </pre>
 458 | 
 459 | <h1>SQL w/ language specified</h1>
 460 | <pre class="prettyprint lang-sql" id="sql_lang">
 461 | /* A multi-line
 462 |  * comment */
 463 | 'Another string /* Isn\'t a comment',
 464 | "A string */"
 465 | -- A line comment
 466 | SELECT * FROM users WHERE id IN (1, 2.0, +30e-1);
 467 | -- keywords are case-insensitive.
 468 | -- Note: user-table is a single identifier, not a pair of keywords
 469 | select * from user-table where id in (x, y, z);
 470 | </pre>
 471 | 
 472 | <h1>XML</h1>
 473 | <pre class="prettyprint" id="xml">
 474 | &lt;!DOCTYPE series PUBLIC "fibonacci numbers"&gt;
 475 | 
 476 | &lt;series base="1" step="s(n-2) + s(n-1)">
 477 |   &lt;element i="0"&gt;1&lt;/element&gt;
 478 |   &lt;element i="1"&gt;1&lt;/element&gt;
 479 |   &lt;element i="2"&gt;2&lt;/element&gt;
 480 |   &lt;element i="3"&gt;3&lt;/element&gt;
 481 |   &lt;element i="4"&gt;5&lt;/element&gt;
 482 |   &lt;element i="5"&gt;8&lt;/element&gt;
 483 |   ...
 484 | &lt;/series&gt;
 485 | </pre>
 486 | 
 487 | <h1>HTML</h1>
 488 | <pre class="prettyprint" id="html">
 489 | &lt;html&gt;
 490 |   &lt;head>
 491 |     &lt;title&gt;Fibonacci number&lt;/title&gt;
 492 |   &lt;/head&gt;
 493 |   &lt;body>
 494 |     &lt;noscript&gt;
 495 |       &lt;dl&gt;
 496 |         &lt;dt&gt;Fibonacci numbers&lt;/dt&gt;
 497 |         &lt;dd&gt;1&lt;/dd&gt;
 498 |         &lt;dd&gt;1&lt;/dd&gt;
 499 |         &lt;dd&gt;2&lt;/dd&gt;
 500 |         &lt;dd&gt;3&lt;/dd&gt;
 501 |         &lt;dd&gt;5&lt;/dd&gt;
 502 |         &lt;dd&gt;8&lt;/dd&gt;
 503 |         &amp;hellip;
 504 |       &lt;/dl&gt;
 505 |     &lt;/noscript&gt;
 506 | 
 507 |     &lt;script type="text/javascript">&lt;!--
 508 | function fib(n) {
 509 |   var a = 1, b = 1;
 510 |   var tmp;
 511 |   while (--n >= 0) {
 512 |     tmp = a;
 513 |     a += b;
 514 |     b = tmp;
 515 |   }
 516 |   return a;
 517 | }
 518 | 
 519 | document.writeln(fib(10));
 520 | // --&gt;
 521 |     &lt;/script>
 522 |   &lt;/body&gt;
 523 | &lt;/html&gt;
 524 | </pre>
 525 | 
 526 | <h1>HTML w/ language specified</h1>
 527 | <pre class="prettyprint lang-html" id="html_lang">
 528 | Fibonacci Numbers
 529 | 
 530 | &lt;noscript&gt;
 531 |   &lt;dl&gt;
 532 |     &lt;dt&gt;Fibonacci numbers&lt;/dt&gt;
 533 |     &lt;dd&gt;1&lt;/dd&gt;
 534 |     &lt;dd&gt;1&lt;/dd&gt;
 535 |     &lt;dd&gt;2&lt;/dd&gt;
 536 |     &lt;dd&gt;3&lt;/dd&gt;
 537 |     &lt;dd&gt;5&lt;/dd&gt;
 538 |     &lt;dd&gt;8&lt;/dd&gt;
 539 |     &amp;hellip;
 540 |   &lt;/dl&gt;
 541 | &lt;/noscript&gt;
 542 | 
 543 | &lt;script type="text/javascript">&lt;!--
 544 | function fib(n) {
 545 |   var a = 1, b = 1;
 546 |   var tmp;
 547 |   while (--n >= 0) {
 548 |     tmp = a;
 549 |     a += b;
 550 |     b = tmp;
 551 |   }
 552 |   return a;
 553 | }
 554 | 
 555 | document.writeln(fib(10));
 556 | // --&gt;
 557 | &lt;/script>
 558 | </pre>
 559 | 
 560 | 
 561 | <h1>HTML using XMP</h1>
 562 | <xmp class="prettyprint" id="htmlXmp"
 563 | ><html>
 564 |   <head>
 565 |     <title>Fibonacci number</title>
 566 |   </head>
 567 |   <body>
 568 |     <noscript>
 569 |       <dl>
 570 |         <dt>Fibonacci numbers</dt>
 571 |         <dd>1</dd>
 572 |         <dd>1</dd>
 573 |         <dd>2</dd>
 574 |         <dd>3</dd>
 575 |         <dd>5</dd>
 576 |         <dd>8</dd>
 577 |         &hellip;
 578 |       </dl>
 579 |     </noscript>
 580 | 
 581 |     <script type="text/javascript"><!--
 582 | function fib(n) {
 583 |   var a = 1, b = 1;
 584 |   var tmp;
 585 |   while (--n >= 0) {
 586 |     tmp = a;
 587 |     a += b;
 588 |     b = tmp;
 589 |   }
 590 |   return a;
 591 | }
 592 | 
 593 | document.writeln(fib(10));
 594 | // -->
 595 |     </script>
 596 |   </body>
 597 | </html>
 598 | 
 599 | 
 600 | 

XHTML

601 |
<xhtml>
 603 |   <head>
 604 |     <title>Fibonacci number</title>
 605 |   </head>
 606 |   <body>
 607 |     <script type="text/javascript"><![CDATA[
 608 | function fib(n) {
 609 |   var a = 1, b = 1;
 610 |   var tmp;
 611 |   while (--n >= 0) {
 612 |     tmp = a;
 613 |     a += b;
 614 |     b = tmp;
 615 |   }
 616 |   return a;
 617 | }
 618 | 
 619 | document.writeln(fib(10));
 620 | ]]>
 621 |     </script>
 622 |   </body>
 623 | </xhtml>
 624 | 
625 | 626 |

PHP

627 |
 628 | <html>
 629 |   <head>
 630 |     <title><?= 'Fibonacci numbers' ?></title>
 631 | 
 632 |     <?php
 633 |       // PHP has a plethora of comment types
 634 |       /* What is a
 635 |          "plethora"? */
 636 |       function fib($n) {
 637 |         # I don't know.
 638 |         $a = 1;
 639 |         $b = 1;
 640 |         while (--$n >= 0) {
 641 |           echo "$a\n";
 642 |           $tmp = $a;
 643 |           $a += $b;
 644 |           $b = $tmp;
 645 |         }
 646 |       }
 647 |     ?>
 648 |   </head>
 649 |   <body>
 650 |     <?= fib(10) ?>
 651 |   </body>
 652 | </html>
 653 | 
654 | 655 |

XSL (Issue 19)

656 |
 657 | <!-- Test elements and attributes with namespaces -->
 658 | 
 659 | <xsl:stylesheet xml:lang="en">
 660 |   <xsl:template match=".">
 661 |     <xsl:text>Hello World</xsl:text>
 662 |   </xsl:template>
 663 | </xsl:stylesheet>
 664 | 
665 | 666 |

Whitespace

667 |

 668 | 
 669 | 

Misc

670 |
// ends with line comment token
 671 | //
672 | 673 |

User submitted testcase for Bug 4

674 |

675 | Javascript Snippets wrapped in HTML SCRIPT tags hides/destroys inner content 676 |

677 |
 678 | <script type="text/javascript">
 679 |    var savedTarget=null;                           // The target layer (effectively vidPane)
 680 |    var orgCursor=null;                             // The original mouse style so we can restore it
 681 |    var dragOK=false;                               // True if we're allowed to move the element under mouse
 682 |    var dragXoffset=0;                              // How much we've moved the element on the horozontal
 683 |    var dragYoffset=0;                              // How much we've moved the element on the verticle
 684 |    vidPaneID = document.getElementById('vidPane'); // Our movable layer
 685 |    vidPaneID.style.top='75px';                     // Starting location horozontal
 686 |    vidPaneID.style.left='75px';                    // Starting location verticle
 687 | <script>
 688 | 
689 |

The fact that the script tag was not closed properly was causing 690 | PR_splitSourceNodes to end without emitting the script contents.

691 | 692 |

Bug 8 - tabs mangled

693 |

If tabs are used to indent code inside <pre> IE6 and 7 won't honor them 694 | after the script runs. 695 | 696 | Code indented with tabs will be shown aligned to the left margin instead of 697 | the proper indenting shown in Firefox. 698 | 699 | I'm using Revision 20 of prettify.js, IE 6.0.29.00 in English and IE 700 | 7.0.5730.11 in Spanish. 701 |

702 |
 703 | one	Two	three	Four	five	|
 704 | Six	seven	Eight	nine	Ten	|
 705 | eleven	Twelve	thirteen	Fourteen	fifteen	|
 706 | 
707 | 708 |

Bug 14a - does not recognize <br> as newline

709 |
//comment
int main(int argc, char **argv) 711 | {}
712 | 713 |

Bug 14b - comments not ignored

714 |
<!-- There's an HTML comment in my comment -->
 716 | <p>And another one inside the end tag</p>
 717 | 
718 | 719 |

Bug 20 - missing blank lines

720 |
<html>
 722 | 
 723 | <head>
724 | 725 |

Bug 21 - code doesn't copy and paste well in IE

726 |
<html>
 728 |   <head>
 729 |     <title>Test</title>
 730 |   </head>
 731 | </html>
732 |

To test this bug, disable overriding of _pr_isIE6 above, and copy and paste 733 | the above into Notepad.

734 | 735 |

Bug 22 - Line numbers and other non-code spans in code

736 |
01: // This is a line of code
 738 | 02: /* Multiline comments can
 739 | 03:  * span over and around
 740 | 04:  * line markers
 741 | And can even be interrupted
 742 | by inline code annotations
 743 | 05:  */
 744 | 06: class MyClass extends Foo {
 745 | 07:   public static void main(String... argv) {
 746 | 08:     System.out.print("Hello World");
 747 | 09:   }
 748 | 10: }
749 | 750 |

Bug 24 - LUA Syntax Highlighting

751 |
 753 | -- Examples from the language reference
 754 |      a = 'alo\n123"'
 755 |      a = "alo\n123\""
 756 |      a = '\97lo\10\04923"'
 757 |      a = [[alo
 758 |      123"]]
 759 |      a = [==[
 760 |      alo
 761 |      123"]==]
 762 | 
 763 | 3   3.0   3.1416   314.16e-2   0.31416E1   0xff   0x56
 764 | 
 765 | -- Some comments that demonstrate long brackets
 766 | double_quoted = "Not a long bracket [=["
 767 | --[=[ quoting out
 768 |  [[ foo ]]
 769 |  [==[does not end comment either]==]
 770 | ]=]
 771 | past_end_of_comment
 772 | --]=]
 773 | 
 774 | -- Example code courtesy Joseph Harmbruster
 775 | #
 776 | do
 777 |   local function ssgeneral(t, n, before)
 778 |     for _, h in ipairs(incs) do
 779 |       for i = h + 1, n do
 780 |         local v = t[i]
 781 |         for j = i - h, 1, -h do
 782 |           local testval = t[j]
 783 |           if not before(v, testval) then break end
 784 |           t[i] = testval; i = j
 785 |         end
 786 |         t[i] = v
 787 |       end 
 788 |     end
 789 |     return t
 790 |   end
 791 | 
 792 |   function shellsort(t, before, n)
 793 |     n = n or #t
 794 |     if not before or before == "<" then return ssup(t, n)
 795 |     elseif before == ">" then return ssdown(t, n)
 796 |     else return ssgeneral(t, n, before)
 797 |     end
 798 |   end
 799 |   return shellsort
 800 | end
801 | 802 |

Bug 42 - Lisp Syntax Highlighting

803 |
; -*- mode: lisp -*-
 805 | 
 806 | (defun back-six-lines () (interactive) (forward-line -6))
 807 | (defun forward-six-lines () (interactive) (forward-line 6))
 808 | 
 809 | (global-set-key "\M-l" 'goto-line)
 810 | (global-set-key "\C-z" 'advertised-undo)
 811 | (global-set-key [C-insert] 'clipboard-kill-ring-save)
 812 | (global-set-key [S-insert] 'clipboard-yank)
 813 | (global-set-key [C-up] 'back-six-lines)
 814 | (global-set-key [C-down] 'forward-six-lines)
 815 | 
 816 | (setq visible-bell t)
 817 | (setq user-mail-address "foo@bar.com")
 818 | (setq default-major-mode 'text-mode)
 819 | 
 820 | (setenv "TERM" "emacs")
 821 | (c-set-offset 'case-label 2)
 822 | (setq c-basic-offset 2)
 823 | (setq perl-indent-level 2)
 824 | (setq delete-key-deletes-forward t)
 825 | (setq indent-tabs-mode nil)
 826 | 
 827 | ;; Text mode
 828 | (add-hook 'text-mode-hook 
 829 |   '(lambda ()
 830 |      (turn-on-auto-fill)
 831 |    )
 832 | )
 833 | 
 834 | ;; Fundamental mode
 835 | (add-hook 'fundamental-mode-hook 
 836 |   '(lambda ()
 837 |      (turn-on-auto-fill)
 838 |    )
 839 | )
 840 | 
841 | 842 |

Bug 33 - OCaml and F#

843 |
 844 | (*
 845 |  * Print the 10th fibonacci number
 846 |  *)
 847 | 
 848 | //// A line comment
 849 | "A string";;
 850 | (0, 125, 0xa0, -1.0, 1e6, 1.2e-3);;  // number literals
 851 | 
 852 | #if fibby
 853 |   let
 854 |     rec fib = function (0, a, _) -> a
 855 |                      | (n, a, b) -> fib(n - 1, a + b, a)
 856 |   in
 857 |     print_int(fib(10, 1, 1));;
 858 | #endif
 859 | 
860 |

Still TODO: handle nested (* (* comments *) *) properly.

861 | 862 |

Bug 45 - Square brackets in strings

863 |
 864 | throw new RuntimeException("Element [" + element.getName() + 
 865 |   "] missing attribute.");
 866 | variable++;
 867 | 
868 | 869 |

Protocol Buffers

870 |
message SearchRequest {
 872 |   required string query = 1;
 873 |   optional int32 page_number = 2;
 874 |   optional int32 result_per_page = 3 [default = 10];
 875 |   enum Corpus {
 876 |     UNIVERSAL = 0;
 877 |     WEB = 1;
 878 |     IMAGES = 2;
 879 |     LOCAL = 3;
 880 |     NEWS = 4;
 881 |     PRODUCTS = 5;
 882 |     VIDEO = 6;
 883 |   }
 884 |   optional Corpus corpus = 4 [default = UNIVERSAL];
 885 | }
886 | 887 | 888 | 889 | 2160 | 2161 | 2162 | --------------------------------------------------------------------------------