Skip to content

Commit 8065c3c

Browse files
Support for records formatting (#256)
* Initial test describing simplest scenario for Data step Co-authored-by: Łukasz Gołębiewski <[email protected]> * [sanity-check] Delete data defs * Extract changeDecl Co-authored-by: Łukasz Gołębiewski <[email protected]> * First green test :-) * Cover case where there are more then one field in data type declaration Co-authored-by: Łukasz Gołębiewski <[email protected]> * Add case03 where a type variable is present * Add case04 - multiple declarations * Make case04 pass * Extend tests with case05 Co-authored-by: Łukasz Gołębiewski <[email protected]> * Add pending case06 Co-authored-by: Łukasz Gołębiewski <[email protected]> * Fix case 06 Co-authored-by: Łukasz Gołębiewski <[email protected]> * Add case07 Co-authored-by: Łukasz Gołębiewski <[email protected]> * Add second phantom case * Add records to config * Make indent size configurable for records Co-authored-by: Paweł Szulc <[email protected]> * Fix warnings in Data.hs * Process derivings during record formatting Co-authored-by: Paweł Szulc <[email protected]> * Do not format when context is present Co-authored-by: Paweł Szulc <[email protected]> * Add case 11 - deriving with DerivingStrategies * Bugfix: do not remove empty data declarations Co-authored-by: Paweł Szulc <[email protected]> * Update README example with ability to format records * Add case12 (Point) * Fix case 12 * Factor out processName * Apply hlint suggestions * Extract constructors helper function * Make 'indent' global * Remove unused Stylish.records method * Fix Config formatting in Config.hs * Extract processConstructor function Co-authored-by: Łukasz Gołębiewski <[email protected]> * Refactor datas function Co-authored-by: Łukasz Gołębiewski <[email protected]> * Include comments with AST. Two tests are still failing... * Fix cases 15 and 16 * Do not format records when comments within Co-authored-by: Łukasz Gołębiewski <[email protected]> * Clean-up Data.hs * Refactor Data.hs Co-authored-by: Pawel Szulc <[email protected]>
1 parent 1a86995 commit 8065c3c

File tree

8 files changed

+457
-4
lines changed

8 files changed

+457
-4
lines changed

README.markdown

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,7 @@ import System.Directory (doesFileExist)
5656
import qualified Data.Map as M
5757
import Data.Map ((!), keys, Map)
5858

59-
data Point = Point
60-
{ pointX, pointY :: Double
61-
, pointName :: String
62-
} deriving (Show)
59+
data Point = Point { pointX, pointY :: Double , pointName :: String} deriving (Show)
6360
```
6461

6562
into:

data/stylish-haskell.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ steps:
1515
# # true.
1616
# add_language_pragma: true
1717

18+
# Format record definitions
19+
- records: {}
20+
1821
# Align the right hand side of some elements. This is quite conservative
1922
# and only applies to statements where each element occupies a single
2023
# line. All default to true.
@@ -222,6 +225,9 @@ steps:
222225
# simple_align but is a bit less conservative.
223226
# - squash: {}
224227

228+
# A common indentation setting. Different steps take this into account.
229+
indent: 4
230+
225231
# A common setting is the number of columns (parts of) code will be wrapped
226232
# to. Different steps take this into account.
227233
#

lib/Language/Haskell/Stylish/Config.hs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import qualified System.IO as IO (Newline
3535
import qualified Language.Haskell.Stylish.Config.Cabal as Cabal
3636
import Language.Haskell.Stylish.Config.Internal
3737
import Language.Haskell.Stylish.Step
38+
import qualified Language.Haskell.Stylish.Step.Data as Data
3839
import qualified Language.Haskell.Stylish.Step.Imports as Imports
3940
import qualified Language.Haskell.Stylish.Step.LanguagePragmas as LanguagePragmas
4041
import qualified Language.Haskell.Stylish.Step.SimpleAlign as SimpleAlign
@@ -52,6 +53,7 @@ type Extensions = [String]
5253
--------------------------------------------------------------------------------
5354
data Config = Config
5455
{ configSteps :: [Step]
56+
, configIndent :: Int
5557
, configColumns :: Maybe Int
5658
, configLanguageExtensions :: [String]
5759
, configNewline :: IO.Newline
@@ -119,6 +121,7 @@ parseConfig (A.Object o) = do
119121
-- First load the config without the actual steps
120122
config <- Config
121123
<$> pure []
124+
<*> (o A..:? "indent" A..!= 4)
122125
<*> (o A..:! "columns" A..!= Just 80)
123126
<*> (o A..:? "language_extensions" A..!= [])
124127
<*> (o A..:? "newline" >>= parseEnum newlines IO.nativeNewline)
@@ -141,6 +144,7 @@ parseConfig _ = mzero
141144
catalog :: Map String (Config -> A.Object -> A.Parser Step)
142145
catalog = M.fromList
143146
[ ("imports", parseImports)
147+
, ("records", parseRecords)
144148
, ("language_pragmas", parseLanguagePragmas)
145149
, ("simple_align", parseSimpleAlign)
146150
, ("squash", parseSquash)
@@ -180,6 +184,11 @@ parseSimpleAlign c o = SimpleAlign.step
180184
where
181185
withDef f k = fromMaybe (f SimpleAlign.defaultConfig) <$> (o A..:? k)
182186

187+
--------------------------------------------------------------------------------
188+
parseRecords :: Config -> A.Object -> A.Parser Step
189+
parseRecords c _ = Data.step
190+
<$> pure (configIndent c)
191+
183192

184193
--------------------------------------------------------------------------------
185194
parseSquash :: Config -> A.Object -> A.Parser Step
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
module Language.Haskell.Stylish.Step.Data where
2+
3+
import Data.List (find, intercalate)
4+
import Data.Maybe (maybeToList)
5+
import qualified Language.Haskell.Exts as H
6+
import Language.Haskell.Exts.Comments
7+
import Language.Haskell.Stylish.Block
8+
import Language.Haskell.Stylish.Editor
9+
import Language.Haskell.Stylish.Step
10+
import Language.Haskell.Stylish.Util
11+
import Prelude hiding (init)
12+
13+
datas :: H.Module l -> [H.Decl l]
14+
datas (H.Module _ _ _ _ decls) = decls
15+
datas _ = []
16+
17+
type ChangeLine = Change String
18+
19+
step :: Int -> Step
20+
step indentSize = makeStep "Data" (step' indentSize)
21+
22+
step' :: Int -> Lines -> Module -> Lines
23+
step' indentSize ls (module', allComments) = applyChanges changes ls
24+
where
25+
datas' = datas $ fmap linesFromSrcSpan module'
26+
changes = datas' >>= maybeToList . changeDecl allComments indentSize
27+
28+
findComment :: LineBlock -> [Comment] -> Maybe Comment
29+
findComment lb = find commentOnLine
30+
where
31+
commentOnLine (Comment _ (H.SrcSpan _ start _ end _) _) =
32+
blockStart lb == start && blockEnd lb == end
33+
34+
commentsWithin :: LineBlock -> [Comment] -> [Comment]
35+
commentsWithin lb = filter within
36+
where
37+
within (Comment _ (H.SrcSpan _ start _ end _) _) =
38+
start >= blockStart lb && end <= blockEnd lb
39+
40+
changeDecl :: [Comment] -> Int -> H.Decl LineBlock -> Maybe ChangeLine
41+
changeDecl _ _ (H.DataDecl _ (H.DataType _) Nothing _ [] _) = Nothing
42+
changeDecl allComments indentSize (H.DataDecl block (H.DataType _) Nothing dhead decls derivings)
43+
| null $ commentsWithin block allComments = Just $ change block (const $ concat newLines)
44+
| otherwise = Nothing
45+
where
46+
newLines = fmap constructors zipped ++ [fmap (indented . H.prettyPrint) derivings]
47+
zipped = zip decls ([1..] ::[Int])
48+
constructors (decl, 1) = processConstructor allComments typeConstructor indentSize decl
49+
constructors (decl, _) = processConstructor allComments (indented "| ") indentSize decl
50+
typeConstructor = "data " <> H.prettyPrint dhead <> " = "
51+
indented = indent indentSize
52+
changeDecl _ _ _ = Nothing
53+
54+
processConstructor :: [Comment] -> String -> Int -> H.QualConDecl LineBlock -> [String]
55+
processConstructor allComments init indentSize (H.QualConDecl _ _ _ (H.RecDecl _ dname fields)) = do
56+
init <> H.prettyPrint dname : n1 : ns ++ [indented "}"]
57+
where
58+
n1 = processName "{ " ( extractField $ head fields)
59+
ns = fmap (processName ", " . extractField) (tail fields)
60+
processName prefix (fnames, _type, Nothing) =
61+
indented prefix <> intercalate ", " (fmap H.prettyPrint fnames) <> " :: " <> H.prettyPrint _type
62+
processName prefix (fnames, _type, (Just (Comment _ _ c))) =
63+
indented prefix <> intercalate ", " (fmap H.prettyPrint fnames) <> " :: " <> H.prettyPrint _type <> " --" <> c
64+
extractField (H.FieldDecl lb names _type) = (names, _type, findComment lb allComments)
65+
indented = indent indentSize
66+
processConstructor _ init _ decl = [init <> trimLeft (H.prettyPrint decl)]

stylish-haskell.cabal

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Library
2929

3030
Exposed-modules:
3131
Language.Haskell.Stylish
32+
Language.Haskell.Stylish.Step.Data
3233
Language.Haskell.Stylish.Step.Imports
3334
Language.Haskell.Stylish.Step.LanguagePragmas
3435
Language.Haskell.Stylish.Step.SimpleAlign
@@ -107,6 +108,8 @@ Test-suite stylish-haskell-tests
107108
Language.Haskell.Stylish.Step
108109
Language.Haskell.Stylish.Step.Imports
109110
Language.Haskell.Stylish.Step.Imports.Tests
111+
Language.Haskell.Stylish.Step.Data
112+
Language.Haskell.Stylish.Step.Data.Tests
110113
Language.Haskell.Stylish.Step.LanguagePragmas
111114
Language.Haskell.Stylish.Step.LanguagePragmas.Tests
112115
Language.Haskell.Stylish.Step.SimpleAlign

tests/Language/Haskell/Stylish/Config/Tests.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ dotStylish = unlines $
165165
, " align: false"
166166
, " remove_redundant: true"
167167
, " - trailing_whitespace: {}"
168+
, " - records: {}"
169+
, "indent: 2"
168170
, "columns: 110"
169171
, "language_extensions:"
170172
, " - TemplateHaskell"

0 commit comments

Comments
 (0)