From 09c2ec4d74af1b82ae8b81eeb617b29ac5995c09 Mon Sep 17 00:00:00 2001 From: Eric Bidelman Date: Thu, 24 Jul 2014 10:47:01 -0700 Subject: [PATCH] Update to latest release --- CHANGES.html | 17 + Makefile | 77 +- README.html | 98 +- examples/quine.html | 55 + js-modules/extractSourceSpans.js | 52 +- js-modules/js_include.pl | 76 + js-modules/numberLines.js | 63 +- js-modules/prettify.js | 205 ++- js-modules/recombineTagsAndDecorations.js | 1 + js-modules/run_prettify.js | 250 +++ src/lang-basic.js | 32 + src/lang-css.js | 2 +- src/lang-erlang.js | 92 + src/lang-llvm.js | 61 + src/lang-matlab.js | 180 ++ src/lang-mumps.js | 139 ++ src/lang-n.js | 1 + src/lang-pascal.js | 32 + src/lang-r.js | 57 + src/lang-rd.js | 47 + src/lang-sql.js | 2 +- src/lang-tcl.js | 62 + src/lang-vb.js | 8 +- src/prettify.js | 384 +++-- src/run_prettify.js | 1905 +++++++++++++++++++++ styles/desert.css | 1 - styles/doxy.css | 64 + styles/index.html | 1 + tests/prettify_test.html | 65 +- tests/prettify_test_2.html | 1001 ++++++++++- tests/run_prettify_test.html | 57 + 31 files changed, 4722 insertions(+), 365 deletions(-) create mode 100644 examples/quine.html create mode 100644 js-modules/js_include.pl create mode 100644 js-modules/run_prettify.js create mode 100644 src/lang-basic.js create mode 100644 src/lang-erlang.js create mode 100644 src/lang-llvm.js create mode 100644 src/lang-matlab.js create mode 100644 src/lang-mumps.js create mode 100644 src/lang-pascal.js create mode 100644 src/lang-r.js create mode 100644 src/lang-rd.js create mode 100644 src/lang-tcl.js create mode 100644 src/run_prettify.js create mode 100644 styles/doxy.css create mode 100644 tests/run_prettify_test.html diff --git a/CHANGES.html b/CHANGES.html index 9da9fa6..de9e2b7 100644 --- a/CHANGES.html +++ b/CHANGES.html @@ -151,5 +151,22 @@

29 March 2011

vulnerability. If you sanitize and prettify HTML from an untrusted source, sanitize first. +

4 February 2013

+ +

24 February 2013

+ +

4 March 2013

+ diff --git a/Makefile b/Makefile index 885ebb2..338ebb9 100644 --- a/Makefile +++ b/Makefile @@ -1,52 +1,40 @@ SHELL := /bin/bash -CLOSURE_COMPILER=java -jar closure-compiler/compiler.jar \ +CLOSURE_COMPILER=java -jar tools/closure-compiler/compiler.jar \ --warning_level VERBOSE \ --language_in ECMASCRIPT5 \ - --compilation_level ADVANCED_OPTIMIZATIONS + --compilation_level ADVANCED_OPTIMIZATIONS \ + --charset US-ASCII # Don't specify --charset=UTF-8. If we do, then non-ascii codepoints # that do not correspond to line terminators are converted # to UTF-8 sequences instead of being emitted as ASCII. # This makes the resulting JavaScript less portable. -YUI_COMPRESSOR=java -jar yui-compressor/yuicompressor-2.4.4.jar \ +YUI_COMPRESSOR=java -jar tools/yui-compressor/yuicompressor-2.4.4.jar \ --charset UTF-8 TAR_ROOT=distrib/google-code-prettify -all: src/prettify.js distrib +all: distrib clean: - rm -rf distrib.tstamp distrib src/prettify.js + rm -rf distrib.tstamp distrib src/prettify.js src/run_prettify.js src/prettify.js: js-modules/*.js js-modules/*.pl - @if [ -e $@ ]; then chmod +w $@; fi - @perl -e '\ - sub readInclude($$$$) {\ - my $$prefix = $$_[0];\ - my $$name = "js-modules/" . $$_[1];\ - my $$buf = "";\ - if ($$name =~ /\.pl$$/) {\ - open(IN, "|perl $$name") or die "$$name: $$!";\ - } else {\ - open(IN, "<$$name") or die "$$name: $$!";\ - }\ - while () {\ - $$buf .= "$$prefix$$_";\ - }\ - return $$buf;\ - }' \ - -pe 's/^(\s*)include\("([^"]+)"\);/readInclude($$1, $$2)/ge' \ - js-modules/prettify.js \ - > src/prettify.js \ - || rm src/prettify.js - @if [ -e $@ ]; then chmod -w $@; fi + @if [ -e "$@" ]; then chmod +w "$@"; fi + @perl js-modules/js_include.pl "$$(basename $@)" > "$@" + @if [ -e "$@" ]; then chmod -w "$@"; fi + +src/run_prettify.js: js-modules/*.js js-modules/*.pl + @if [ -e "$@" ]; then chmod +w "$@"; fi + @perl js-modules/js_include.pl "$$(basename $@)" > "$@" + @if [ -e "$@" ]; then chmod -w "$@"; fi distrib: distrib.tstamp distrib/prettify-small.tgz distrib/prettify-small.zip distrib/prettify-small.tar.bz2 @wc -c distrib/prettify-small.{tar.bz2,tgz,zip} \ | grep -v total -distrib.tstamp: src/*.js src/*.css +distrib.tstamp: src/prettify.js src/run_prettify.js src/*.js src/*.css @echo Compiling @mkdir -p $(TAR_ROOT) @for f in src/*.css; do \ @@ -56,11 +44,21 @@ distrib.tstamp: src/*.js src/*.css | grep -v total; \ done @$(CLOSURE_COMPILER) --js src/prettify.js \ - --externs closure-compiler/console-externs.js \ - --externs closure-compiler/amd-externs.js \ + --externs tools/closure-compiler/console-externs.js \ + --externs tools/closure-compiler/amd-externs.js \ + --define IN_GLOBAL_SCOPE=true \ + --output_wrapper='!function(){%output%}()' \ > $(TAR_ROOT)/prettify.js @wc -c src/prettify.js $(TAR_ROOT)/prettify.js \ | grep -v total + @$(CLOSURE_COMPILER) --js src/run_prettify.js \ + --externs tools/closure-compiler/console-externs.js \ + --externs tools/closure-compiler/amd-externs.js \ + --define IN_GLOBAL_SCOPE=false \ + --output_wrapper='!function(){%output%}()' \ + > $(TAR_ROOT)/run_prettify.js + @wc -c src/run_prettify.js $(TAR_ROOT)/run_prettify.js \ + | grep -v total @for f in src/lang*.js; do \ if [ $$f -nt $(TAR_ROOT)/$$(basename $$f) ]; then \ $(CLOSURE_COMPILER) --js $$f --externs js-modules/externs.js \ @@ -82,19 +80,32 @@ distrib.tstamp: src/*.js src/*.css done @touch distrib.tstamp +lang-aliases : lang-aliases.tstamp +lang-aliases.tstamp : distrib.tstamp + @tools/lang-handler-aliases.sh \ + distrib/sources/google-code-prettify/src \ + | perl -ne 'system("cp $$1 $$2") if m/^(\S+) (\S+)$$/ && ! -e $$2' \ + && touch lang-aliases.tstamp + %.tgz: %.tar @gzip -c -9 $^ > $@ %.tar.bz2: %.tar - @bzip2 -9f $^ + @bzip2 -k -9f $^ distrib/prettify-small.tar: distrib.tstamp - @pushd distrib >& /dev/null; \ - tar cf ../$@ google-code-prettify; \ - popd >& /dev/null + tar cf $@ -C distrib google-code-prettify distrib/prettify-small.zip: distrib.tstamp @pushd distrib >& /dev/null; \ rm -f ../$@; \ zip -q -9 -r ../$@ google-code-prettify; \ popd >& /dev/null + +distrib/prettify.tar: distrib.tstamp + mkdir -p distrib/sources/google-code-prettify + cp -fr CHANGES.html COPYING README.html Makefile \ + examples js-modules src styles tests tools \ + distrib/sources/google-code-prettify + tar cf distrib/prettify.tar -C distrib/sources google-code-prettify + diff --git a/README.html b/README.html index 967d613..d1fd4a1 100644 --- a/README.html +++ b/README.html @@ -12,6 +12,7 @@ @@ -22,15 +23,11 @@

Javascript code prettifier

Setup

  1. Download a distribution -
  2. Include the script and stylesheets in your document - (you will need to make sure the css and js file are on your server, and - adjust the paths in the script and link tag) +
  3. Include the script tag below in your document
    -<link href="prettify.css" type="text/css" rel="stylesheet" />
    -<script type="text/javascript" src="prettify.js"></script>
    -
  4. Add onload="prettyPrint()" to your - document's body tag. -
  5. Modify the stylesheet to get the coloring you prefer
  6. +>script src="https://google-code-prettify.googlecode.com/svn/loader/run_prettify.js></script> +
  7. See Getting Started to configure that URL with options you need. +
  8. Look at the skin gallery and pick styles that suit you.

Usage

@@ -66,47 +63,43 @@

FAQ

For which languages does it work?

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

- -

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

-

And similarly for - Clojure, - CSS, - Go, - Haskell, - Lua, - OCAML, SML, F#, - Nemerle, - Protocol Buffers, - Scala, - SQL, - TeX, LaTeX, - VHDL, - Visual Basic, - WikiText, - XQuery, and - YAML. + and Ruby, but, because of commenting conventions, but doesn't work on + Smalltalk.

+ +

Other languages are supported via extensions: +

+ Apollo + Basic + Clojure + CSS + Dart + Erlang + Go + Haskell + Lisp, Scheme + Llvm + Lua + Matlab + MLs:F#, Ocaml,SML + Mumps + Nemerle + Pascal + Protocol buffers + R, S + RD + Scala + SQL + TCL + Latek + Visual Basic + CHDL + Wiki + XQ + YAML +

If you'd like to add an extension for your favorite language, please look at src/lang-lisp.js and file an @@ -223,11 +216,18 @@

How can I customize the colors and styles of my code?

theme gallery for examples.

+

I can't add classes to my code (because it comes from Markdown, etc.)

+

+ Instead of <pre class="prettyprint ..."> you can use a + comment or processing instructions that survives processing instructions : + <?prettify ...?> works as explained in + Getting Started

+


diff --git a/examples/quine.html b/examples/quine.html new file mode 100644 index 0000000..b53bac0 --- /dev/null +++ b/examples/quine.html @@ -0,0 +1,55 @@ + + + + +Making Quines Prettier + + + + + + +

Making Quines Prettier

+ +

+Below is the content of this page prettified. The <pre> +element is prettified because it has class="prettyprint" and +because the sourced script loads a JavaScript library that styles source +code. +

+ +

+The line numbers to the left appear because the preceding comment +<?prettify lang=html linenums=true?> turns on +line-numbering and the +stylesheet +(see skin=sunburst in the <script src>) +specifies that every fifth line should be numbered. +

+ + + +

+
+
+
diff --git a/js-modules/extractSourceSpans.js b/js-modules/extractSourceSpans.js
index 07d7569..5654cef 100644
--- a/js-modules/extractSourceSpans.js
+++ b/js-modules/extractSourceSpans.js
@@ -52,34 +52,32 @@ function extractSourceSpans(node, isPreformatted) {
   var k = 0;
 
   function walk(node) {
-    switch (node.nodeType) {
-      case 1:  // Element
-        if (nocode.test(node.className)) { return; }
-        for (var child = node.firstChild; child; child = child.nextSibling) {
-          walk(child);
+    var type = node.nodeType;
+    if (type == 1) {  // Element
+      if (nocode.test(node.className)) { return; }
+      for (var child = node.firstChild; child; child = child.nextSibling) {
+        walk(child);
+      }
+      var nodeName = node.nodeName.toLowerCase();
+      if ('br' === nodeName || 'li' === nodeName) {
+        chunks[k] = '\n';
+        spans[k << 1] = length++;
+        spans[(k++ << 1) | 1] = node;
+      }
+    } else if (type == 3 || type == 4) {  // Text
+      var text = node.nodeValue;
+      if (text.length) {
+        if (!isPreformatted) {
+          text = text.replace(/[ \t\r\n]+/g, ' ');
+        } else {
+          text = text.replace(/\r\n?/g, '\n');  // Normalize newlines.
         }
-        var nodeName = node.nodeName.toLowerCase();
-        if ('br' === nodeName || 'li' === nodeName) {
-          chunks[k] = '\n';
-          spans[k << 1] = length++;
-          spans[(k++ << 1) | 1] = node;
-        }
-        break;
-      case 3: case 4:  // Text
-        var text = node.nodeValue;
-        if (text.length) {
-          if (!isPreformatted) {
-            text = text.replace(/[ \t\r\n]+/g, ' ');
-          } else {
-            text = text.replace(/\r\n?/g, '\n');  // Normalize newlines.
-          }
-          // TODO: handle tabs here?
-          chunks[k] = text;
-          spans[k << 1] = length;
-          length += text.length;
-          spans[(k++ << 1) | 1] = node;
-        }
-        break;
+        // TODO: handle tabs here?
+        chunks[k] = text;
+        spans[k << 1] = length;
+        length += text.length;
+        spans[(k++ << 1) | 1] = node;
+      }
     }
   }
 
diff --git a/js-modules/js_include.pl b/js-modules/js_include.pl
new file mode 100644
index 0000000..8d3b623
--- /dev/null
+++ b/js-modules/js_include.pl
@@ -0,0 +1,76 @@
+#!/usr/bin/perl
+
+# Given a JS file looks for lines like
+#    include("path/to/file/to/include");
+# and replaces them with the quoted file relative to the js-modules directory.
+# If the included file ends with ".pl" then it is treated as a perl file to
+# execute and the stdout is used as the JS to include.
+
+use strict;
+
+# Closure Compiler @define annotations that need to be pulled out of included
+# files because @defines need to be top-level vars.
+my $global_defs = "";
+
+# Find @defines at the top of a JS file by pulling off comments and looking for
+# comments containing @define followed by a var declaration.
+sub extractGlobalDefs($) {
+  my @headerComments;
+  my $s = shift;
+  while ($s) {
+    last unless $s =~ m#^\s*(?://[^\r\n]*|/\*.*?\*/[ \t]*)[\r\n]*#s;
+    my $comment = $&;
+    $s = $';
+    if ($comment =~ /[\@]define/ && $s =~ /^\s*var\s+[^;]+;[ \t]*[\r\n]*/) {
+      my $global = $&;
+      $s = $';
+      $global =~ s/(var\s*IN_GLOBAL_SCOPE\s*=\s*)true\b/$1false/;
+      $global_defs .= "$comment$global";
+    } else {
+      push(@headerComments, $comment);
+    }
+  }
+  return (join "", @headerComments) . $s;
+}
+
+# readInclude(whiteSpacePrefix, path) returns the JS content at path
+# (with the ".pl" adjustment above) and prepends each line with the
+# whitespace in whiteSpacePrefix to produce a chunk of JS that matches the
+# indentation of the including file.
+# @defines are extracted so that they can all appear globally at the top of
+# the file.
+sub readInclude($$) {
+  my $prefix = shift;
+  my $name = "js-modules/" . (shift);
+  my $in;
+  if ($name =~ /\.pl$/) {
+    open($in, "perl $name|") or die "$name: $!";
+  } else {
+    open($in, "<$name")      or die "$name: $!";
+  }
+  my $buf = "";
+  while (<$in>) {
+    if (m/(\s*)include\("([^"]+)"\);\s*$/) {
+      my $inc = extractGlobalDefs(readInclude("$prefix$1", $2));
+      $buf .= $inc;
+    } else {
+      $buf .= "$prefix$_";
+    }
+  }
+  close($in);
+  return $buf;
+}
+
+my $target = shift;
+my $inc = readInclude("", $target);
+my $header = "";
+# Put descriptive top level comments above the grouped @defines.
+if ($inc =~ s#^(?://[^\r\n]*|/\*.*?\*/|\s)+##s) {
+  $header = $&;
+}
+my $globals = $global_defs;
+# Un-indent @defines.
+$globals =~ s#^[ \t]*##gm;
+$globals .= "\n" unless $globals eq "";
+
+print "$header$globals$inc";
diff --git a/js-modules/numberLines.js b/js-modules/numberLines.js
index 66ab8c4..840fe0b 100644
--- a/js-modules/numberLines.js
+++ b/js-modules/numberLines.js
@@ -24,42 +24,37 @@ function numberLines(node, opt_startLineNum, isPreformatted) {
   var listItems = [li];
 
   function walk(node) {
-    switch (node.nodeType) {
-      case 1:  // Element
-        if (nocode.test(node.className)) { break; }
-        if ('br' === node.nodeName) {
-          breakAfter(node);
-          // Discard the 
since it is now flush against a . - if (node.parentNode) { - node.parentNode.removeChild(node); - } - } else { - for (var child = node.firstChild; child; child = child.nextSibling) { - walk(child); - } + var type = node.nodeType; + if (type == 1 && !nocode.test(node.className)) { // Element + if ('br' === node.nodeName) { + breakAfter(node); + // Discard the
since it is now flush against a . + if (node.parentNode) { + node.parentNode.removeChild(node); } - break; - case 3: case 4: // Text - if (isPreformatted) { - var text = node.nodeValue; - var match = text.match(lineBreak); - if (match) { - var firstLine = text.substring(0, match.index); - node.nodeValue = firstLine; - var tail = text.substring(match.index + match[0].length); - if (tail) { - var parent = node.parentNode; - parent.insertBefore( - document.createTextNode(tail), node.nextSibling); - } - breakAfter(node); - if (!firstLine) { - // Don't leave blank text nodes in the DOM. - node.parentNode.removeChild(node); - } - } + } else { + for (var child = node.firstChild; child; child = child.nextSibling) { + walk(child); } - break; + } + } else if ((type == 3 || type == 4) && isPreformatted) { // Text + var text = node.nodeValue; + var match = text.match(lineBreak); + if (match) { + var firstLine = text.substring(0, match.index); + node.nodeValue = firstLine; + var tail = text.substring(match.index + match[0].length); + if (tail) { + var parent = node.parentNode; + parent.insertBefore( + document.createTextNode(tail), node.nextSibling); + } + breakAfter(node); + if (!firstLine) { + // Don't leave blank text nodes in the DOM. + node.parentNode.removeChild(node); + } + } } } diff --git a/js-modules/prettify.js b/js-modules/prettify.js index 63f03b2..2c46ef2 100644 --- a/js-modules/prettify.js +++ b/js-modules/prettify.js @@ -55,6 +55,9 @@ // JSLint declarations /*global console, document, navigator, setTimeout, window, define */ +/** @define {boolean} */ +var IN_GLOBAL_SCOPE = true; + /** * Split {@code prettyPrint} into multiple timeouts so as not to interfere with * UI events. @@ -63,18 +66,23 @@ window['PR_SHOULD_USE_CONTINUATION'] = true; /** - * Find all the {@code
} and {@code } tags in the DOM with
- * {@code class=prettyprint} and prettify them.
- *
- * @param {Function?} opt_whenDone if specified, called when the last entry
- *     has been finished.
+ * Pretty print a chunk of code.
+ * @param {string} sourceCodeHtml The HTML to pretty print.
+ * @param {string} opt_langExtension The language name to use.
+ *     Typically, a filename extension like 'cpp' or 'java'.
+ * @param {number|boolean} opt_numberLines True to number lines,
+ *     or the 1-indexed number of the first line in sourceCodeHtml.
+ * @return {string} code as html, but prettier
  */
 var prettyPrintOne;
 /**
- * Pretty print a chunk of code.
+ * Find all the {@code 
} and {@code } tags in the DOM with
+ * {@code class=prettyprint} and prettify them.
  *
- * @param {string} sourceCodeHtml code as html
- * @return {string} code as html, but prettier
+ * @param {Function} opt_whenDone called when prettifying is done.
+ * @param {HTMLElement|HTMLDocument} opt_root an element or document
+ *   containing all the elements to pretty print.
+ *   Defaults to {@code document.body}.
  */
 var prettyPrint;
 
@@ -86,22 +94,22 @@ var prettyPrint;
   // and to defeat aggressive optimizers that fold large string constants.
   var FLOW_CONTROL_KEYWORDS = ["break,continue,do,else,for,if,return,while"];
   var C_KEYWORDS = [FLOW_CONTROL_KEYWORDS,"auto,case,char,const,default," + 
-      "double,enum,extern,float,goto,int,long,register,short,signed,sizeof," +
-      "static,struct,switch,typedef,union,unsigned,void,volatile"];
+      "double,enum,extern,float,goto,inline,int,long,register,short,signed," +
+      "sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];
   var COMMON_KEYWORDS = [C_KEYWORDS,"catch,class,delete,false,import," +
       "new,operator,private,protected,public,this,throw,true,try,typeof"];
   var CPP_KEYWORDS = [COMMON_KEYWORDS,"alignof,align_union,asm,axiom,bool," +
-      "concept,concept_map,const_cast,constexpr,decltype," +
-      "dynamic_cast,explicit,export,friend,inline,late_check," +
-      "mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast," +
-      "template,typeid,typename,using,virtual,where"];
+      "concept,concept_map,const_cast,constexpr,decltype,delegate," +
+      "dynamic_cast,explicit,export,friend,generic,late_check," +
+      "mutable,namespace,nullptr,property,reinterpret_cast,static_assert," +
+      "static_cast,template,typeid,typename,using,virtual,where"];
   var JAVA_KEYWORDS = [COMMON_KEYWORDS,
-      "abstract,boolean,byte,extends,final,finally,implements,import," +
-      "instanceof,null,native,package,strictfp,super,synchronized,throws," +
-      "transient"];
+      "abstract,assert,boolean,byte,extends,final,finally,implements,import," +
+      "instanceof,interface,null,native,package,strictfp,super,synchronized," +
+      "throws,transient"];
   var CSHARP_KEYWORDS = [JAVA_KEYWORDS,
       "as,base,by,checked,decimal,delegate,descending,dynamic,event," +
-      "fixed,foreach,from,group,implicit,in,interface,internal,into,is,let," +
+      "fixed,foreach,from,group,implicit,in,internal,into,is,let," +
       "lock,object,out,override,orderby,params,partial,readonly,ref,sbyte," +
       "sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort," +
       "var,virtual,where"];
@@ -122,10 +130,13 @@ var prettyPrint;
       "def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo," +
       "rescue,retry,self,super,then,true,undef,unless,until,when,yield," +
       "BEGIN,END"];
+   var RUST_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "as,assert,const,copy,drop," +
+      "enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv," +
+      "pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"];
   var SH_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "case,done,elif,esac,eval,fi," +
       "function,in,local,set,then,until"];
   var ALL_KEYWORDS = [
-      CPP_KEYWORDS, CSHARP_KEYWORDS, JSCRIPT_KEYWORDS, PERL_KEYWORDS +
+      CPP_KEYWORDS, CSHARP_KEYWORDS, JSCRIPT_KEYWORDS, PERL_KEYWORDS,
       PYTHON_KEYWORDS, RUBY_KEYWORDS, SH_KEYWORDS];
   var C_TYPES = /^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/;
 
@@ -486,7 +497,18 @@ var prettyPrint;
       fallthroughStylePatterns.push(
           [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
     }
-    if (options['regexLiterals']) {
+    var regexLiterals = options['regexLiterals'];
+    if (regexLiterals) {
+      /**
+       * @const
+       */
+      var regexExcls = regexLiterals > 1
+        ? ''  // Multiline regex literals
+        : '\n\r';
+      /**
+       * @const
+       */
+      var regexAny = regexExcls ? '.' : '[\\S\\s]';
       /**
        * @const
        */
@@ -494,18 +516,19 @@ var prettyPrint;
           // A regular expression literal starts with a slash that is
           // not followed by * or / so that it is not confused with
           // comments.
-          '/(?=[^/*])'
+          '/(?=[^/*' + regexExcls + '])'
           // and then contains any number of raw characters,
-          + '(?:[^/\\x5B\\x5C]'
+          + '(?:[^/\\x5B\\x5C' + regexExcls + ']'
           // escape sequences (\x5C),
-          +    '|\\x5C[\\s\\S]'
+          +    '|\\x5C' + regexAny
           // or non-nesting character sets (\x5B\x5D);
-          +    '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+'
+          +    '|\\x5B(?:[^\\x5C\\x5D' + regexExcls + ']'
+          +             '|\\x5C' + regexAny + ')*(?:\\x5D|$))+'
           // finally closed by a /.
           + '/');
       fallthroughStylePatterns.push(
           ['lang-regex',
-           new RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')')
+           RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')')
            ]);
     }
 
@@ -560,7 +583,10 @@ var prettyPrint;
       // If that does turn out to be a problem, we should change the below
       // when hc is truthy to include # in the run of punctuation characters
       // only when not followint [|&;<>].
-      /^.[^\s\w\.$@\'\"\`\/\\]*/;
+      '^.[^\\s\\w.$@\'"`/\\\\]*';
+    if (options['regexLiterals']) {
+      punctuation += '(?!\s*\/)';
+    }
 
     fallthroughStylePatterns.push(
         // TODO(mikesamuel): recognize non-latin letters and numerals in idents
@@ -580,9 +606,10 @@ var prettyPrint;
              // with an optional modifier like UL for unsigned long
              + '[a-z]*', 'i'),
          null, '0123456789'],
-        // Don't treat escaped quotes in bash as starting strings.  See issue 144.
+        // Don't treat escaped quotes in bash as starting strings.
+        // See issue 144.
         [PR_PLAIN,       /^\\[\s\S]?/, null],
-        [PR_PUNCTUATION, punctuation, null]);
+        [PR_PUNCTUATION, new RegExp(punctuation), null]);
 
     return createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns);
   }
@@ -702,30 +729,30 @@ var prettyPrint;
           'keywords': SH_KEYWORDS,
           'hashComments': true,
           'multiLineStrings': true
-        }), ['bsh', 'csh', 'sh']);
+        }), ['bash', 'bsh', 'csh', 'sh']);
   registerLangHandler(sourceDecorator({
           'keywords': PYTHON_KEYWORDS,
           'hashComments': true,
           'multiLineStrings': true,
           'tripleQuotedStrings': true
-        }), ['cv', 'py']);
+        }), ['cv', 'py', 'python']);
   registerLangHandler(sourceDecorator({
           'keywords': PERL_KEYWORDS,
           'hashComments': true,
           'multiLineStrings': true,
-          'regexLiterals': true
+          'regexLiterals': 2  // multiline regex literals
         }), ['perl', 'pl', 'pm']);
   registerLangHandler(sourceDecorator({
           'keywords': RUBY_KEYWORDS,
           'hashComments': true,
           'multiLineStrings': true,
           'regexLiterals': true
-        }), ['rb']);
+        }), ['rb', 'ruby']);
   registerLangHandler(sourceDecorator({
           'keywords': JSCRIPT_KEYWORDS,
           'cStyleComments': true,
           'regexLiterals': true
-        }), ['js']);
+        }), ['javascript', 'js']);
   registerLangHandler(sourceDecorator({
           'keywords': COFFEE_KEYWORDS,
           'hashComments': 3,  // ### style block comments
@@ -734,6 +761,11 @@ var prettyPrint;
           'tripleQuotedStrings': true,
           'regexLiterals': true
         }), ['coffee']);
+  registerLangHandler(sourceDecorator({
+          'keywords': RUST_KEYWORDS,
+          'cStyleComments': true,
+          'multilineStrings': true
+        }), ['rc', 'rs', 'rust']);
   registerLangHandler(
       createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']);
 
@@ -757,24 +789,30 @@ var prettyPrint;
       recombineTagsAndDecorations(job);
     } catch (e) {
       if (win['console']) {
-        console['log'](e && e['stack'] ? e['stack'] : e);
+        console['log'](e && e['stack'] || e);
       }
     }
   }
 
   /**
+   * Pretty print a chunk of code.
    * @param sourceCodeHtml {string} The HTML to pretty print.
    * @param opt_langExtension {string} The language name to use.
    *     Typically, a filename extension like 'cpp' or 'java'.
    * @param opt_numberLines {number|boolean} True to number lines,
    *     or the 1-indexed number of the first line in sourceCodeHtml.
    */
-  function prettyPrintOne(sourceCodeHtml, opt_langExtension, opt_numberLines) {
-    var container = document.createElement('pre');
+  function $prettyPrintOne(sourceCodeHtml, opt_langExtension, opt_numberLines) {
+    var container = document.createElement('div');
     // This could cause images to load and onload listeners to fire.
     // E.g. .
     // We assume that the inner HTML is from a trusted source.
-    container.innerHTML = sourceCodeHtml;
+    // The pre-tag is required for IE8 which strips newlines from innerHTML
+    // when it is injected into a 
 tag.
+    // http://stackoverflow.com/questions/451486/pre-tag-loses-line-breaks-when-setting-innerhtml-in-ie
+    // http://stackoverflow.com/questions/195363/inserting-a-newline-into-a-pre-tag-ie-javascript
+    container.innerHTML = '
' + sourceCodeHtml + '
'; + container = container.firstChild; if (opt_numberLines) { numberLines(container, opt_numberLines, true); } @@ -789,8 +827,19 @@ var prettyPrint; return container.innerHTML; } - function prettyPrint(opt_whenDone) { - function byTagName(tn) { return document.getElementsByTagName(tn); } + /** + * Find all the {@code
} and {@code } tags in the DOM with
+    * {@code class=prettyprint} and prettify them.
+    *
+    * @param {Function} opt_whenDone called when prettifying is done.
+    * @param {HTMLElement|HTMLDocument} opt_root an element or document
+    *   containing all the elements to pretty print.
+    *   Defaults to {@code document.body}.
+    */
+  function $prettyPrint(opt_whenDone, opt_root) {
+    var root = opt_root || document.body;
+    var doc = root.ownerDocument || document;
+    function byTagName(tn) { return root.getElementsByTagName(tn); }
     // fetch a list of nodes to rewrite
     var codeSegments = [byTagName('pre'), byTagName('code'), byTagName('xmp')];
     var elements = [];
@@ -817,6 +866,7 @@ var prettyPrint;
     var preformattedTagNameRe = /pre|xmp/i;
     var codeRe = /^code$/i;
     var preCodeXmpRe = /^(?:pre|code|xmp)$/i;
+    var EMPTY = {};
 
     function doWork() {
       var endTime = (win['PR_SHOULD_USE_CONTINUATION'] ?
@@ -824,8 +874,34 @@ var prettyPrint;
                      Infinity);
       for (; k < elements.length && clock['now']() < endTime; k++) {
         var cs = elements[k];
+
+        // Look for a preceding comment like
+        // 
+        var attrs = EMPTY;
+        {
+          for (var preceder = cs; (preceder = preceder.previousSibling);) {
+            var nt = preceder.nodeType;
+            //  is parsed by HTML 5 to a comment node (8)
+            // like , but in XML is a processing instruction
+            var value = (nt === 7 || nt === 8) && preceder.nodeValue;
+            if (value
+                ? !/^\??prettify\b/.test(value)
+                : (nt !== 3 || /\S/.test(preceder.nodeValue))) {
+              // Skip over white-space text nodes but not others.
+              break;
+            }
+            if (value) {
+              attrs = {};
+              value.replace(
+                  /\b(\w+)=([\w:.%+-]+)/g,
+                function (_, name, value) { attrs[name] = value; });
+              break;
+            }
+          }
+        }
+
         var className = cs.className;
-        if (prettyPrintRe.test(className)
+        if ((attrs !== EMPTY || prettyPrintRe.test(className))
             // Don't redo this if we've already done it.
             // This allows recalling pretty print to just prettyprint elements
             // that have been added to the page since last call.
@@ -854,27 +930,31 @@ var prettyPrint;
             // HTML5 recommends that a language be specified using "language-"
             // as the prefix instead.  Google Code Prettify supports both.
             // http://dev.w3.org/html5/spec-author-view/the-code-element.html
-            var langExtension = className.match(langExtensionRe);
-            // Support 

-            var wrapper;
-            if (!langExtension && (wrapper = childContentWrapper(cs))
-                && codeRe.test(wrapper.tagName)) {
-              langExtension = wrapper.className.match(langExtensionRe);
-            }
+            var langExtension = attrs['lang'];
+            if (!langExtension) {
+              langExtension = className.match(langExtensionRe);
+              // Support 

+              var wrapper;
+              if (!langExtension && (wrapper = childContentWrapper(cs))
+                  && codeRe.test(wrapper.tagName)) {
+                langExtension = wrapper.className.match(langExtensionRe);
+              }
 
-            if (langExtension) { langExtension = langExtension[1]; }
+              if (langExtension) { langExtension = langExtension[1]; }
+            }
 
             var preformatted;
             if (preformattedTagNameRe.test(cs.tagName)) {
               preformatted = 1;
             } else {
               var currentStyle = cs['currentStyle'];
+              var defaultView = doc.defaultView;
               var whitespace = (
                   currentStyle
                   ? currentStyle['whiteSpace']
-                  : (document.defaultView
-                     && document.defaultView.getComputedStyle)
-                  ? document.defaultView.getComputedStyle(cs, null)
+                  : (defaultView
+                     && defaultView.getComputedStyle)
+                  ? defaultView.getComputedStyle(cs, null)
                   .getPropertyValue('white-space')
                   : 0);
               preformatted = whitespace
@@ -883,10 +963,15 @@ var prettyPrint;
 
             // Look for a class like linenums or linenums: where  is the
             // 1-indexed number of the first line.
-            var lineNums = cs.className.match(/\blinenums\b(?::(\d+))?/);
-            lineNums = lineNums
-                ? lineNums[1] && lineNums[1].length ? +lineNums[1] : true
+            var lineNums = attrs['linenums'];
+            if (!(lineNums = lineNums === 'true' || +lineNums)) {
+              lineNums = className.match(/\blinenums\b(?::(\d+))?/);
+              lineNums =
+                lineNums
+                ? lineNums[1] && lineNums[1].length
+                  ? +lineNums[1] : true
                 : false;
+            }
             if (lineNums) { numberLines(cs, lineNums, preformatted); }
 
             // do the pretty printing
@@ -903,7 +988,7 @@ var prettyPrint;
       if (k < elements.length) {
         // finish up in a continuation
         setTimeout(doWork, 250);
-      } else if (opt_whenDone) {
+      } else if ('function' === typeof opt_whenDone) {
         opt_whenDone();
       }
     }
@@ -932,8 +1017,14 @@ var prettyPrint;
         'PR_STRING': PR_STRING,
         'PR_TAG': PR_TAG,
         'PR_TYPE': PR_TYPE,
-        'prettyPrintOne': win['prettyPrintOne'] = prettyPrintOne,
-        'prettyPrint': win['prettyPrint'] = prettyPrint
+        'prettyPrintOne':
+           IN_GLOBAL_SCOPE
+             ? (win['prettyPrintOne'] = $prettyPrintOne)
+             : (prettyPrintOne = $prettyPrintOne),
+        'prettyPrint': prettyPrint =
+           IN_GLOBAL_SCOPE
+             ? (win['prettyPrint'] = $prettyPrint)
+             : (prettyPrint = $prettyPrint)
       };
 
   // Make PR available via the Asynchronous Module Definition (AMD) API.
diff --git a/js-modules/recombineTagsAndDecorations.js b/js-modules/recombineTagsAndDecorations.js
index 33a8fb3..62d2c5a 100644
--- a/js-modules/recombineTagsAndDecorations.js
+++ b/js-modules/recombineTagsAndDecorations.js
@@ -3,6 +3,7 @@
  * {@code job.decorations} and modifies {@code job.sourceNode} in place.
  * @param {Object} job like 
{
  *    sourceCode: {string} source as plain text,
+ *    sourceNode: {HTMLElement} the element containing the source,
  *    spans: {Array.} alternating span start indices into source
  *       and the text node or element (e.g. {@code 
}) corresponding to that * span. diff --git a/js-modules/run_prettify.js b/js-modules/run_prettify.js new file mode 100644 index 0000000..8c1e6d5 --- /dev/null +++ b/js-modules/run_prettify.js @@ -0,0 +1,250 @@ +// Copyright (C) 2013 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +// Looks at query parameters to decide which language handlers and style-sheets +// to load. + +// Query Parameter Format Effect Default +// +------------------+---------------+------------------------------+--------+ +// | autorun= | true | false | If true then prettyPrint() | "true" | +// | | | is called on page load. | | +// +------------------+---------------+------------------------------+--------+ +// | lang= | language name | Loads the language handler | Can | +// | | | named "lang-.js". | appear | +// | | | See available handlers at | many | +// | | | http://code.google.com/p/ | times. | +// | | | google-code-prettify/source/ | | +// | | | browse/trunk/src | | +// +------------------+---------------+------------------------------+--------+ +// | skin= | skin name | Loads the skin stylesheet | none. | +// | | | named ".css". | | +// | | | http://code.google.com/p/ | | +// | | | google-code-prettify/source/ | | +// | | | browse/trunk/styles | | +// +------------------+---------------+------------------------------+--------+ +// | callback= | JS identifier | When "prettyPrint" finishes | none | +// | | | window.exports[js_ident] is | | +// | | | called. | | +// | | | The callback must be under | | +// | | | exports to reduce the risk | | +// | | | of XSS via query parameter | | +// | | | injection. | | +// +------------------+---------------+------------------------------+--------+ + +// Exmaples +// .../prettify.js?lang=css&skin=sunburst +// 1. Loads the CSS language handler which can be used to prettify CSS +// stylesheets, HTML