# Apply smiley replacement filter to Pandoc but not in code segments

I wrote a filter for pandas that converts :-) to ☺ but not in inline code or code blocks. Pandoc makes it so the applyExceptOnCodeBlock smileyfy function trickles through the whole tree. But since I operate on Blocks and Inlines I need to handle every data constructor for Block that takes Inlines.

import Text.Pandoc.JSON
import Text.Pandoc.Walk

import Data.List (intercalate)
import Data.List.Split (splitOn)

main :: IO ()
main = toJSONFilter $applyExceptOnCodeBlock smileyfy smileyfy :: String -> String smileyfy = intercalate "☺" . splitOn ":-)" applyExceptOnCodeInline :: (String -> String) -> Inline -> Inline applyExceptOnCodeInline f (Str s) = Str (f s) applyExceptOnCodeInline f (Code attr s) = Code attr s -- for code nothing shall happen applyExceptOnCodeInline _ x = x applyExceptOnCodeBlock :: (String -> String) -> Block -> Block applyExceptOnCodeBlock f (Plain is) = Plain$ applyExceptOnCodeInline f <$> is applyExceptOnCodeBlock f (Para is) = Para$ applyExceptOnCodeInline f <$> is applyExceptOnCodeBlock f (LineBlock iss) = LineBlock$ (fmap . fmap) (applyExceptOnCodeInline f) iss
applyExceptOnCodeBlock f (RawBlock format s)  = RawBlock format $f s applyExceptOnCodeBlock f (DefinitionList [(is, bss)]) = DefinitionList [(applyExceptOnCodeInline f <$> is
,fmap (fmap (applyExceptOnCodeBlock f)) bss)]
applyExceptOnCodeBlock f (Header i attr is) = Header i attr $applyExceptOnCodeInline f <$> is
applyExceptOnCodeBlock f (Table is as ds tcs tcss) = Table (applyExceptOnCodeInline f <$> is) as ds tcs tcss applyExceptOnCodeBlock _ x = x --this covers CodeBlock as well  And thus it looks very convoluted. Is there a clearer way to express this? • For possible future visitors, I'd like to add that a small Lua filter is easier to apply, as you don't need a fully Haskell stack installed. – Zeta Sep 7, 2019 at 5:43 ## 1 Answer There's a much shorter way to express the same functionality, but I'm not sure you'll immediately find it to be clearer. import Data.List (intercalate) import Data.List.Split (splitOn) import Text.Pandoc.JSON main :: IO () main = toJSONFilter smile smile :: Inline -> Inline smile (Str s) = Str . intercalate "☺" . splitOn ":-)"$ s
smile x = x


I think the insight you missed is that the Block constructor CodeBlock contains no Inlines, so you can trust the Walkable typeclass machinery to ignore them if your smiley making code only works on Inlines. That eliminates the entire applyExceptOnCodeBlock function.

Similarly it isn't necessary to include a case for Inline's Code constructor, constructors like Code and Math contain their own raw strings and are safely ignored. Pandoc's datatypes are already structured to divide prose text from markup making it easy to define transformations by only matching on the things you do want to modify.

• Thanks! I originally wanted the filter to ignore BlockQuote but when I switched to Code I didn't realize that I no longer had a problem. Sep 7, 2019 at 11:58