In today’s Programming Praxis exercise, our goal is to write two functions to convert between text fragments indented with tabs and spaces. Let’s get started, shall we?
A required import:
Converting tabs to spaces is easy: just replace each tab with the desired amount of spaces. For the actual replacing we use a regular expression library.
detab :: Int -> String -> String detab w s = subRegex (mkRegex "\t") s (replicate w ' ')
Converting spaces to tabs is a bit more complicated, particularly if the text is indented using a combination of tabs and spaces. First we count the total indent length, and then we insert the required amount of tabs and spaces.
entab :: Int -> String -> String entab w = unlines . map f . lines where f s = replicate tabs '\t' ++ replicate spaces ' ' ++ line where (indent, line) = span (`elem` " \t") s (tabs, spaces) = divMod (sum $ map width indent) w width c = if c == '\t' then w else 1
For testing, we need to make sure we handle all the test cases: no indent, less than a tab of indent, one tab of indent, less than two tabs of indent, two tabs of indent and a line with mixed spaces and tabs.
main :: IO () main = do print $ entab 2 "a\n b\n c\n d\n e\n \t f\n" == "a\n b\n\tc\n\t d\n\t\te\n\t\tf\n" print $ detab 2 "a\n b\n\tc\n\t d\n\t\te\n \t f\n" == "a\n b\n c\n d\n e\n f\n"
Everything seems to be working correctly. Personally, I’m on the spaces side of this particular holy war, specifically in the four spaces camp. Two spaces is still acceptable, every other setting is wrong 🙂