diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -25,6 +25,8 @@
 - Diagnostics can be exported to JSON, if you don't quite like the rendering as it is, or if you need to transmit them to e.g. a website
 - Plug and play (mega)parsec integration and it magically works with your parsers!
 - Support for optional custom error codes, if you want to go the Rust way
+- Variable width Unicode characters are handled in a crossplatform manner
+- TAB characters have custom sizes specified when printing a diagnostic, so that *you* decide the width of a TAB, not your terminal emulator!
 
 ## Usage
 
@@ -97,17 +99,14 @@
 let diagnostic' = addReport diagnostic beautifulExample
 
 -- Print with unicode characters and colors
-printDiagnostic stdout True True diagnostic'
+printDiagnostic stdout True True 4 diagnostic'
 ```
 
 More examples are given in the [`test/rendering`](./test/rendering) folder.
 
 ## TODO list
 
-- Handle variable-width characters such as tabs or some unicode characters.
-
-  For tabs, we may just resort to having the user fix a given number of
-  spaces in the `printDiagnostic` function.
+<< empty, for now >>
 
 ## License
 
diff --git a/diagnose.cabal b/diagnose.cabal
--- a/diagnose.cabal
+++ b/diagnose.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           diagnose
-version:        1.7.2
+version:        1.8.0
 synopsis:       Beautiful error reporting done easily
 description:    This package provides a simple way of getting beautiful compiler/interpreter errors
                 using a very simple interface for the programmer.
@@ -65,17 +65,18 @@
   ghc-options: -Wall -Wextra
   build-depends:
       base >=4.7 && <5
-    , bytestring ==0.10.*
-    , data-default ==0.7.*
-    , hashable ==1.3.*
-    , prettyprinter ==1.7.*
-    , prettyprinter-ansi-terminal ==1.1.*
-    , text ==1.2.*
+    , data-default >=0.7 && <1
+    , hashable >=1.3 && <2
+    , prettyprinter >=1.7.0 && <2
+    , prettyprinter-ansi-terminal >=1.1.0 && <2
+    , text >=1.2 && <2
     , unordered-containers ==0.2.*
+    , wcwidth >=0.0.1 && <1
   if flag(json)
     cpp-options: -DUSE_AESON
     build-depends:
-        aeson ==1.5.*
+        aeson >=1.5 && <2
+      , bytestring >=0.9 && <1
   if flag(megaparsec-compat)
     build-depends:
         containers ==0.6.*
@@ -105,18 +106,19 @@
   ghc-options: -Wall -Wextra -threaded -rtsopts -with-rtsopts=-N
   build-depends:
       base >=4.7 && <5
-    , bytestring ==0.10.*
-    , data-default ==0.7.*
+    , data-default >=0.7 && <1
     , diagnose
-    , hashable ==1.3.*
-    , prettyprinter ==1.7.*
-    , prettyprinter-ansi-terminal ==1.1.*
-    , text ==1.2.*
+    , hashable >=1.3 && <2
+    , prettyprinter >=1.7.0 && <2
+    , prettyprinter-ansi-terminal >=1.1.0 && <2
+    , text >=1.2 && <2
     , unordered-containers ==0.2.*
+    , wcwidth >=0.0.1 && <1
   if flag(json)
     cpp-options: -DUSE_AESON
     build-depends:
-        aeson ==1.5.*
+        aeson >=1.5 && <2
+      , bytestring >=0.9 && <1
   if flag(megaparsec-compat)
     build-depends:
         containers ==0.6.*
@@ -143,18 +145,19 @@
   ghc-options: -Wall -Wextra -threaded -rtsopts -with-rtsopts=-N
   build-depends:
       base >=4.7 && <5
-    , bytestring ==0.10.*
-    , data-default ==0.7.*
+    , data-default >=0.7 && <1
     , diagnose
-    , hashable ==1.3.*
-    , prettyprinter ==1.7.*
-    , prettyprinter-ansi-terminal ==1.1.*
-    , text ==1.2.*
+    , hashable >=1.3 && <2
+    , prettyprinter >=1.7.0 && <2
+    , prettyprinter-ansi-terminal >=1.1.0 && <2
+    , text >=1.2 && <2
     , unordered-containers ==0.2.*
+    , wcwidth >=0.0.1 && <1
   if flag(json)
     cpp-options: -DUSE_AESON
     build-depends:
-        aeson ==1.5.*
+        aeson >=1.5 && <2
+      , bytestring >=0.9 && <1
   if flag(megaparsec-compat)
     build-depends:
         containers ==0.6.*
@@ -180,18 +183,19 @@
   ghc-options: -Wall -Wextra -threaded -rtsopts -with-rtsopts=-N
   build-depends:
       base >=4.7 && <5
-    , bytestring ==0.10.*
-    , data-default ==0.7.*
+    , data-default >=0.7 && <1
     , diagnose
-    , hashable ==1.3.*
-    , prettyprinter ==1.7.*
-    , prettyprinter-ansi-terminal ==1.1.*
-    , text ==1.2.*
+    , hashable >=1.3 && <2
+    , prettyprinter >=1.7.0 && <2
+    , prettyprinter-ansi-terminal >=1.1.0 && <2
+    , text >=1.2 && <2
     , unordered-containers ==0.2.*
+    , wcwidth >=0.0.1 && <1
   if flag(json)
     cpp-options: -DUSE_AESON
     build-depends:
-        aeson ==1.5.*
+        aeson >=1.5 && <2
+      , bytestring >=0.9 && <1
   if flag(megaparsec-compat)
     build-depends:
         containers ==0.6.*
diff --git a/src/Error/Diagnose.hs b/src/Error/Diagnose.hs
--- a/src/Error/Diagnose.hs
+++ b/src/Error/Diagnose.hs
@@ -150,6 +150,8 @@
 --
 --   - A 'Bool' set to 'False' if you don't want colors in the end result.
 --
+--   - A 'Int' describing the number of spaces with which to output a TAB character.
+--
 --   - And finally the 'Diagnostic' to output.
 
 -- $diagnostic_json
@@ -218,7 +220,7 @@
 --   >            --   Creates a new diagnostic with no default hints from the bundle returned by megaparsec
 --   >         diag' = addFile diag filename content
 --   >            --   Add the file used when parsing with the same filename given to 'MP.runParser'
---   >     in printDiagnostic stderr True True diag'
+--   >     in printDiagnostic stderr True True 4 diag'
 --   >   Right res   -> print res
 --
 --   This example will return the following error message (assuming default instances for @'Error.Diagnose.Compat.Megaparsec.HasHints' 'Data.Void.Void' msg@):
@@ -259,7 +261,7 @@
 --   >            --   Creates a new diagnostic with no default hints from the bundle returned by megaparsec
 --   >         diag' = addFile diag filename content
 --   >            --   Add the file used when parsing with the same filename given to 'MP.runParser'
---   >     in printDiagnostic stderr True True diag'
+--   >     in printDiagnostic stderr True True 4 diag'
 --   >   Right res  -> print res
 --
 --   This will output the following errr on @stderr@:
diff --git a/src/Error/Diagnose/Diagnostic/Internal.hs b/src/Error/Diagnose/Diagnostic/Internal.hs
--- a/src/Error/Diagnose/Diagnostic/Internal.hs
+++ b/src/Error/Diagnose/Diagnostic/Internal.hs
@@ -69,11 +69,13 @@
   Pretty msg =>
   -- | Should we use unicode when printing paths?
   Bool ->
-  -- | The diagnostic to print
+  -- | The number of spaces each TAB character will span.
+  Int ->
+  -- | The diagnostic to print.
   Diagnostic msg ->
   Doc AnsiStyle
-prettyDiagnostic withUnicode (Diagnostic reports file) =
-  fold . intersperse hardline $ prettyReport file withUnicode <$> reports
+prettyDiagnostic withUnicode tabSize (Diagnostic reports file) =
+  fold . intersperse hardline $ prettyReport file withUnicode tabSize <$> reports
 {-# INLINE prettyDiagnostic #-}
 
 -- | Prints a 'Diagnostic' onto a specific 'Handle'.
@@ -85,11 +87,13 @@
   Bool ->
   -- | 'False' to disable colors.
   Bool ->
+  -- | The number of spaces each TAB character will span.
+  Int ->
   -- | The diagnostic to output.
   Diagnostic msg ->
   m ()
-printDiagnostic handle withUnicode withColors diag =
-  liftIO $ hPutDoc handle (unlessId withColors unAnnotate $ prettyDiagnostic withUnicode diag)
+printDiagnostic handle withUnicode withColors tabSize diag =
+  liftIO $ hPutDoc handle (unlessId withColors unAnnotate $ prettyDiagnostic withUnicode tabSize diag)
   where
     unlessId cond app = if cond then id else app
     {-# INLINE unlessId #-}
diff --git a/src/Error/Diagnose/Report/Internal.hs b/src/Error/Diagnose/Report/Internal.hs
--- a/src/Error/Diagnose/Report/Internal.hs
+++ b/src/Error/Diagnose/Report/Internal.hs
@@ -4,9 +4,8 @@
 {-# LANGUAGE MultiWayIf #-}
 {-# LANGUAGE RecordWildCards #-}
 {-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE TypeApplications #-}
-
 {-# OPTIONS -Wno-name-shadowing #-}
+{-# LANGUAGE TypeApplications #-}
 
 -- |
 -- Module      : Error.Diagnose.Report.Internal
@@ -27,15 +26,16 @@
 #endif
 import Control.Applicative ((<|>))
 import Data.Bifunctor (bimap, first, second)
+import Data.Char.WCWidth (wcwidth)
 import Data.Default (def)
 import Data.Foldable (fold)
 import Data.Function (on)
 import Data.Functor ((<&>))
 import Data.HashMap.Lazy (HashMap)
 import qualified Data.HashMap.Lazy as HashMap
+import qualified Data.HashMap.Lazy as IntMap
 import qualified Data.List as List
 import qualified Data.List.Safe as List
-import Data.Ord (Down (..))
 import Error.Diagnose.Position
 import Prettyprinter (Doc, Pretty (..), align, annotate, colon, hardline, lbracket, rbracket, space, width, (<+>))
 import Prettyprinter.Internal (Doc (..))
@@ -136,10 +136,12 @@
   HashMap FilePath [String] ->
   -- | Should we print paths in unicode?
   Bool ->
+  -- | The number of spaces each TAB character will span
+  Int ->
   -- | The whole report to output
   Report msg ->
   Doc AnsiStyle
-prettyReport fileContent withUnicode (Report isError code message markers hints) =
+prettyReport fileContent withUnicode tabSize (Report isError code message markers hints) =
   let sortedMarkers = List.sortOn (fst . begin . fst) markers
       -- sort the markers so that the first lines of the reports are the first lines of the file
 
@@ -150,7 +152,8 @@
       -- if there are no markers, then default to 3, else get the maximum between 3 and the length of the last marker
 
       header =
-        annotate (bold <> color if isError then Red else Yellow) $
+        annotate
+          (bold <> color if isError then Red else Yellow)
           ( lbracket
               <> ( if isError
                      then "error"
@@ -163,7 +166,7 @@
    in {-
               A report is of the form:
               (1)    [error|warning]: <message>
-              (2)           +-> <file>
+              (2)           +--> <file>
               (3)           :
               (4)    <line> | <line of code>
                             : <marker lines>
@@ -174,7 +177,7 @@
       -}
 
       {- (1) -} header <> colon <+> align (pretty message)
-        <> {- (2), (3), (4) -} fold (uncurry (prettySubReport fileContent withUnicode isError maxLineNumberLength) <$> groupedMarkers)
+        <> {- (2), (3), (4) -} fold (uncurry (prettySubReport fileContent withUnicode isError tabSize maxLineNumberLength) <$> groupedMarkers)
         <> {- (5) -} ( if
                            | null hints && null markers -> mempty
                            | null hints -> mempty
@@ -280,6 +283,8 @@
   Bool ->
   -- | Is the current report an error report?
   Bool ->
+  -- | The number of spaces each TAB character will span
+  Int ->
   -- | The size of the biggest line number
   Int ->
   -- | Is this sub-report the first one in the list?
@@ -287,7 +292,7 @@
   -- | The list of line-ordered markers appearing in a single file
   [(Position, Marker msg)] ->
   Doc AnsiStyle
-prettySubReport fileContent withUnicode isError maxLineNumberLength isFirst markers =
+prettySubReport fileContent withUnicode isError tabSize maxLineNumberLength isFirst markers =
   let (markersPerLine, multilineMarkers) = splitMarkersPerLine markers
       -- split the list on whether markers are multiline or not
 
@@ -312,7 +317,7 @@
    in {- (2) -} hardline <> fileMarker
         <> hardline
           <+> {- (3) -} pipePrefix maxLineNumberLength withUnicode
-        <> {- (4) -} prettyAllLines fileContent withUnicode isError maxLineNumberLength sortedMarkersPerLine multilineMarkers allLineNumbers
+        <> {- (4) -} prettyAllLines fileContent withUnicode isError tabSize maxLineNumberLength sortedMarkersPerLine multilineMarkers allLineNumbers
 
 isThisMarker :: Marker msg -> Bool
 isThisMarker (This _) = True
@@ -333,13 +338,15 @@
   HashMap FilePath [String] ->
   Bool ->
   Bool ->
+  -- | The number of spaces each TAB character will span
   Int ->
+  Int ->
   [(Int, [(Position, Marker msg)])] ->
   [(Position, Marker msg)] ->
   [Int] ->
   Doc AnsiStyle
-prettyAllLines _ _ _ _ _ [] [] = mempty
-prettyAllLines _ withUnicode isError leftLen _ multiline [] =
+prettyAllLines _ _ _ _ _ _ [] [] = mempty
+prettyAllLines _ withUnicode isError _ leftLen _ multiline [] =
   let colorOfLastMultilineMarker = maybe mempty (markerColor isError . snd) (List.safeLast multiline)
       -- take the color of the last multiline marker in case we need to add additional bars
 
@@ -358,7 +365,7 @@
       showMultilineMarkerMessages [m] = [showMultilineMarkerMessage m True]
       showMultilineMarkerMessages (m : ms) = showMultilineMarkerMessage m False : showMultilineMarkerMessages ms
    in prefixWithBar colorOfLastMultilineMarker <> prefix <> fold (List.intersperse prefix $ showMultilineMarkerMessages $ reverse multiline)
-prettyAllLines files withUnicode isError leftLen inline multiline (line : ls) =
+prettyAllLines files withUnicode isError tabSize leftLen inline multiline (line : ls) =
   {-
       A line of code is composed of:
       (1)     <line> | <source code>
@@ -399,47 +406,50 @@
                 <> space
 
       allMarkersInLine = {- List.sortOn fst $ -} allInlineMarkersInLine <> allMultilineMarkersInLine
+
+      (widths, renderedCode) = getLine_ files (allMarkersInLine <> allMultilineMarkersSpanningLine) line tabSize isError
    in hardline
         <> {- (1) -} linePrefix leftLen line withUnicode <+> additionalPrefix
-        <> getLine_ files (allMarkersInLine <> allMultilineMarkersSpanningLine) line isError
-        <> {- (2) -} showAllMarkersInLine (not $ null multiline) inSpanOfMultiline colorOfFirstMultilineMarker withUnicode isError leftLen allInlineMarkersInLine
-        <> {- (3) -} prettyAllLines files withUnicode isError leftLen inline multiline ls
+        <> renderedCode
+        <> {- (2) -} showAllMarkersInLine (not $ null multiline) inSpanOfMultiline colorOfFirstMultilineMarker withUnicode isError leftLen widths allInlineMarkersInLine
+        <> {- (3) -} prettyAllLines files withUnicode isError tabSize leftLen inline multiline ls
 
 -- |
-getLine_ :: HashMap FilePath [String] -> [(Position, Marker msg)] -> Int -> Bool -> Doc AnsiStyle
-getLine_ files markers line isError = case List.safeIndex (line - 1) =<< (HashMap.!?) files . file . fst =<< List.safeHead markers of
-  Nothing -> annotate (bold <> colorDull Magenta) "<no line>"
+getLine_ :: HashMap FilePath [String] -> [(Position, Marker msg)] -> Int -> Int -> Bool -> (IntMap.HashMap Int Int, Doc AnsiStyle)
+getLine_ files markers line tabSize isError = case List.safeIndex (line - 1) =<< (HashMap.!?) files . file . fst =<< List.safeHead markers of
+  Nothing -> (mempty, annotate (bold <> colorDull Magenta) "<no line>")
   Just code ->
-    fold $
-      indexed code <&> \(n, c) ->
-        let colorizingMarkers = flip
-              filter
-              markers
-              \(Position (bl, bc) (el, ec) _, _) ->
-                if bl == el
-                  then n >= bc && n < ec
-                  else (bl == line && n >= bc) || (el == line && n < ec)
-         in maybe id ((\m -> annotate (bold <> markerColor isError m)) . snd) (List.safeHead colorizingMarkers) (pretty c)
+    let (tabs, code') = indexedWithTabsReplaced code
+     in ( tabs,
+          fold $
+            code' <&> \(n, c) ->
+              let colorizingMarkers = flip
+                    filter
+                    markers
+                    \(Position (bl, bc) (el, ec) _, _) ->
+                      if bl == el
+                        then n >= bc && n < ec
+                        else (bl == line && n >= bc) || (el == line && n < ec)
+               in maybe id ((\m -> annotate (bold <> markerColor isError m)) . snd) (List.safeHead colorizingMarkers) (pretty c)
+        )
   where
-    indexed :: [a] -> [(Int, a)]
-    indexed = goIndexed 1
+    indexedWithTabsReplaced :: String -> (IntMap.HashMap Int Int, [(Int, Char)])
+    indexedWithTabsReplaced = goIndexed 1
 
-    goIndexed :: Int -> [a] -> [(Int, a)]
-    goIndexed _ [] = []
-    goIndexed n (x : xs) = (n, x) : goIndexed (n + 1) xs
+    goIndexed :: Int -> String -> (IntMap.HashMap Int Int, [(Int, Char)])
+    goIndexed _ [] = (mempty, [])
+    goIndexed n ('\t' : xs) = bimap (IntMap.insert n tabSize) (replicate tabSize (n, ' ') <>) (goIndexed (n + 1) xs)
+    goIndexed n (x : xs) = bimap (IntMap.insert n (wcwidth x)) ((n, x) :) (goIndexed (n + 1) xs)
 
 -- |
-showAllMarkersInLine :: Pretty msg => Bool -> Bool -> (Doc AnsiStyle -> Doc AnsiStyle) -> Bool -> Bool -> Int -> [(Position, Marker msg)] -> Doc AnsiStyle
-showAllMarkersInLine _ _ _ _ _ _ [] = mempty
-showAllMarkersInLine hasMultilines inSpanOfMultiline colorMultilinePrefix withUnicode isError leftLen ms =
+showAllMarkersInLine :: Pretty msg => Bool -> Bool -> (Doc AnsiStyle -> Doc AnsiStyle) -> Bool -> Bool -> Int -> IntMap.HashMap Int Int -> [(Position, Marker msg)] -> Doc AnsiStyle
+showAllMarkersInLine _ _ _ _ _ _ _ [] = mempty
+showAllMarkersInLine hasMultilines inSpanOfMultiline colorMultilinePrefix withUnicode isError leftLen widths ms =
   let maxMarkerColumn = snd $ end $ fst $ List.last $ List.sortOn (snd . end . fst) ms
-      specialPrefix =
-        if inSpanOfMultiline
-          then colorMultilinePrefix (if withUnicode then "│ " else "| ") <> space
-          else
-            if hasMultilines
-              then colorMultilinePrefix "  " <> space
-              else mempty
+      specialPrefix
+        | inSpanOfMultiline = colorMultilinePrefix (if withUnicode then "│ " else "| ") <> space
+        | hasMultilines = colorMultilinePrefix "  " <> space
+        | otherwise = mempty
    in -- get the maximum end column, so that we know when to stop looking for other markers on the same line
       hardline <+> dotPrefix leftLen withUnicode <+> (if List.null ms then mempty else specialPrefix <> showMarkers 1 maxMarkerColumn <> showMessages specialPrefix ms maxMarkerColumn)
   where
@@ -449,12 +459,15 @@
         let allMarkers = flip filter ms \(Position (_, bc) (_, ec) _, _) -> n >= bc && n < ec
          in -- only consider markers which span onto the current column
             case allMarkers of
-              [] -> space <> showMarkers (n + 1) lineLen
+              [] -> fold (replicate (IntMap.lookupDefault 0 n widths) space) <> showMarkers (n + 1) lineLen
               (Position {..}, marker) : _ ->
-                if snd begin == n
-                  then annotate (markerColor isError marker) (if withUnicode then "┬" else "^") <> showMarkers (n + 1) lineLen
-                  else annotate (markerColor isError marker) (if withUnicode then "─" else "-") <> showMarkers (n + 1) lineLen
-    -- if the marker just started on this column, output a caret, else output a dash
+                annotate
+                  (markerColor isError marker)
+                  ( if snd begin == n
+                      then (if withUnicode then "┬" else "^") <> fold (replicate (IntMap.lookupDefault 0 n widths - 1) if withUnicode then "─" else "-")
+                      else fold (replicate (IntMap.lookupDefault 0 n widths) if withUnicode then "─" else "-")
+                  )
+                  <> showMarkers (n + 1) lineLen
 
     showMessages specialPrefix ms lineLen = case List.safeUncons ms of
       Nothing -> mempty -- no more messages to show
@@ -467,15 +480,16 @@
             allColumns _ [] = (1, [])
             allColumns n ms@((Position (_, bc) _ _, col) : ms')
               | n == bc = bimap (+ 1) (col :) (allColumns (n + 1) ms')
-              | n < bc = bimap (+ 1) (space :) (allColumns (n + 1) ms)
-              | otherwise = bimap (+ 1) (space :) (allColumns (n + 1) ms')
+              | n < bc = bimap (+ 1) (replicate (IntMap.lookupDefault 0 n widths) space <>) (allColumns (n + 1) ms)
+              | otherwise = bimap (+ 1) (replicate (IntMap.lookupDefault 0 n widths) space <>) (allColumns (n + 1) ms')
             -- transform the list of remaining markers into a single document line
 
             hasSuccessor = length filteredPipes /= length pipes
 
             lineStart pipes =
               let (n, docs) = allColumns 1 $ List.sortOn (snd . begin . fst) pipes
-               in dotPrefix leftLen withUnicode <+> specialPrefix <> fold docs <> pretty (replicate (bc - n) ' ')
+                  numberOfSpaces = sum [IntMap.lookupDefault 0 x widths | x <- [n .. bc - 1]] -- bc - n
+               in dotPrefix leftLen withUnicode <+> specialPrefix <> fold docs <> pretty (replicate numberOfSpaces ' ')
             -- the start of the line contains the "dot"-prefix as well as all the pipes for all the still not rendered marker messages
 
             prefix =
@@ -485,11 +499,11 @@
                   pipesBeforeRendered = pipesBefore <&> second \marker -> annotate (markerColor isError marker) (if withUnicode then "│" else "|")
                   -- pre-render pipes which are before because they will be shown
 
-                  lastBeginPosition = snd . begin . fst <$> List.safeLast (List.sortOn (Down . snd . begin . fst) pipesAfter)
+                  lastBeginPosition = snd . begin . fst <$> List.safeHead (List.sortOn (snd . begin . fst) pipesAfter)
 
                   lineLen = case lastBeginPosition of
                     Nothing -> 0
-                    Just col -> col - bc
+                    Just col -> sum [IntMap.lookupDefault 0 x widths | x <- [bc .. col - 1]]
 
                   currentPipe =
                     if
diff --git a/test/megaparsec/Spec.hs b/test/megaparsec/Spec.hs
--- a/test/megaparsec/Spec.hs
+++ b/test/megaparsec/Spec.hs
@@ -30,8 +30,8 @@
       res2 = first (errorDiagnosticFromBundle Nothing "Parse error on input" Nothing) $ MP.runParser @Void (MP.some MP.decimal <* MP.eof) filename content2
 
   case res1 of
-    Left diag -> printDiagnostic stdout True True (addFile diag filename (Text.unpack content1) :: Diagnostic String)
+    Left diag -> printDiagnostic stdout True True 4 (addFile diag filename (Text.unpack content1) :: Diagnostic String)
     Right res -> print res
   case res2 of
-    Left diag -> printDiagnostic stdout True True (addFile diag filename (Text.unpack content2) :: Diagnostic String)
+    Left diag -> printDiagnostic stdout True True 4 (addFile diag filename (Text.unpack content2) :: Diagnostic String)
     Right res -> print res
diff --git a/test/parsec/Repro2.hs b/test/parsec/Repro2.hs
--- a/test/parsec/Repro2.hs
+++ b/test/parsec/Repro2.hs
@@ -29,8 +29,8 @@
 
 main :: IO ()
 main = do
-  either (printDiagnostic stderr True True) print $ diagParse parser1 "issues/2.txt" "\\1"
-  either (printDiagnostic stderr True True) print $ diagParse parser2 "issues/2.txt" "\\1"
+  either (printDiagnostic stderr True True 4) print $ diagParse parser1 "issues/2.txt" "\\1"
+  either (printDiagnostic stderr True True 4) print $ diagParse parser2 "issues/2.txt" "\\1"
 
 -- smaller example
 op' :: String -> Parser String
diff --git a/test/parsec/Spec.hs b/test/parsec/Spec.hs
--- a/test/parsec/Spec.hs
+++ b/test/parsec/Spec.hs
@@ -30,10 +30,10 @@
       res2 = first (errorDiagnosticFromParseError Nothing "Parse error on input" Nothing) $ P.parse (P.many1 P.digit <* P.eof) filename content2
 
   case res1 of
-    Left diag -> printDiagnostic stdout True True (addFile diag filename (Text.unpack content1) :: Diagnostic String)
+    Left diag -> printDiagnostic stdout True True 4 (addFile diag filename (Text.unpack content1) :: Diagnostic String)
     Right res -> print res
   case res2 of
-    Left diag -> printDiagnostic stdout True True (addFile diag filename (Text.unpack content2) :: Diagnostic String)
+    Left diag -> printDiagnostic stdout True True 4 (addFile diag filename (Text.unpack content2) :: Diagnostic String)
     Right res -> print res
 
   -- all issue reproduction
diff --git a/test/rendering/Spec.hs b/test/rendering/Spec.hs
--- a/test/rendering/Spec.hs
+++ b/test/rendering/Spec.hs
@@ -31,7 +31,7 @@
             ("somefile.zc", "let id<a>(x : a) : a := x\n  + 1"),
             ("err.nst", "\n\n\n\n    = jmp g\n\n    g: forall(s: Ts, e: Tc).{ %r0: *s64 | s -> e }"),
             ("unsized.nst", "main: forall(a: Ta, s: Ts, e: Tc).{ %r5: forall().{| s -> e } | s -> %r5 }\n    = salloc a\n    ; sfree\n"),
-            ("unicode.txt", "±⅀\t★♲♥🎉⑳⓴ჳᏁℳ爪")
+            ("unicode.txt", "±⅀\t★♲♥🎉汉⑳⓴ჳᏁℳ爪")
           ]
 
   let reports =
@@ -70,9 +70,9 @@
   let diag = HashMap.foldlWithKey' addFile (foldr (flip addReport) def reports) files
 
   hPutStrLn stdout "\n\nWith unicode: ─────────────────────────\n"
-  printDiagnostic stdout True True diag
+  printDiagnostic stdout True True 4 diag
   hPutStrLn stdout "\n\nWithout unicode: ----------------------\n"
-  printDiagnostic stdout False True diag
+  printDiagnostic stdout False True 4 diag
 #ifdef USE_AESON
   hPutStrLn stdout "\n\nAs JSON: ------------------------------\n"
   BS.hPutStr stdout (diagnosticToJson diag)
@@ -354,5 +354,7 @@
   err
     (Just "❎")
     "ⓈⓉⓇⒶⓃⒼⒺ ⓊⓃⒾⒸⓄⒹⒺ"
-    [(Position (1, 1) (1, 7) "unicode.txt", This "should work fine 🎉")]
+    [ (Position (1, 1) (1, 7) "unicode.txt", This "should work fine 🎉"),
+      (Position (1, 7) (1, 9) "unicode.txt", Where "After TAB")
+    ]
     []
