diff --git a/Text/Trifecta.hs b/Text/Trifecta.hs
--- a/Text/Trifecta.hs
+++ b/Text/Trifecta.hs
@@ -2,10 +2,12 @@
   ( module Text.Trifecta.Diagnostic
   , module Text.Trifecta.Parser
   , module Text.Trifecta.Rope
+  , module Text.Trifecta.Highlight
   , module System.Console.Terminfo.PrettyPrint
   ) where
 
 import Text.Trifecta.Diagnostic
 import Text.Trifecta.Parser
 import Text.Trifecta.Rope
+import Text.Trifecta.Highlight
 import System.Console.Terminfo.PrettyPrint
diff --git a/Text/Trifecta/Diagnostic/Rendering/Prim.hs b/Text/Trifecta/Diagnostic/Rendering/Prim.hs
--- a/Text/Trifecta/Diagnostic/Rendering/Prim.hs
+++ b/Text/Trifecta/Diagnostic/Rendering/Prim.hs
@@ -24,15 +24,17 @@
 import Control.Comonad
 import Control.Monad.State
 import Data.Array
-import Data.ByteString hiding (groupBy, empty, any)
+import Data.ByteString as B hiding (groupBy, empty, any)
 import Data.Foldable
 import Data.Function (on)
+import Data.Int (Int64)
 import Data.Functor.Bind
 import Data.List (groupBy)
 import Data.Semigroup
 import Data.Semigroup.Foldable
 import Data.Semigroup.Traversable
 import Data.Traversable
+import Text.Trifecta.IntervalMap
 import Prelude as P
 import Prelude hiding (span)
 import System.Console.Terminfo.Color
@@ -41,12 +43,13 @@
 import Text.Trifecta.Rope.Bytes
 import Text.Trifecta.Rope.Delta
 import Text.Trifecta.Highlight.Class
+import Text.Trifecta.Highlight.Effects
 import qualified Data.ByteString.UTF8 as UTF8 
 
 outOfRangeEffects :: [ScopedEffect] -> [ScopedEffect]
 outOfRangeEffects xs = soft Bold : xs
 
-type Lines = Array (Int,Int) ([ScopedEffect], Char)
+type Lines = Array (Int,Int64) ([ScopedEffect], Char)
 
 (///) :: Ix i => Array i e -> [(i, e)] -> Array i e
 a /// xs = a // P.filter (inRange (bounds a) . fst) xs
@@ -58,7 +61,7 @@
   where old@((t,lo),(b,hi)) = bounds a
         new = ((min t y,lo),(max b y,hi))
 
-draw :: [ScopedEffect] -> Int -> Int -> String -> Lines -> Lines
+draw :: [ScopedEffect] -> Int -> Int64 -> String -> Lines -> Lines
 draw e y n xs a0 
   | Prelude.null xs = a0
   | otherwise = gt $ lt (a /// out) 
@@ -71,22 +74,42 @@
     gt | Prelude.any (\el -> snd (fst el) > hi) out = (// [((y,hi),(outOfRangeEffects e,'>'))])
        | otherwise = id
 
+-- | fill the interval from [n .. m) with a given effect
+recolor :: ([ScopedEffect] -> [ScopedEffect]) -> Maybe Int64 -> Maybe Int64 -> Lines -> Lines
+recolor f n0 m0 a0 
+  | m <= n = a0
+  | otherwise = a /// P.map rc [n .. m - 1]
+  where 
+    ((_,lo),(_,hi)) = bounds a
+    n = maybe lo id n0
+    m = maybe (hi + 1) id m0
+    a = grow 0 a0
+    rc i = (yi, (f e, c)) -- only if not isSpace?
+      where 
+        yi = (0, i)
+        (e,c) = a ! yi
+
 data Rendering = Rendering
-  { renderingDelta    :: !Delta                  -- focus, the render will keep this visible
-  , renderingLineLen  :: {-# UNPACK #-} !Int     -- actual line length
+  { renderingDelta    :: !Delta                 -- focus, the render will keep this visible
+  , renderingLineLen   :: {-# UNPACK #-} !Int64 -- actual line length
+  , renderingLineBytes :: {-# UNPACK #-} !Int64 -- line length in bytes
   , renderingLine     :: Lines -> Lines
   , renderingOverlays :: Delta -> Lines -> Lines
   }
 
 instance Highlightable Rendering where
-  addHighlights _h r = r -- TODO
+  addHighlights intervals (Rendering d ll lb l o) = Rendering d ll lb l' o where
+    d' = rewind d
+    l' = Prelude.foldr (.) l [ recolor (eff tok) (column lo <$ guard (near d lo)) (column hi <$ guard (near d hi)) 
+                             | (Interval lo hi, tok) <- intersections d' (d' <> Columns ll lb) intervals ]
+    eff t _ = highlightEffects t
 
 instance Show Rendering where
-  showsPrec d (Rendering p ll _ _) = showParen (d > 10) $ 
-    showString "Rendering " . showsPrec 11 p . showChar ' ' . showsPrec 11 ll . showString " ... ..."
+  showsPrec d (Rendering p ll lb _ _) = showParen (d > 10) $ 
+    showString "Rendering " . showsPrec 11 p . showChar ' ' . showsPrec 11 ll . showChar ' ' . showsPrec 11 lb . showString " ... ..."
 
 nullRendering :: Rendering -> Bool
-nullRendering (Rendering (Columns 0 0) 0 _ _) = True
+nullRendering (Rendering (Columns 0 0) 0 0 _ _) = True
 nullRendering _ = False
 
 emptyRendering :: Rendering
@@ -94,8 +117,8 @@
 
 instance Semigroup Rendering where
   -- an unprincipled hack
-  Rendering (Columns 0 0) 0 _ f <> Rendering del len doc g = Rendering del len doc $ \d l -> f d (g d l)
-  Rendering del len doc f <> Rendering _ _ _ g = Rendering del len doc $ \d l -> f d (g d l)
+  Rendering (Columns 0 0) 0 0 _ f <> Rendering del len lb doc g = Rendering del len lb doc $ \d l -> f d (g d l)
+  Rendering del len lb doc f <> Rendering _ _ _ _ g = Rendering del len lb doc $ \d l -> f d (g d l)
 
 instance Monoid Rendering where
   mappend = (<>) 
@@ -115,16 +138,17 @@
   render = id
 
 class Source t where
-  source :: t -> (Int, Lines -> Lines) {- return whether to append EOF, the number of bytes, and the the line -}
+  source :: t -> (Int64, Int64, Lines -> Lines) {- the number of (padded) columns, number of bytes, and the the line -}
 
 instance Source String where
   source s 
-    | Prelude.elem '\n' s = ( ls, draw [] 0 0 s') 
-    | otherwise           = ( ls + Prelude.length end, draw [soft (Foreground Blue), soft Bold] 0 ls end . draw [] 0 0 s') 
+    | Prelude.elem '\n' s = ( ls, bs, draw [] 0 0 s') 
+    | otherwise           = ( ls + fromIntegral (Prelude.length end), bs, draw [soft (Foreground Blue), soft Bold] 0 ls end . draw [] 0 0 s') 
     where
       end = "<EOF>" 
       s' = go 0 s
-      ls = Prelude.length s' 
+      bs = fromIntegral $ B.length $ UTF8.fromString $ Prelude.takeWhile (/='\n') s
+      ls = fromIntegral $ Prelude.length s' 
       go n ('\t':xs) = let t = 8 - mod n 8 in P.replicate t ' ' ++ go (n + t) xs
       go _ ('\n':_)  = []
       go n (x:xs)    = x : go (n + 1) xs
@@ -137,18 +161,18 @@
 -- | create a drawing surface
 rendering :: Source s => Delta -> s -> Rendering
 rendering del s = case source s of 
-  (len, doc) -> Rendering del len doc (\_ l -> l)
+  (len, lb, doc) -> Rendering del len lb doc (\_ l -> l)
 
 (.#) :: (Delta -> Lines -> Lines) -> Rendering -> Rendering
-f .# Rendering d ll s g = Rendering d ll s $ \e l -> f e $ g e l 
+f .# Rendering d ll lb s g = Rendering d ll lb s $ \e l -> f e $ g e l 
 
 instance Pretty Rendering where
   pretty r = prettyTerm r >>= const empty
 
 instance PrettyTerm Rendering where
-  prettyTerm (Rendering d ll l f) = nesting $ \k -> columns $ \n -> go (n - k) where
+  prettyTerm (Rendering d ll _ l f) = nesting $ \k -> columns $ \n -> go (fromIntegral (n - k)) where
     go cols = align (vsep (P.map ln [t..b])) where 
-      (lo, hi) = window (fromIntegral $ column d) ll (min (max (cols - 2) 30) 200)
+      (lo, hi) = window (column d) ll (min (max (cols - 2) 30) 200)
       a = f d $ l $ array ((0,lo),(-1,hi)) []
       ((t,_),(b,_)) = bounds a
       ln y = hcat 
@@ -156,7 +180,7 @@
            $ groupBy ((==) `on` fst) 
            [ a ! (y,i) | i <- [lo..hi] ] 
 
-window :: Int -> Int -> Int -> (Int, Int)
+window :: Int64 -> Int64 -> Int64 -> (Int64, Int64)
 window c l w 
   | c <= w2     = (0, min w l)
   | c + w2 >= l = if l > w then (l-w, l) else (0, w)
diff --git a/Text/Trifecta/Diagnostic/Rendering/Span.hs b/Text/Trifecta/Diagnostic/Rendering/Span.hs
--- a/Text/Trifecta/Diagnostic/Rendering/Span.hs
+++ b/Text/Trifecta/Diagnostic/Rendering/Span.hs
@@ -36,9 +36,9 @@
 
 drawSpan :: Delta -> Delta -> Delta -> Lines -> Lines
 drawSpan s e d a
-  | nl && nh  = go (column l) (P.replicate (fromIntegral (max (column h - column l) 0)) '~') a
-  | nl        = go (column l) (P.replicate (max (snd (snd (bounds a)) - fromIntegral (column l) + 1) 0) '~') a
-  |       nh  = go (-1)       (P.replicate (fromIntegral (max (column h + 1) 0)) '~') a
+  | nl && nh  = go (column l) (rep (max (column h - column l) 0) '~') a
+  | nl        = go (column l) (rep (max (snd (snd (bounds a)) - column l + 1) 0) '~') a
+  |       nh  = go (-1)       (rep (max (column h + 1) 0) '~') a
   | otherwise = a
   where
     go = draw spanEffects 1 . fromIntegral
@@ -46,6 +46,7 @@
     h = argmax bytes s e
     nl = near l d
     nh = near h d
+    rep = P.replicate . fromIntegral
 
 -- |
 -- > int main(int argc, char ** argv) { int; }
diff --git a/Text/Trifecta/Highlight.hs b/Text/Trifecta/Highlight.hs
new file mode 100644
--- /dev/null
+++ b/Text/Trifecta/Highlight.hs
@@ -0,0 +1,7 @@
+module Text.Trifecta.Highlight 
+  ( module Text.Trifecta.Highlight.Prim
+  , module Text.Trifecta.Highlight.Class
+  ) where
+
+import Text.Trifecta.Highlight.Prim
+import Text.Trifecta.Highlight.Class
diff --git a/Text/Trifecta/Rope.hs b/Text/Trifecta/Rope.hs
--- a/Text/Trifecta/Rope.hs
+++ b/Text/Trifecta/Rope.hs
@@ -6,8 +6,10 @@
   , Delta(..)
   , HasDelta(..)
   , HasBytes(..)
+  , HighlightedRope(..)
   ) where
 
 import Text.Trifecta.Rope.Prim
 import Text.Trifecta.Rope.Delta
 import Text.Trifecta.Rope.Bytes
+import Text.Trifecta.Rope.Highlighted
diff --git a/trifecta.cabal b/trifecta.cabal
--- a/trifecta.cabal
+++ b/trifecta.cabal
@@ -1,6 +1,6 @@
 name:          trifecta
 category:      Text, Parsing, Diagnostics, Pretty Printer, Logging
-version:       0.39
+version:       0.40
 license:       BSD3
 cabal-version: >= 1.6
 license-file:  LICENSE
@@ -54,6 +54,7 @@
     Text.Trifecta.Diagnostic.Rendering.Caret
     Text.Trifecta.Diagnostic.Rendering.Fixit
     Text.Trifecta.Diagnostic.Rendering.Span
+    Text.Trifecta.Highlight
     Text.Trifecta.Highlight.Class
     Text.Trifecta.Highlight.Prim
     Text.Trifecta.Highlight.Effects
