@@ -9,8 +9,10 @@ module Data.Text.FixWhitespace
99 , transform
1010 , transformWithLog
1111 , TabSize
12+ , ConsecutiveEmptyLines
1213 , Verbose
1314 , defaultTabSize
15+ , defaultConsecutiveEmptyLines
1416 )
1517 where
1618
@@ -29,12 +31,17 @@ import Data.List.Extra.Drop ( dropWhileEnd1, dropWhile1 )
2931
3032type Verbose = Bool
3133type TabSize = Int
34+ type ConsecutiveEmptyLines = Int
3235
3336-- | Default tab size.
3437--
3538defaultTabSize :: TabSize
3639defaultTabSize = 8
3740
41+ -- | Maximum consecutive empty lines
42+ defaultConsecutiveEmptyLines :: ConsecutiveEmptyLines
43+ defaultConsecutiveEmptyLines = 0
44+
3845-- | Result of checking a file against the whitespace policy.
3946--
4047data CheckResult
@@ -54,30 +61,44 @@ data LineError = LineError Int Text
5461-- | Check a file against the whitespace policy,
5562-- returning a fix if violations occurred.
5663--
57- checkFile :: TabSize -> Verbose -> FilePath -> IO CheckResult
58- checkFile tabSize verbose f =
64+ checkFile :: TabSize -> ConsecutiveEmptyLines -> Verbose -> FilePath -> IO CheckResult
65+ checkFile tabSize consecutiveLines verbose f =
5966 handle (\ (e :: IOException ) -> return $ CheckIOError e) $
6067 withFile f ReadMode $ \ h -> do
6168 hSetEncoding h utf8
6269 s <- Text. hGetContents h
6370 let (s', lvs)
64- | verbose = transformWithLog tabSize s
65- | otherwise = (transform tabSize s, [] )
71+ | verbose = transformWithLog tabSize consecutiveLines s
72+ | otherwise = (transform tabSize consecutiveLines s, [] )
6673 return $ if s' == s then CheckOK else CheckViolation s' lvs
6774
6875transform
6976 :: TabSize -- ^ Expand tab characters to so many spaces. Keep tabs if @<= 0@.
77+ -> ConsecutiveEmptyLines -- ^ Maximum count of consecutive empty lines. Unlimited if @<= 0@.
7078 -> Text -- ^ Text before transformation.
7179 -> Text -- ^ Text after transformation.
72- transform tabSize =
80+ transform tabSize consecutiveLines =
7381 Text. unlines .
82+ squashConsecutiveEmptyLines 0 .
7483 removeFinalEmptyLinesExceptOne .
7584 map (removeTrailingWhitespace . convertTabs tabSize) .
7685 Text. lines
7786 where
7887 removeFinalEmptyLinesExceptOne =
7988 reverse . dropWhile1 Text. null . reverse
8089
90+ squashConsecutiveEmptyLines :: Int -> [Text ] -> [Text ]
91+ squashConsecutiveEmptyLines _ [] = []
92+ squashConsecutiveEmptyLines n (l: ls)
93+ | Text. null l
94+ = if n >= consecutiveLines
95+ then squashConsecutiveEmptyLines n ls
96+ else
97+ l : squashConsecutiveEmptyLines (n + 1 ) ls
98+
99+ | otherwise
100+ = l : squashConsecutiveEmptyLines 0 ls
101+
81102-- | The transformation monad: maintains info about lines that
82103-- violate the rules. Used in the verbose mode to build a log.
83104--
@@ -87,9 +108,10 @@ type TransformM = Writer [LineError]
87108--
88109transformWithLog
89110 :: TabSize -- ^ Expand tab characters to so many spaces. Keep tabs if @<= 0@.
111+ -> ConsecutiveEmptyLines -- ^ Maximum count of consecutive empty lines. Unlimited if @<= 0@.
90112 -> Text -- ^ Text before transformation.
91113 -> (Text , [LineError ]) -- ^ Text after transformation and violating lines if any.
92- transformWithLog tabSize =
114+ transformWithLog tabSize consecutiveLines =
93115 runWriter .
94116 fmap Text. unlines .
95117 fixAllViolations .
@@ -98,6 +120,8 @@ transformWithLog tabSize =
98120 where
99121 fixAllViolations :: [(Int ,Text )] -> TransformM [Text ]
100122 fixAllViolations =
123+ (if consecutiveLines > 0 then squashConsecutiveEmptyLines 1 0 else return )
124+ <=<
101125 removeFinalEmptyLinesExceptOne
102126 <=<
103127 mapM (fixLineWith $ removeTrailingWhitespace . convertTabs tabSize)
@@ -114,6 +138,25 @@ transformWithLog tabSize =
114138 lenLs' = length ls'
115139 els = replicate (lenLs - lenLs') " "
116140
141+ squashConsecutiveEmptyLines :: Int -> Int -> [Text ] -> TransformM [Text ]
142+ squashConsecutiveEmptyLines _ _ [] = return []
143+ squashConsecutiveEmptyLines i n (l: ls)
144+ | Text. null l
145+ = if n >= consecutiveLines
146+ then do
147+ tell [LineError i l]
148+ squashConsecutiveEmptyLinesAfterError (i + 1 ) ls
149+ else
150+ (l: ) <$> squashConsecutiveEmptyLines (i + 1 ) (n + 1 ) ls
151+
152+ | otherwise
153+ = (l: ) <$> squashConsecutiveEmptyLines (i + 1 ) 0 ls
154+
155+ squashConsecutiveEmptyLinesAfterError _ [] = return []
156+ squashConsecutiveEmptyLinesAfterError i (l: ls)
157+ | Text. null l = squashConsecutiveEmptyLinesAfterError (i + 1 ) ls
158+ | otherwise = squashConsecutiveEmptyLines i 0 (l: ls)
159+
117160 fixLineWith :: (Text -> Text ) -> (Int , Text ) -> TransformM Text
118161 fixLineWith fixer (i, l)
119162 | l == l' = pure l
0 commit comments