Skip to content

Commit d12c1cd

Browse files
committed
Initial draft exact printer
1 parent 7691c45 commit d12c1cd

File tree

18 files changed

+377
-32
lines changed

18 files changed

+377
-32
lines changed

Cabal-syntax/Cabal-syntax.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ library
197197
Distribution.Utils.String
198198
Distribution.Utils.Structured
199199
Distribution.Version
200+
Distribution.PackageDescription.ExactPrint
200201
Language.Haskell.Extension
201202

202203
other-extensions:

Cabal-syntax/src/Distribution/Fields/Pretty.hs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ module Distribution.Fields.Pretty
1515
, PrettyField (..)
1616
, showFields
1717
, showFields'
18+
, prettyFieldAnn
1819

1920
-- * Transformation from 'P.Field'
2021
, fromParsecFields
@@ -47,6 +48,13 @@ data PrettyField ann
4748
| PrettyEmpty
4849
deriving (Functor, Foldable, Traversable)
4950

51+
52+
prettyFieldAnn :: PrettyField ann -> Maybe ann
53+
prettyFieldAnn = \case
54+
PrettyField ann _ _ -> Just ann
55+
PrettySection ann _ _ _ -> Just ann
56+
PrettyEmpty -> Nothing
57+
5058
-- | Prettyprint a list of fields.
5159
--
5260
-- Note: the first argument should return 'String's without newlines

Cabal-syntax/src/Distribution/PackageDescription/Configuration.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ finalizePD
476476
(Platform arch os)
477477
impl
478478
constraints
479-
(GenericPackageDescription pkg _ver flags mb_lib0 sub_libs0 flibs0 exes0 tests0 bms0) = do
479+
(GenericPackageDescription pkg _ver flags mb_lib0 sub_libs0 flibs0 exes0 tests0 bms0 _exactPrintMeta) = do
480480
(targetSet, flagVals) <-
481481
resolveWithFlags flagChoices enabled os arch impl constraints condTrees check
482482
let
@@ -556,7 +556,7 @@ resolveWithFlags [] Distribution.System.Linux Distribution.System.I386 (Distribu
556556
-- function.
557557
flattenPackageDescription :: GenericPackageDescription -> PackageDescription
558558
flattenPackageDescription
559-
(GenericPackageDescription pkg _ _ mlib0 sub_libs0 flibs0 exes0 tests0 bms0) =
559+
(GenericPackageDescription pkg _ _ mlib0 sub_libs0 flibs0 exes0 tests0 bms0 _exactPrintMeta) =
560560
pkg
561561
{ library = mlib
562562
, subLibraries = reverse sub_libs
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
{-# LANGUAGE LambdaCase #-}
2+
{-# LANGUAGE RecordWildCards #-}
3+
{-# LANGUAGE OverloadedStrings #-}
4+
module Distribution.PackageDescription.ExactPrint
5+
(exactPrint
6+
) where
7+
8+
import Distribution.Types.GenericPackageDescription
9+
import Distribution.PackageDescription.PrettyPrint
10+
import Data.Text(Text, pack, unpack)
11+
import qualified Text.PrettyPrint as PP
12+
import Text.PrettyPrint(Doc, ($+$), ($$), (<+>))
13+
import qualified Data.Map as Map
14+
import Data.Map(Map)
15+
import Distribution.Fields.Pretty
16+
import qualified Data.Text.Encoding as Text
17+
import Distribution.Fields.Field(FieldName)
18+
import Distribution.Parsec.Position
19+
import Data.List(sortOn)
20+
import Control.Monad(join)
21+
import Distribution.PackageDescription(specVersion)
22+
23+
exactPrint :: GenericPackageDescription -> Text
24+
exactPrint package = foldExactly (exactPrintMeta package) fields
25+
where
26+
fields :: [PrettyField ()]
27+
fields = ppGenericPackageDescription (specVersion (packageDescription (package))) package
28+
29+
30+
31+
foldExactly :: ExactPrintMeta -> [PrettyField ()] -> Text
32+
foldExactly meta' pretty = pack $ PP.render $ currentDoc $ renderLines emptyState positioned
33+
where
34+
positioned :: [PrettyField (Maybe ExactPosition)]
35+
positioned = sortFields $ attachPositions (exactPositions meta') pretty
36+
37+
data RenderState = MkRenderState {
38+
currentPosition :: Position
39+
, currentDoc :: Doc
40+
}
41+
42+
emptyState :: RenderState
43+
emptyState = MkRenderState {
44+
currentPosition = Position 1 1
45+
, currentDoc = mempty
46+
}
47+
48+
renderLines ::
49+
RenderState ->
50+
[PrettyField (Maybe ExactPosition)] -> -- ^ assuming the lines are sorted on exact position
51+
RenderState
52+
renderLines state' fields =
53+
foldr renderLine state' fields
54+
55+
renderLine :: PrettyField (Maybe ExactPosition) -> RenderState -> RenderState
56+
renderLine field (previous@MkRenderState {..}) = case field of
57+
PrettyField mAnn name' doc ->
58+
let
59+
60+
newPosition = case mAnn of
61+
Just position -> retPos (namePosition position)
62+
Nothing -> retPos currentPosition
63+
64+
in MkRenderState {
65+
currentDoc = currentDoc $+$ renderWithPositionAdjustment mAnn currentPosition name' doc,
66+
currentPosition = newPosition
67+
}
68+
PrettySection ann name' ppDoc sectionFields -> previous -- TODO render section
69+
PrettyEmpty -> previous
70+
71+
renderWithPositionAdjustment :: (Maybe ExactPosition) -> Position -> FieldName -> Doc -> Doc
72+
renderWithPositionAdjustment mAnn current name doc =
73+
if rows < 0 then error "unexpected empty negative rows"
74+
else
75+
let
76+
spacing :: Doc
77+
spacing = foldr ($+$) mempty ("" <$ [1..rows])
78+
in
79+
spacing $$
80+
(PP.nest columns
81+
(PP.text fieldName ) <> ((PP.hsep ("" <$ [0..offset])) <> doc))
82+
-- <+> "--" <+> PP.text (show ((rows, columns), mAnn, current, offset)) -- DEBUG
83+
where
84+
(Position rows columns) = case mAnn of
85+
Just position -> (namePosition position) `difference` current
86+
Nothing -> zeroPos
87+
88+
arguments :: [Position]
89+
arguments = foldMap argumentPosition mAnn
90+
91+
fieldName :: String
92+
fieldName = unpack (Text.decodeUtf8 name) <> ":"
93+
94+
offset :: Int
95+
offset = (case arguments of
96+
((Position _ cols):_) -> cols
97+
[] -> 0) - length fieldName - 1
98+
99+
-- pp randomly changes ordering, this undoes that
100+
sortFields :: [PrettyField (Maybe ExactPosition)] -> [PrettyField (Maybe ExactPosition)]
101+
sortFields = reverse . sortOn (join . prettyFieldAnn)
102+
103+
attachPositions :: Map FieldName ExactPosition -> [PrettyField ()] -> [PrettyField (Maybe ExactPosition)]
104+
attachPositions positionLookup = map (annotatePositions positionLookup)
105+
106+
annotatePositions :: Map FieldName ExactPosition -> PrettyField () -> PrettyField (Maybe ExactPosition)
107+
annotatePositions positionLookup = \case
108+
PrettyField _ann name' doc ->
109+
PrettyField (Map.lookup name' positionLookup) name' doc
110+
PrettySection _ann name' ppDoc sectionFields ->
111+
PrettySection (Map.lookup name' positionLookup) name' ppDoc (attachPositions positionLookup sectionFields)
112+
PrettyEmpty -> PrettyEmpty

Cabal-syntax/src/Distribution/PackageDescription/Parsec.hs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import Distribution.Compat.Lens
4141
import Distribution.FieldGrammar
4242
import Distribution.FieldGrammar.Parsec (NamelessField (..))
4343
import Distribution.Fields.ConfVar (parseConditionConfVar)
44-
import Distribution.Fields.Field (FieldName, getName)
44+
import Distribution.Fields.Field (FieldName, getName, fieldLineAnn, sectionArgAnn, nameAnn)
4545
import Distribution.Fields.LexerMonad (LexWarning, toPWarnings)
4646
import Distribution.Fields.ParseResult
4747
import Distribution.Fields.Parser
@@ -92,11 +92,11 @@ parseGenericPackageDescription bs = do
9292
_ -> pure Nothing
9393

9494
case readFields' bs'' of
95-
Right (fs, lexWarnings) -> do
95+
Right (fields, lexWarnings) -> do
9696
when patched $
9797
parseWarning zeroPos PWTQuirkyCabalFile "Legacy cabal file"
9898
-- UTF8 is validated in a prepass step, afterwards parsing is lenient.
99-
parseGenericPackageDescription' csv lexWarnings invalidUtf8 fs
99+
parseGenericPackageDescription' csv lexWarnings invalidUtf8 fields
100100
-- TODO: better marshalling of errors
101101
Left perr -> parseFatalFailure pos (show perr)
102102
where
@@ -151,11 +151,11 @@ parseGenericPackageDescription'
151151
-> Maybe Int
152152
-> [Field Position]
153153
-> ParseResult GenericPackageDescription
154-
parseGenericPackageDescription' scannedVer lexWarnings utf8WarnPos fs = do
154+
parseGenericPackageDescription' scannedVer lexWarnings utf8WarnPos fieldPositions = do
155155
parseWarnings (toPWarnings lexWarnings)
156156
for_ utf8WarnPos $ \pos ->
157157
parseWarning zeroPos PWTUTF $ "UTF8 encoding problem at byte offset " ++ show pos
158-
let (syntax, fs') = sectionizeFields fs
158+
let (syntax, fs') = sectionizeFields fieldPositions
159159
let (fields, sectionFields) = takeFields fs'
160160

161161
-- cabal-version
@@ -199,7 +199,8 @@ parseGenericPackageDescription' scannedVer lexWarnings utf8WarnPos fs = do
199199

200200
-- Sections
201201
let gpd =
202-
emptyGenericPackageDescription
202+
(emptyGenericPackageDescription
203+
{ exactPrintMeta = ExactPrintMeta { exactPositions = toExact fieldPositions, exactComments = mempty} })
203204
& L.packageDescription .~ pd
204205
gpd1 <- view stateGpd <$> execStateT (goSections specVer sectionFields) (SectionS gpd Map.empty)
205206

@@ -234,6 +235,20 @@ parseGenericPackageDescription' scannedVer lexWarnings utf8WarnPos fs = do
234235
++ "' must use section syntax. See the Cabal user guide for details."
235236
maybeWarnCabalVersion _ _ = return ()
236237

238+
toExact :: [Field Position] -> Map FieldName ExactPosition
239+
toExact = foldr toExactStep mempty
240+
241+
toExactStep :: Field Position -> Map FieldName ExactPosition -> Map FieldName ExactPosition
242+
toExactStep field prev = case field of
243+
Field name lines' ->
244+
Map.insert (getName name)
245+
(ExactPosition { namePosition = (nameAnn name), argumentPosition = (fieldLineAnn <$> lines')})
246+
prev
247+
Section name args fields' ->
248+
Map.insert (getName name)
249+
(ExactPosition { namePosition = (nameAnn name), argumentPosition = (sectionArgAnn <$> args)})
250+
$ foldr toExactStep prev fields'
251+
237252
goSections :: CabalSpecVersion -> [Field Position] -> SectionParser ()
238253
goSections specVer = traverse_ process
239254
where

Cabal-syntax/src/Distribution/PackageDescription/PrettyPrint.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ pdToGpd pd =
237237
, condExecutables = mkCondTree' exeName <$> executables pd
238238
, condTestSuites = mkCondTree' testName <$> testSuites pd
239239
, condBenchmarks = mkCondTree' benchmarkName <$> benchmarks pd
240+
, exactPrintMeta = emptyExactPrintMeta
240241
}
241242
where
242243
-- We set CondTree's [Dependency] to an empty list, as it

Cabal-syntax/src/Distribution/Parsec/Position.hs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{-# LANGUAGE DeriveGeneric #-}
2+
{-# LANGUAGE DeriveDataTypeable #-}
23

34
module Distribution.Parsec.Position
45
( Position (..)
@@ -8,6 +9,7 @@ module Distribution.Parsec.Position
89
, zeroPos
910
, positionCol
1011
, positionRow
12+
, difference
1113
) where
1214

1315
import Distribution.Compat.Prelude
@@ -18,10 +20,11 @@ data Position
1820
= Position
1921
{-# UNPACK #-} !Int -- row
2022
{-# UNPACK #-} !Int -- column
21-
deriving (Eq, Ord, Show, Generic)
23+
deriving (Eq, Ord, Show, Generic, Data)
2224

2325
instance Binary Position
2426
instance NFData Position where rnf = genericRnf
27+
instance Structured Position
2528

2629
-- | Shift position by n columns to the right.
2730
incPos :: Int -> Position -> Position
@@ -44,3 +47,6 @@ positionCol (Position _ c) = c
4447
-- | @since 3.0.0.0
4548
positionRow :: Position -> Int
4649
positionRow (Position r _) = r
50+
51+
difference :: Position -> Position -> Position
52+
difference (Position a1 a2) (Position b1 b2) = Position (a1 - b1) (a2 - b2)

Cabal-syntax/src/Distribution/Types/GenericPackageDescription.hs

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55

66
module Distribution.Types.GenericPackageDescription
77
( GenericPackageDescription (..)
8+
, ExactPrintMeta(..)
9+
, ExactPosition(..)
810
, emptyGenericPackageDescription
11+
, emptyExactPrintMeta
912
) where
1013

1114
import Distribution.Compat.Prelude
@@ -28,6 +31,30 @@ import Distribution.Types.Library
2831
import Distribution.Types.TestSuite
2932
import Distribution.Types.UnqualComponentName
3033
import Distribution.Version
34+
import Data.Text(Text, pack)
35+
import Distribution.Fields.Field(FieldName)
36+
import Distribution.Parsec.Position(Position)
37+
38+
data ExactPosition = ExactPosition {namePosition :: Position
39+
-- argument can be filedline or section args
40+
-- recursive names within sections have their own
41+
-- name identifier so they're not modelled
42+
, argumentPosition :: [Position] }
43+
deriving (Show, Eq, Typeable, Data, Generic, Ord)
44+
instance Structured ExactPosition
45+
instance NFData ExactPosition where rnf = genericRnf
46+
instance Binary ExactPosition
47+
48+
49+
data ExactPrintMeta = ExactPrintMeta
50+
{ exactPositions :: Map FieldName ExactPosition
51+
, exactComments :: Map Position Text
52+
}
53+
deriving (Show, Eq, Typeable, Data, Generic)
54+
55+
instance Binary ExactPrintMeta
56+
instance Structured ExactPrintMeta
57+
instance NFData ExactPrintMeta where rnf = genericRnf
3158

3259
-- ---------------------------------------------------------------------------
3360
-- The 'GenericPackageDescription' type
@@ -70,24 +97,42 @@ data GenericPackageDescription = GenericPackageDescription
7097
, CondTree ConfVar [Dependency] Benchmark
7198
)
7299
]
100+
, exactPrintMeta :: ExactPrintMeta
73101
}
74102
deriving (Show, Eq, Typeable, Data, Generic)
75103

104+
76105
instance Package GenericPackageDescription where
77106
packageId = packageId . packageDescription
78107

79-
instance Binary GenericPackageDescription
80108
instance Structured GenericPackageDescription
109+
110+
-- | Required for rebuild monad
111+
instance Binary GenericPackageDescription
81112
instance NFData GenericPackageDescription where rnf = genericRnf
82113

114+
emptyExactPrintMeta :: ExactPrintMeta
115+
emptyExactPrintMeta = ExactPrintMeta mempty mempty
116+
83117
emptyGenericPackageDescription :: GenericPackageDescription
84-
emptyGenericPackageDescription = GenericPackageDescription emptyPackageDescription Nothing [] Nothing [] [] [] [] []
118+
emptyGenericPackageDescription = GenericPackageDescription
119+
{ packageDescription = emptyPackageDescription
120+
, gpdScannedVersion = Nothing
121+
, genPackageFlags = []
122+
, condLibrary = Nothing
123+
, condSubLibraries = []
124+
, condForeignLibs = []
125+
, condExecutables = []
126+
, condTestSuites = []
127+
, condBenchmarks = []
128+
, exactPrintMeta = emptyExactPrintMeta
129+
}
85130

86131
-- -----------------------------------------------------------------------------
87132
-- Traversal Instances
88133

89134
instance L.HasBuildInfos GenericPackageDescription where
90-
traverseBuildInfos f (GenericPackageDescription p v a1 x1 x2 x3 x4 x5 x6) =
135+
traverseBuildInfos f (GenericPackageDescription p v a1 x1 x2 x3 x4 x5 x6 exactPrintMeta') =
91136
GenericPackageDescription
92137
<$> L.traverseBuildInfos f p
93138
<*> pure v
@@ -98,6 +143,7 @@ instance L.HasBuildInfos GenericPackageDescription where
98143
<*> (traverse . L._2 . traverseCondTreeBuildInfo) f x4
99144
<*> (traverse . L._2 . traverseCondTreeBuildInfo) f x5
100145
<*> (traverse . L._2 . traverseCondTreeBuildInfo) f x6
146+
<*> pure exactPrintMeta'
101147

102148
-- We use this traversal to keep [Dependency] field in CondTree up to date.
103149
traverseCondTreeBuildInfo
@@ -118,3 +164,4 @@ traverseCondTreeBuildInfo g = node
118164
CondBranch v
119165
<$> node x
120166
<*> traverse node y
167+

Cabal-syntax/src/Distribution/Types/GenericPackageDescription/Lens.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ allCondTrees
8181
)
8282
-> GenericPackageDescription
8383
-> f GenericPackageDescription
84-
allCondTrees f (GenericPackageDescription p v a1 x1 x2 x3 x4 x5 x6) =
84+
allCondTrees f (GenericPackageDescription p v a1 x1 x2 x3 x4 x5 x6 exactPrintMeta) =
8585
GenericPackageDescription
8686
<$> pure p
8787
<*> pure v
@@ -92,6 +92,7 @@ allCondTrees f (GenericPackageDescription p v a1 x1 x2 x3 x4 x5 x6) =
9292
<*> (traverse . _2) f x4
9393
<*> (traverse . _2) f x5
9494
<*> (traverse . _2) f x6
95+
<*> pure exactPrintMeta
9596

9697
-------------------------------------------------------------------------------
9798
-- Flag

0 commit comments

Comments
 (0)