Using CodeMirror to add C# syntax highlighting to an editable HTML Textarea

I wanted to display some C# code in an html <textarea> control that was displayed in an ASP MVC 3 application using @Html.TextArea():

@using (Html.BeginForm())
{
    @Html.TextArea("sampleCode", (string)ViewBag.sampleCode, new { rows = "20", cols = "120" })
    <input type="submit" />
}

Note that I'm talking about an editable textarea, not just a code display - for code display, you can use something like SyntaxHighlighter or Prettify pretty easily.

While a big textarea is workable, I wanted to improve the experience a bit. There are several WYSIWYG textbox controls for general formatted text out there, but it was a little harder to find one that that fit well with code. I played around with SyntaxHighlighter a bit, since I use it for displaying code in my blog, but it didn't work smoothly with a textarea. A StackOverflow post pointed me to a great solution: CodeMirror. It didn't (yet) have c# syntax support, but I added that in pretty easily - I'll talk about that in a bit.

Demo

Here's an idea of how it works. This is an editable textbox with C# code syntax highlighting, which dynamically updates as you type.

Adding CodeMirror to a textbox

Adding CodeMirror is really easy:

  1. Grab the CodeMirror package
  2. Add the necessary CSS and JavaScript references in your page
  3. Call CodeMirror.fromTextArea() on your textarea element

Getting the CodeMirror package

Normally you'd grab that from http://codemirror.net, but my c# syntax changes were just merged in and aren't in the download package yet, so you can just grab the latest zip from github: https://github.com/marijnh/CodeMirror2/zipball/master.

Add the necessary CSS and JavaScript references in your page

The CSS and JavaScript dependencies vary based on you'll need, depending on which features you're using. Fortunately, the CodeMirror package includes a lot of great samples (also available on the website) which show clearly what you'll need to reference: just view-source. Assuming your CodeMirror scripts were in /Scripts/CodeMirror/, this would reference the basic scripts for C# highlighting:

<script src="/Scripts/CodeMirror/lib/codemirror.js"></script>
<script src="/Scripts/CodeMirror/mode/clike/clike.js" type="text/javascript"></script>
<link href="/Scripts/CodeMirror/lib/codemirror.css" rel="stylesheet" type="text/css" />
<link href="/Scripts/CodeMirror/theme/default.css" rel="stylesheet" type="text/css" />

Here's what those four references do:

  1. The first script reference brings in the main CodeMirror library
  2. The second script refernce uses one of the common supported modes - in this case I'm using the c-like syntax mode
  3. The next is the main codemirror CSS reference
  4. The last reference is for the default CodeMirror theme - there are 4 included color themes, and you can add your own with your own custom CSS file

Call CodeMirror.fromTextArea() on your textarea element

The CodeMirror call has a lot of sensible defaults, so this is pretty easy. The only real thing to point out is that the CodeMirror.fromTextArea() takes a textarea element, so you can either call document.getElementById or use jQuery and access the first array element, e.g. $('code')[0]. For a simple case, the code looks like this:

<script> 
  var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
    lineNumbers: true,
    matchBrackets: true,
    mode: "text/x-csharp"
  });
</script> 

Styling the CodeMirror box

CodeMirror creates a new element with the class "CodeMirror" so you need to apply your CSS styles to the CodeMirror class, not the ID or class of your original element.

Putting it all together

Here's the source for the above textbox

<script src="/Scripts/CodeMirror/lib/codemirror.js"></script>
<script src="/Scripts/CodeMirror/mode/clike/clike.js" type="text/javascript"></script>
<link href="/Scripts/CodeMirror/lib/codemirror.css" rel="stylesheet" type="text/css" />
<link href="/Scripts/CodeMirror/theme/default.css" rel="stylesheet" type="text/css" />

<style>
    .CodeMirror {
        border:1px solid #334;
        width:800px;
    }
</style>

<textarea id="code" name="code">using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcMusicStore.Models;

namespace MvcMusicStore.Controllers
{
    [Authorize(Roles = "Administrator")]
    public class StoreManagerController : Controller
    {
        private MusicStoreEntities db = new MusicStoreEntities();

        //
        // GET: /StoreManager/

        public ViewResult Index()
        {
            var albums = db.Albums.Include(a => a.Genre).Include(a => a.Artist);
            return View(albums.ToList());
        }

        //
        // GET: /StoreManager/Details/5

        public ViewResult Details(int id)
        {
            Album album = db.Albums.Find(id);
            return View(album);
        }

        // etc. etc.
    }
}
</textarea> 
 
<script> 
  var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
    lineNumbers: true,
    matchBrackets: true,
    mode: "text/x-csharp"
  });
</script> 
 

Adding new syntax

I mentioned earlier that when I first found CodeMirror, I noticed that it didn't have C# support. Fortunately, I noticed that it did have a C-Like syntax mode, and adding C# syntax support was really easy.

  1. I looked up the C# 4 keywords on MSDN here.
  2. I did some find-replace-fu to turn it into a space delimited list.
  3. I added a few lines to the end of clike.js, following the format of the previous C++ and Java samples.
CodeMirror.defineMIME("text/x-java", {
  name: "clike",
  atAnnotations: true,
  keywords: keywords("abstract assert boolean break byte case catch char class const continue default " + 
                     "do double else enum extends false final finally float for goto if implements import " +
                     "instanceof int interface long native new null package private protected public " +
                     "return short static strictfp super switch synchronized this throw throws transient " +
                     "true try void volatile while")
});
CodeMirror.defineMIME("text/x-csharp", {
  name: "clike",
  atAnnotations: true,
  atStrings: true,
  keywords: keywords("abstract as base bool break byte case catch char checked class const continue decimal" + 
                     " default delegate do double else enum event explicit extern false finally fixed float for" + 
                     " foreach goto if implicit in int interface internal is lock long namespace new null object" + 
                     " operator out override params private protected public readonly ref return sbyte sealed short" + 
                     " sizeof stackalloc static string struct switch this throw true try typeof uint ulong unchecked" + 
                     " unsafe ushort using virtual void volatile while add alias ascending descending dynamic from get" + 
                     " global group into join let orderby partial remove select set value var yield")
});

The key is to find a similar language which is already supported and modify it.

2 Comments

Comments have been disabled for this content.