Posting source code in WordPress: escaping and syntax highlighting the inserted code

Date November 4, 2007

I have already mentioned that I had difficulty in finding a suitable solution to post source code in my blog. However good the workaround presented in my previous post is functioning, I was not satisfied with it. I noticed two annoying things. The first was that I had to escape HTML special chars “manually” in the inserted code, which was a pain in the ass. My second problem was (I like to indent my code with TAB) that indentation was too wide, which looked not really nice out (and was impractical when your code has multiple nested control structures). After a little JavaScript hacking and playing with GeSHi and CSS I solved both of them.

A little side note: I found that the built-in rich text editor is almost absolutely useless, so I turned it off, and I suggest it to every WordPress user.

Escaping the inserted code

When you want to put some source code to the post, you usually simply enclose it in code or pre tags. The problem is, that the source code may contain characters which have special meaning in the HTML language, so they should be escaped. These characters are: <, >, &, &qout; and they should be converted to: &lt;, &gt;, &amp;, &qout;. I would like to have some kind of automation for this task, which could be run when we insert the text to the post.

When you insert a code (and use a syntax highlighter) you should specify the language of that code. For example if you use the WP-Syntax plugin you should enclose your code in pre tags and use the lang attribute to specify the language. In addition you can mark if you want line numbers to appear. Enclosing the copy-pasted text is another repetitive and tedious task so it is another good candidate for automation

It would be great if we could do this two task in one step, so I thought I extend the toolbar above the textarea to provide this functionality by adding a new sourcecode button to it:
Toolbar sourcecode button

The sourcecode button works similar as the others button on the toolbar, if no text is selected on the first click it inserts the open tag, on the second click it inserts the close tag. If text is selected it encloses the text in pre tags when you activate it. The button can be accessed via Ctrl-F keyboard shortcut too. Before it ads the open tag to the editor it prompts for the syntax highlighting parameters and generates the proper open tag to them. When text is selected the special characters will be replaced to HTML entities in it. This way adding source code to your post will be very straightforward:

1. Select the text in te editor:
Select the source code
2. Select the inserted text and press Ctrl-F:
Select the inserted text
3. Type the syntax highlighting information:
Type syntax highlighting information
4. The resulted text is:
The resulted text

Extending the toolbar is quite easy, only the wp-includes/js/quicktags.js file should be slightly modified (you can download the fully modified version from here, all the modifications are commented). To add a new button to the toolbar you should add the following code:

127
128
129
130
131
132
133
edButtons[edButtons.length] =
new edButton(&#39;ed_pre&#39;
,&#39;pre&#39;
,&#39;<pre>&#39;
,&#39;</pre>&#39;
,&#39;f&#39;
);

After then we should modify the edInsertTag function, and we are ready:

297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
//------<code modification>-------
// Prompts for syntax highlighting parameter, and return the appropriate start tag.
// TODO: l10n support.
function edGetPreStartTag() {
    var syntaxInfo = prompt(&#39;Please insert syntax highlighting information:&#39;+&qout;\n&qout;+&#39;language[, line number]!&#39;, &#39;php,1&#39;).split(&#39;,&#39;);
    return &#39;<pre lang=&qout;&#39;+syntaxInfo[0]+&#39;&qout;&#39;+(syntaxInfo[1] != undefined ? &#39; line=&qout;&#39;+syntaxInfo[1]+&#39;&qout;&#39; : &#39;&#39;)+&#39;>&#39;+&qout;\n&qout;;
}

// It converts all HTML special chars to HTML entites
function edHtmlSpecialChars(string) {
    return string.replace(/&/g, &#39;&amp;&#39;).replace(/</g, &#39;&lt;&#39;).
           replace(/>/g, &#39;&gt;&#39;).replace(/&qout;/g, &#39;&qout;&#39;).
           replace(/&#39;/g, &#39;&#39;&#39;);
}
//------</code modification>-------

// insertion code

function edInsertTag(myField, i) {
    //IE support
    if (document.selection) {
        myField.focus();
        sel = document.selection.createRange();
        if (sel.text.length > 0) {
            //------<code modification>-------
            if (edButtons[i].id == &#39;ed_pre&#39;) {
                sel.text = edHtmlSpecialChars(sel.text);
                edButtons[i].tagStart = edGetPreStartTag();
            }
            //------</code modification>-------
            sel.text = edButtons[i].tagStart + sel.text + edButtons[i].tagEnd;
        }
        else {
            if (!edCheckOpenTags(i) || edButtons[i].tagEnd == &#39;&#39;) {
                //------<code modification>-------
                if (edButtons[i].id == &#39;ed_pre&#39;) {
                    edButtons[i].tagStart = edGetPreStartTag();
                }
                //------</code modification>-------
                sel.text = edButtons[i].tagStart;
                edAddTag(i);
            }
            else {
                sel.text = edButtons[i].tagEnd;
                edRemoveTag(i);
            }
        }
        myField.focus();
    }
    //MOZILLA/NETSCAPE support
    else if (myField.selectionStart || myField.selectionStart == &#39;0&#39;) {
        var startPos = myField.selectionStart;
        var endPos = myField.selectionEnd;
        var cursorPos = endPos;
        var scrollTop = myField.scrollTop;

        if (startPos != endPos) {
            //------<code modification>-------
            var selectedText = myField.value.substring(startPos, endPos);
            var selectedTextLengthGrowth = 0;
            if (edButtons[i].id == &#39;ed_pre&#39;) {
                edButtons[i].tagStart = edGetPreStartTag();
                selectedTextLengthGrowth = selectedText.length;
                selectedText = edHtmlSpecialChars(selectedText);
                selectedTextLengthGrowth = selectedText.length - selectedTextLengthGrowth;
            }
            myField.value = myField.value.substring(0, startPos)
                          + edButtons[i].tagStart
                          + selectedText
                          + edButtons[i].tagEnd
                          + myField.value.substring(endPos, myField.value.length);
            cursorPos += edButtons[i].tagStart.length + edButtons[i].tagEnd.length + selectedTextLengthGrowth;
            //------</code modification>-------
        }
        else {
            if (!edCheckOpenTags(i) || edButtons[i].tagEnd == &#39;&#39;) {
                //------<code modification>-------
                if (edButtons[i].id == &#39;ed_pre&#39;) {
                    edButtons[i].tagStart = edGetPreStartTag();
                }
                //------</code modification>-------
                myField.value = myField.value.substring(0, startPos)
                              + edButtons[i].tagStart
                              + myField.value.substring(endPos, myField.value.length);
                edAddTag(i);
                cursorPos = startPos + edButtons[i].tagStart.length;
            }
            else {
                myField.value = myField.value.substring(0, startPos)
                              + edButtons[i].tagEnd
                              + myField.value.substring(endPos, myField.value.length);
                edRemoveTag(i);
                cursorPos = startPos + edButtons[i].tagEnd.length;
            }
        }
        myField.focus();
        myField.selectionStart = cursorPos;
        myField.selectionEnd = cursorPos;
        myField.scrollTop = scrollTop;
    }
    else {
        if (!edCheckOpenTags(i) || edButtons[i].tagEnd == &#39;&#39;) {
            //------<code modification>-------
            if (edButtons[i].id == &#39;ed_pre&#39;) {
                edButtons[i].tagStart = edGetPreStartTag();
            }
            //------</code modification>-------
            myField.value += edButtons[i].tagStart;
            edAddTag(i);
        }
        else {
            myField.value += edButtons[i].tagEnd;
            edRemoveTag(i);
        }
        myField.focus();
    }
}

Controlling the indentation

The WP-Syntax plugin uses GeSHi with default settings, which means that GeSHi wraps code blocks in pre tags. In this mode GeSHi doesn’t replace TAB characters with spaces, so we can’t control the appearance of the indentation. But if we instruct GeSHi to use div tags instead of pre, it will replace leading TAB characters to spaces, and you can set the number of spaces. If we want to use 4 spaces we should modify the plugin accordingly to the following:

79
80
81
82
$geshi = new GeSHi(htmlspecialchars_decode($code), $language);
$geshi->enable_keyword_links(false);
$geshi->set_header_type(GESHI_HEADER_DIV);
$geshi->set_tab_width(4);

After this modification you should probably modify your CSS. I use now the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/***** SYNTAX HIGHLIGHTER *****/
.wp_syntax {
    color: #100;
    background-color: #f9f9f9;
    border: 1px solid silver;
    margin: 0 0 1.5em 0;
    overflow: auto;
}

/* IE FIX */
.wp_syntax {
    overflow-x: auto;
    overflow-y: hidden;
    padding-bottom: expression(this.scrollWidth > this.offsetWidth ? 15 : 0);
    width: 100%;
}

.wp_syntax table {
    border-collapse: collapse;
}

.wp_syntax div, .wp_syntax td {
    vertical-align: top;
    padding: 2px 4px;
    white-space: nowrap;
}

.wp_syntax .line_numbers {
    text-align: right;
    background-color: #e8e9dc;
    color: gray;
    overflow: visible;
}

td.code div {
    padding-top: 0px;
}

I think these modifications would be interesting in the official WordPress version too, I found them useful, now I can insert source code to the post in a very convenient way.

16 Responses to “Posting source code in WordPress: escaping and syntax highlighting the inserted code”

  1. bricky said:

    Nice post, well explained.

    I particularly like your /* IE FIX */ in the stylesheet, I’ve been looking for a way around that scrollbar problem for some time. :)

  2. WordPress quicktag toolbar tweaking again | Gergely Hodicska said:

    [...] the text from the clipboard to the attribute. This is a repetitive and tedious task, so like in the previous case I automate [...]

  3. shell8 (:|)[:|] » Refaktoring, elsÅ‘ kör said:

    [...] Lesz fejlesztési rovat, ehhez jól jön majd kódmutogatásnál egy rendes kódszínező, tippek itt. [...]

  4. Il Nissardo, chez Andrea » Blog Archive » Come inserire codice sorgente in un post Wordpress said:

    [...] che i caratteri vengano interpretati da Wordpress. Dopo diverse ricerche ho trovato una soluzione molto elegante grazie a questo blog.. Se avete il mio stesso problema, vi consiglio di darci [...]

  5. Cómo resaltar código fuente en WordPress | Einstein Dream said:

    [...] alguien (Posting source code in WordPress: escaping and syntax highlighting the inserted code) ya se ha tomado la molestia de resolver este problema, y de paso, agregando magnificas [...]

  6. wordpress test blog » Source Code in WordPress posten und Probleme mit dem TinyMCE-Editor eliminieren said:

    [...] wie von Felho beschrieben modifizieren um GeSHi auf die Verwendung von div-Tags umzuschalten [...]

  7. Ximik said:

    Thank you, really very good thing.

  8. Daniele said:

    Very very good post! There is only one mistake in your javascript: you replace the quotation mark with &qout; but (becarful!) the right code is "!

  9. Daniele said:

    Mmm…. In the previous comment the page encoded what I have written….
    The right code is &quot;

  10. Inserire blocchi di codice in WordPress evidenziando la sintassi | BigThink - Idee dal web: gadgets, risorse webmaster, tutorial, programmazione said:

    [...] trovato un articolo che spiega in modo preciso come creare un nuovo pulsante per la barra degli strumenti [...]

  11. Miley-Cyrus-Fan said:

    Hmm.. thank you so much, usefull information

  12. Etiketer said:

    Thanks! Really interesting. I wish i could spend my time on writing articles…just have no time for it.

  13. Colombre’s Corner » Come inserire blocchi di codice in un articolo WordPress said:

    [...] e in questo utile articolo: http://blog.felho.hu/posting-source-code-in-wordpress-escaping-and-syntax-highlighting-the-inserted-... [...]

  14. John Barrett said:

    Thanks for posting this! I am brand new to word press, but I can’t figure this posting code issue. I have tried about every plugin on Word Press, no love for any of them! I upload the GeSHI to the plugin folder, but it does not do the trick either, yikes I feel so stupid that I can’t get this.

    I have a post here:
    http://blog.meleana.info/?p=82

    Can you offer any help to me?
    Thanks so much
    Johnny`-`

    By the way great post, I am going to to over this again and hopefully I can finally fix this issue.

  15. Plugin WP-Syntax et caractères spéciaux - Pierre MARTIN said:

    [...] il semble qu’il y ait un problème associé (sécurité ? sans doute) car l’auteur propose une solution plus élégante … à tester ! Je ne vous garantit donc pas la sécurité de ce [...]

  16. Abraxas » Wordpress Syntax Highlighting mit WP-Syntax said:

    [...] Resultat im Artikel den HTML-Code angezeigt bekommt. Um dieses Problem zu umgehen, habe ich eine Lösung von Gergely Hodicska gefunden, die es zudem ermöglicht, den Quellcode noch komfortabler einzugeben. Im ersten Schritt [...]

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>