@@ -9,8 +9,10 @@ module Data.Text.FixWhitespace
9
9
, transform
10
10
, transformWithLog
11
11
, TabSize
12
+ , ConsecutiveEmptyLines
12
13
, Verbose
13
14
, defaultTabSize
15
+ , defaultConsecutiveEmptyLines
14
16
)
15
17
where
16
18
@@ -29,12 +31,17 @@ import Data.List.Extra.Drop ( dropWhileEnd1, dropWhile1 )
29
31
30
32
type Verbose = Bool
31
33
type TabSize = Int
34
+ type ConsecutiveEmptyLines = Int
32
35
33
36
-- | Default tab size.
34
37
--
35
38
defaultTabSize :: TabSize
36
39
defaultTabSize = 8
37
40
41
+ -- | Maximum consecutive empty lines
42
+ defaultConsecutiveEmptyLines :: ConsecutiveEmptyLines
43
+ defaultConsecutiveEmptyLines = 0
44
+
38
45
-- | Result of checking a file against the whitespace policy.
39
46
--
40
47
data CheckResult
@@ -54,30 +61,44 @@ data LineError = LineError Int Text
54
61
-- | Check a file against the whitespace policy,
55
62
-- returning a fix if violations occurred.
56
63
--
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 =
59
66
handle (\ (e :: IOException ) -> return $ CheckIOError e) $
60
67
withFile f ReadMode $ \ h -> do
61
68
hSetEncoding h utf8
62
69
s <- Text. hGetContents h
63
70
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, [] )
66
73
return $ if s' == s then CheckOK else CheckViolation s' lvs
67
74
68
75
transform
69
76
:: TabSize -- ^ Expand tab characters to so many spaces. Keep tabs if @<= 0@.
77
+ -> ConsecutiveEmptyLines -- ^ Maximum count of consecutive empty lines. Unlimited if @<= 0@.
70
78
-> Text -- ^ Text before transformation.
71
79
-> Text -- ^ Text after transformation.
72
- transform tabSize =
80
+ transform tabSize consecutiveLines =
73
81
Text. unlines .
82
+ (if consecutiveLines > 0 then squashConsecutiveEmptyLines 0 else id ) .
74
83
removeFinalEmptyLinesExceptOne .
75
84
map (removeTrailingWhitespace . convertTabs tabSize) .
76
85
Text. lines
77
86
where
78
87
removeFinalEmptyLinesExceptOne =
79
88
reverse . dropWhile1 Text. null . reverse
80
89
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
+
81
102
-- | The transformation monad: maintains info about lines that
82
103
-- violate the rules. Used in the verbose mode to build a log.
83
104
--
@@ -87,9 +108,10 @@ type TransformM = Writer [LineError]
87
108
--
88
109
transformWithLog
89
110
:: TabSize -- ^ Expand tab characters to so many spaces. Keep tabs if @<= 0@.
111
+ -> ConsecutiveEmptyLines -- ^ Maximum count of consecutive empty lines. Unlimited if @<= 0@.
90
112
-> Text -- ^ Text before transformation.
91
113
-> (Text , [LineError ]) -- ^ Text after transformation and violating lines if any.
92
- transformWithLog tabSize =
114
+ transformWithLog tabSize consecutiveLines =
93
115
runWriter .
94
116
fmap Text. unlines .
95
117
fixAllViolations .
@@ -98,6 +120,8 @@ transformWithLog tabSize =
98
120
where
99
121
fixAllViolations :: [(Int ,Text )] -> TransformM [Text ]
100
122
fixAllViolations =
123
+ (if consecutiveLines > 0 then squashConsecutiveEmptyLines 1 0 else return )
124
+ <=<
101
125
removeFinalEmptyLinesExceptOne
102
126
<=<
103
127
mapM (fixLineWith $ removeTrailingWhitespace . convertTabs tabSize)
@@ -114,6 +138,25 @@ transformWithLog tabSize =
114
138
lenLs' = length ls'
115
139
els = replicate (lenLs - lenLs') " "
116
140
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
+
117
160
fixLineWith :: (Text -> Text ) -> (Int , Text ) -> TransformM Text
118
161
fixLineWith fixer (i, l)
119
162
| l == l' = pure l
0 commit comments