packages feed

mmark-ext-0.2.1.3: Text/MMark/Extension/PunctuationPrettifier.hs

{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}

-- |
-- Module      :  Text.MMark.Extension.PunctuationPrettifier
-- Copyright   :  © 2018–present Mark Karpov
-- License     :  BSD 3 clause
--
-- Maintainer  :  Mark Karpov <markkarpov92@gmail.com>
-- Stability   :  experimental
-- Portability :  portable
--
-- Punctuation prettifier.
module Text.MMark.Extension.PunctuationPrettifier
  ( punctuationPrettifier,
  )
where

import Data.Char (isSpace)
import Data.Text (Text)
import qualified Data.Text as T
import Text.MMark.Extension (Extension, Inline (..))
import qualified Text.MMark.Extension as Ext

-- | Prettify punctuation (only affects plain text in inlines):
--
--     * Replace @...@ with ellipsis @…@
--     * Replace @---@ with em-dash @—@
--     * Replace @--@ with en-dash @–@
--     * Replace @\"@ with left double quote @“@ when previous character was
--       a space character, otherwise replace it with right double quote @”@
--     * Replace @'@ with left single quote @‘@ when previous character was
--       a space character, otherwise replace it with right single quote @’@
--       aka apostrophe
punctuationPrettifier :: Extension
punctuationPrettifier = Ext.inlineTrans $ \case
  Plain txt -> Plain (T.unfoldr gen (True, txt))
  other -> other

gen ::
  -- | Whether the previous character was a space and remaining input
  (Bool, Text) ->
  -- | Next generated char and the state
  Maybe (Char, (Bool, Text))
gen (s, i) =
  case T.uncons i of
    Nothing -> Nothing
    Just ('.', i') ->
      case T.splitAt 2 i' of
        ("..", i'') -> Just ('…', (False, i''))
        _ -> Just ('.', (False, i'))
    Just ('-', i') ->
      case T.splitAt 2 i' of
        ("--", i'') -> Just ('—', (False, i''))
        _ ->
          case T.splitAt 1 i' of
            ("-", i'') -> Just ('–', (False, i''))
            _ -> Just ('-', (False, i'))
    Just ('\"', i') ->
      if s -- whether previous character was a space character
        then Just ('“', (False, i'))
        else Just ('”', (False, i'))
    Just ('\'', i') ->
      if s
        then Just ('‘', (False, i'))
        else Just ('’', (False, i'))
    Just (ch, i') ->
      Just (ch, (isSpace ch, i'))