Save My <pre> WordPress Plugin, or, WTF-8 My Whitespace

If you’ve tried to use code samples in WordPress, you might have noticed unexpected behavior from the WYSIWYG editor. It will edit the whitespace inside of <pre> tags, leaving you with some poorly formatted, hard-to-read code. Today, I set out to stop that.

What causes this?

Whenever the WYSIWYG editor opens, it sends the content of the post through a few filters. You can find most of these in wp-includes/formatting.php. The culprit here is the function wpautop. This function runs a long list of regular expressions to make your content a little prettier and better formatted. But we don’t want that to happen to our <pre> tags; they’re pre-formatted. We want those left alone. To do that, I had to find a way to keep the content of the <pre> element from going through that filter.

WordPress’s plugin system allows you to change its behavior without altering the core code, so you don’t have to re-alter it every time you upgrade. As easy as it would have been, in the short-term, to just edit wpautop to make it behave properly, I wanted a longer-term solution that would be easier to share with other WordPress users. Therefore, a plugin.

Since a plugin can’t just intercept an arbitrary function call, you have to find a filter hook that can call your function at the appropriate time. wpautop is called by wp_richedit_pre. wp_richedit_pre, in turn, is hooked into the filter system through the hook the_editor_content.

Finally, step one, cut

Remove wp_richedit_pre from the the_editor_content filter.

Step two, redirect

Replace that with a similar function that will call something other than wpautop.

Here I create a new class to hold my plugin’s functions, aptly named PreservePre. The the_editor_content filter will call this class’s preformat_intercept method, which will do the same thing as wp_richedit_pre, but replace the call to wpautop with a call to PreservePre‘s safe_autop method.

Step three, recurse

safe_autop performs a pretty simple function.

  1. If there are no <pre> tags in the text, it just sends everything on to wpautop and returns the result.
  2. If the text starts with a <pre> tag, safe_autop splits it into two parts, the <pre> element and everything after the <pre> element. The latter part is sent back through safe_autop to let recursion run its course. The two parts are then stuck back together and returned.
  3. If there is a <pre> tag anywhere else, safe_autop splits the text into two parts: everything before the <pre> and everything else. Each half is sent back to safe_autop, with the results stuck back together and returned. That means the part before the <pre> will match the first case and be sent to wpautop, the rest will match the second case and continue with the recursion.

So after all that, anything not in a <pre> element goes through wpautop; anything that is in a <pre> element is left alone.

So now we’re in good shape, right? When WordPress opens the WYSIWYG editor, <pre> elements are left alone, their whitespace untouched. But wait, there’s more!

Step four, gnashing of teeth

The WYSIWYG editor has both a “Visual” tab and “Code” tab. If you visit the “Code” tab, you can see all of the HTML for the post. When you go back to the “Visual” tab, the editor sends your content through wpautop again (you might have changed something you shouldn’t have, after all). But this isn’t the same wpautop; this time, it’s JavaScript. Yes, the same function runs client-side anytime you move from the “Code” tab to the “Visual” tab.

Step five, intercept

JavaScript, fortunately, allows you to redefine previously declared functions. So you just rename wpautop to old_wpautop, and redefine wpautop to point to the safe_autop function, reimplemented in JavaScript. Use the wp_print_scripts action hook to tell WordPress to run your script, and you’re all set.

The Plugin

If you’ve made it this far, here’s your reward: the Save My <pre> WordPress plugin.

UPDATE (2008-03-07): Fixed a small bug in the JavaScript. I forgot to check for the existence of wpautop before mucking about with it. This didn’t cause any problems, other than throwing an exception. Version 1.1 is now available from the same place.

UPDATE (2008-03-12): Now also available from http://wordpress.org/extend/plugins/save-my-pre/.

UPDATE (2008-09-27): Recent versions of WordPress don’t eat whitespace like they used to. The WYSIWYG still eliminates any leading or trailing whitespace inside of a <pre>, but everything else remains intact. That seems acceptible to me, so I see no reason to further maintain this plugin. Thanks to all who used it and commented.

15 Responses to Save My <pre> WordPress Plugin, or, WTF-8 My Whitespace

  1. Pingback: I migliori plugin di Wordpress per blogger esperti - Geekissimo

  2. Sharani says:

    I spent all day reading about various workarounds for how to insert line breaks into WordPress and decided to go with your plugin rather than some of the other options like using a different visual editor. Since I am using WordPress to design a public library web site, I hoped to have some of the static pages mimic the look of more traditional web sites. I decided to use your plugin and already fixed the spacing on some images that took up more space than the accompanying text. Bravo! Thank-you so much for writing this plugin.

  3. Sharani says:

    I spoke too soon. after I published it the break tags were stripped out. For us non-geekie types, can you please give an exact example of how you use the tag. Would I type ? I am using version 2.3.3 and have activated the plugin. I entered the above in code version. I’m guessing I don’t understand how to use the pre tag precisely? Can you give the dummies version?

  4. Hi, Sharani. I’m not quite sure what you’re hoping the plugin will do. The use I had in mind is when you’re putting samples of code in a page. For an example, say you want to share some HTML on a page, as I did here not long ago. You would put it inside of <pre> tags so that all your indentation is preserved. My HTML looked like:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    	<head>
    		<title>IE Float Bug</title>
    	</head>
    	<body>
    
    		<p id="red">red</p>
    		<p id="blue">blue</p>
    	</body>
    </html>

    But the WordPress WYSIWYG wanted to turn that into:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
    <title>IE Float Bug</title>
    </head>
    <body>
    <p id="red">red</p>
    <p id="blue">blue</p>
    </body>
    </html>

    So, all that said, it sounds like you’re trying to use the <pre> tag to help you layout your page. If that’s the case, there are much better ways to that using CSS. Or, perhaps I’m misunderstanding what you’re saying. Let me know if that helps, and if I can be of service to you.

  5. Pingback: GeeKpc » Blog Archive » i migliori plugin di Wordpress (per blogger esperti)

  6. Rick says:

    Hi

    Sorry but it doesn’t work in 2.5

    I enter the pre tags in HTML, then go to visual – It shows everything OK in visual and protects code!

    Now I select html again – bam – code has been changed in html! Now back to visual -real mess. Back to html – more changes!….

  7. Pingback:   Come scrivere sample-code in un post by CriticalSection

  8. Pingback: 30 Essential and Advanced Wordpress Plugins | Speckyboy - Wordpress and Design

  9. Anadadly says:

    Very nice!!

  10. Pingback: 18 utili Plugin per Blogger Esperti

  11. colin moock says:

    please don’t consider this plugin dead. i’d love to continue using the visual editor, even for posts that contain code.

    if you do ever update this plugin, consider including support for other tags too, such as the one used by code colorer:

    [cc language="actionscript"]
    package {
    public class HelloWorld {
    public function HelloWorld () {
    }
    }
    }
    [/cc]

  12. Pingback: Liste von Wordpress-Plugins des Autopilot-Blogs | Digitale-Infoprodukte.de

  13. Pingback: I Migliori plugin per Wordpress | General Magazine

  14. Pingback: Cyberdigital webmaster & Tech Blog » Blog Archive » 30 Essential and Advanced Wordpress Plugins

  15. Pingback: 30 Essential and Advanced Wordpress Plugins | Cyberdigital Webmaster & Tech blog