Skip to content
This repository was archived by the owner on Apr 22, 2020. It is now read-only.

Commit 60cf2e0

Browse files
author
mikesamuel
committed
Added support for 'nocode' spans to allow entire sections of markup to be placed outside the flow of source code tokens. google.code plans to use this for line numbers and code review comments.
1 parent 07b3779 commit 60cf2e0

File tree

4 files changed

+188
-39
lines changed

4 files changed

+188
-39
lines changed

CHANGES.html

+11-3
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ <h2>29 March 2007</h2>
2727
(<a href="tests/prettify_test.html#issue8">test</a>)
2828
<li>Fixed entity handling so that the caveat
2929
<blockquote>
30-
<p>Caveats: please properly escape less-thans. <tt>x&amp;lt;y</tt>
31-
instead of <tt>x&lt;y</tt>, and use <tt>&quot;</tt> instead of
32-
<tt>&amp;quot;</tt> for string delimiters.</p>
30+
<p>Caveats: please properly escape less-thans. <tt>x&amp;lt;y</tt>
31+
instead of <tt>x&lt;y</tt>, and use <tt>&quot;</tt> instead of
32+
<tt>&amp;quot;</tt> for string delimiters.</p>
3333
</blockquote>
3434
is no longer applicable.
3535
<li>Added noisefree's C#
@@ -49,5 +49,13 @@ <h2>5 Jul 2008</h2>
4949
<ul>
5050
<li>Defined language extensions for Lisp and LUA</code>
5151
</ul>
52+
<h2>14 Jul 2008</h2>
53+
<ul>
54+
<li>Language handlers for F#, OCAML, SQL</code>
55+
<li>Support for <code>nocode</code> spans to allow embedding of line
56+
numbers and code annotations which should not be styled or otherwise
57+
affect the tokenization of prettified code.
58+
See the issue 22 <a href="tests/prettify_test.html#issue22">testcase</a>.</code>
59+
</ul>
5260
</body>
5361
</html>

README.html

+18
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,24 @@ <h3>Why doesn't Prettyprinting of strings work on WordPress?</h3>
118118
>WordPress's help center</a> for info on how to stop smart quoting of code
119119
snippets.</p>
120120

121+
<h3>How do I put line numbers in my code?</h3>
122+
<p>You can use the <code>nocode</code> class to identify a span of markup
123+
that is not code.
124+
<pre>&lt;pre class=prettyprint&gt;
125+
&lt;span class="<b>nocode</b>"&gt;1:&lt;/span&gt; /* This is line 1 of my code
126+
&lt;span class="<b>nocode</b>"&gt;2:&lt;/span&gt; * and here's line 2 */
127+
&lt;span class="<b>nocode</b>"&gt;3:&lt;/span&gt; print("I'm line number 3");
128+
&lt;/pre&gt;</pre>
129+
produces
130+
<pre class=prettyprint>
131+
<span class="nocode">1:</span> /* This is line 1 of my code
132+
<span class="nocode">2:</span> * and here's line 2 */
133+
<span class="nocode">3:</span> print("I'm line number 3");
134+
</pre>
135+
136+
<p>For a more complete example see the issue22
137+
<a href="tests/prettify_test.html#issue22">testcase</a>.</p>
138+
121139
<br><br><br>
122140

123141

src/prettify.js

+68-15
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ var PR_TAB_WIDTH = 8;
6363
var PR_normalizedHtml;
6464

6565
/** Contains functions for creating and registering new language handlers.
66-
* @namespace
66+
* @type {Object}
6767
*/
6868
var PR;
6969

@@ -80,11 +80,11 @@ var prettyPrintOne;
8080
*/
8181
var prettyPrint;
8282

83-
/** browser detection. */
84-
function pr_isIE6() {
83+
/** browser detection. @extern */
84+
function _pr_isIE6() {
8585
var isIE6 = navigator && navigator.userAgent &&
8686
/\bMSIE 6\./.test(navigator.userAgent);
87-
pr_isIE6 = function () { return isIE6; };
87+
_pr_isIE6 = function () { return isIE6; };
8888
return isIE6;
8989
}
9090

@@ -170,6 +170,12 @@ function pr_isIE6() {
170170
/** token style for an sgml attribute value. */
171171
var PR_ATTRIB_VALUE = 'atv';
172172

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+
173179
function isWordChar(ch) {
174180
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
175181
}
@@ -366,7 +372,7 @@ function pr_isIE6() {
366372
/** returns a function that expand tabs to spaces. This function can be fed
367373
* successive chunks of text, and will maintain its own internal state to
368374
* keep track of how tabs are expanded.
369-
* @return {function (plainText : string) : string} a function that takes
375+
* @return {function (string) : string} a function that takes
370376
* plain text and return the text with tabs expanded.
371377
* @private
372378
*/
@@ -422,6 +428,7 @@ function pr_isIE6() {
422428
var pr_commentPrefix = /^<!--/;
423429
var pr_cdataPrefix = /^<\[CDATA\[/;
424430
var pr_brPrefix = /^<br\b/i;
431+
var pr_tagNameRe = /^<(\/?)([a-zA-Z]+)/;
425432

426433
/** split markup into chunks of html tags (style null) and
427434
* plain text (style {@link #PR_PLAIN}), converting tags which are
@@ -454,7 +461,33 @@ function pr_isIE6() {
454461
sourceBuf.push('\n');
455462
++sourceBufLen;
456463
} else {
457-
extractedTags.push(sourceBufLen, match);
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+
}
458491
}
459492
} else {
460493
var literalText = htmlToText(match);
@@ -466,6 +499,16 @@ function pr_isIE6() {
466499
return { source: sourceBuf.join(''), tags: extractedTags };
467500
}
468501

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+
469512
/** Given triples of [style, pattern, context] returns a lexing function,
470513
* The lexing function interprets the patterns to find token boundaries and
471514
* returns a decoration list of the form
@@ -494,7 +537,7 @@ function pr_isIE6() {
494537
* @param {Array} fallthroughStylePatterns patterns that will be tried in
495538
* order if the shortcut ones fail. May have shortcuts.
496539
*
497-
* @return {function (sourceCode : string) -> Array.<number|string>} a
540+
* @return {function (string, number?) : Array.<number|string>} a
498541
* function that takes source code and returns a list of decorations.
499542
*/
500543
function createSimpleLexer(shortcutStylePatterns,
@@ -648,7 +691,7 @@ function pr_isIE6() {
648691
* It recognizes C, C++, and shell style comments.
649692
*
650693
* @param {Object} options a set of optional parameters.
651-
* @return {function (sourceCode : string) : Array.<string|number>} a
694+
* @return {function (string) : Array.<string|number>} a
652695
* decorator that takes sourceCode as plain text and that returns a
653696
* decoration list
654697
*/
@@ -921,6 +964,12 @@ function pr_isIE6() {
921964
var decPos = 0; // index into decorations
922965
var tabExpander = makeTabExpander(PR_TAB_WIDTH);
923966

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+
924973
// A helper function that is responsible for opening sections of decoration
925974
// and outputing properly escaped chunks of source
926975
function emitTextUpTo(sourceIdx) {
@@ -936,17 +985,20 @@ function pr_isIE6() {
936985
}
937986
// This interacts badly with some wikis which introduces paragraph tags
938987
// into pre blocks for some strange reason.
939-
// It's necessary for IE though which seems to lose the preformattednes
940-
988+
// It's necessary for IE though which seems to lose the preformattedness
941989
// of <pre> tags when their innerHTML is assigned.
942990
// http://stud3.tuwien.ac.at/~e0226430/innerHtmlQuirk.html
943991
// and it serves to undo the conversion of <br>s to newlines done in
944992
// chunkify.
945993
var htmlChunk = textToHtml(
946994
tabExpander(sourceText.substring(outputIdx, sourceIdx)))
947-
.replace(/(\r\n?|\n| ) /g, '$1&nbsp;')
948-
.replace(/\r\n?|\n/g, '<br />');
949-
html.push(htmlChunk);
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 />'));
9501002
outputIdx = sourceIdx;
9511003
}
9521004
}
@@ -996,7 +1048,7 @@ function pr_isIE6() {
9961048
/** Maps language-specific file extensions to handlers. */
9971049
var langHandlerRegistry = {};
9981050
/** Register a language handler for the given file extensions.
999-
* @param {function (sourceCode : string) : Array.<number|string>} handler
1051+
* @param {function (string) : Array.<number|string>} handler
10001052
* a function from source code to a list of decorations.
10011053
* @param {Array.<string>} fileExtensions
10021054
*/
@@ -1092,7 +1144,7 @@ function pr_isIE6() {
10921144
}
10931145

10941146
function prettyPrint(opt_whenDone) {
1095-
var isIE6 = pr_isIE6();
1147+
var isIE6 = _pr_isIE6();
10961148

10971149
// fetch a list of nodes to rewrite
10981150
var codeSegments = [
@@ -1212,6 +1264,7 @@ function pr_isIE6() {
12121264
'PR_DECLARATION': PR_DECLARATION,
12131265
'PR_KEYWORD': PR_KEYWORD,
12141266
'PR_LITERAL': PR_LITERAL,
1267+
'PR_NOCODE': PR_NOCODE,
12151268
'PR_PLAIN': PR_PLAIN,
12161269
'PR_PUNCTUATION': PR_PUNCTUATION,
12171270
'PR_SOURCE': PR_SOURCE,

0 commit comments

Comments
 (0)