exactprint/Thrift/ExactPrint/Convert.hs (668 lines of code) (raw):

-- Copyright (c) Facebook, Inc. and its affiliates. {-# LANGUAGE CPP #-} module Thrift.ExactPrint.Convert ( computeOffsets , computeThriftFileOffsets , declOffsets ) where import Prelude hiding (Enum) import Data.Bifunctor import Data.List import Data.Some import Unsafe.Coerce import Thrift.Compiler.Parser import Thrift.Compiler.Types import Thrift.ExactPrint.Types #if MIN_VERSION_dependent_sum(0,6,0) #define This Some #endif computeOffsets :: Program l Loc -> Program l Offset computeOffsets Program{..} = Program { progIncludes = map computeOffsets progIncludes , progHeaders = headers , progDecls = decls , progComments = comments , .. } where (headers, headerEnd) = foldO computeHeaderOffsets absoluteOrigin progHeaders (decls, declsEnd) = foldO computeDeclOffsets headerEnd progDecls (comments, _) = foldO commentOffset declsEnd progComments computeThriftFileOffsets :: ThriftFile a Loc -> ThriftFile a Offset computeThriftFileOffsets ThriftFile{..} = ThriftFile { thriftHeaders = headers , thriftDecls = decls , thriftComments = comments , .. } where (headers, headerEnd) = foldO computeHeaderOffsets absoluteOrigin thriftHeaders (decls, declsEnd) = foldO computeDeclOffsets headerEnd thriftDecls (comments, _) = foldO commentOffset declsEnd thriftComments declOffsets :: [Decl s l Loc] -> [Decl s l Offset] declOffsets = fst . foldO computeDeclOffsets absoluteOrigin absoluteOrigin :: Loc absoluteOrigin = Loc { locFile = "" , locStartLine = 1 , locStartCol = 1 , locEndLine = 1 , locEndCol = 1 } -- Headers --------------------------------------------------------------------- computeHeaderOffsets :: Loc -> Header Loc -> (Header Offset, Loc) computeHeaderOffsets origin HInclude{..} = (HInclude { incKeywordLoc = getOffsets origin incKeywordLoc , incPathLoc = getOffsets (lLocation incKeywordLoc) incPathLoc , .. }, lLocation incPathLoc) computeHeaderOffsets origin HNamespace{..} = (HNamespace { nmKeywordLoc = getOffsets origin nmKeywordLoc , nmLangLoc = getOffsets (lLocation nmKeywordLoc) nmLangLoc , nmNameLoc = getOffsets (lLocation nmLangLoc) nmNameLoc , .. }, lLocation nmNameLoc) -- Decls ----------------------------------------------------------------------- computeDeclOffsets :: Loc -> Decl s l Loc -> (Decl s l Offset, Loc) computeDeclOffsets origin d = case d of D_Struct s -> first D_Struct $ computeStructOffsets origin s D_Union u -> first D_Union $ computeUnionOffsets origin u D_Enum e -> first D_Enum $ computeEnumOffsets origin e D_Typedef t -> first D_Typedef $ computeTypedefOffsets origin t D_Const c -> first D_Const $ computeConstOffsets origin c D_Service s -> first D_Service $ computeServiceOffsets origin s -- Structs --------------------------------------------------------------------- computeStructOffsets :: Loc -> Struct s l Loc -> (Struct s l Offset, Loc) computeStructOffsets origin Struct{..} = (Struct { structMembers = fields , structLoc = offsets , structAnns = anns , structSAnns = sAnns , errorClassifications = eClassns , .. }, structEnd) where (sAnns, sAnnsEnd) = foldO sAnnOffsets origin structSAnns (eClassns, eClassnsEnd) = foldO eClassnOffsets sAnnsEnd errorClassifications (offsets, anns, structEnd) = computeStructLoc eClassnsEnd structLoc structAnns fieldEnd (fields, fieldEnd) = foldO computeFieldOffsets (lLocation slOpenBrace) structMembers StructLoc{..} = structLoc computeStructLoc :: Loc -> StructLoc Loc -> Maybe (Annotations Loc) -> Loc -> (StructLoc Offset, Maybe (Annotations Offset), Loc) computeStructLoc origin StructLoc{..} anns fieldEnd = (StructLoc { slKeyword = getOffsets origin slKeyword , slName = getOffsets (lLocation slKeyword) slName , slOpenBrace = getOffsets (lLocation slName) slOpenBrace , slCloseBrace = getOffsets fieldEnd slCloseBrace }, anns', end) where (anns', end) = annsOffsets (lLocation slCloseBrace) anns -- Unions ---------------------------------------------------------------------- computeUnionOffsets :: Loc -> Union s l Loc -> (Union s l Offset, Loc) computeUnionOffsets origin Union{..} = (Union { unionAlts = alts , unionLoc = offsets , unionAnns = anns , unionSAnns = sAnns , .. }, unionEnd) where (sAnns, sAnnsEnd) = foldO sAnnOffsets origin unionSAnns (offsets, anns, unionEnd) = computeStructLoc sAnnsEnd unionLoc unionAnns altsEnd (alts, altsEnd) = foldO computeAltOffsets (lLocation slOpenBrace) unionAlts StructLoc{..} = unionLoc computeAltOffsets :: Loc -> UnionAlt s l Loc -> (UnionAlt s l Offset, Loc) computeAltOffsets origin UnionAlt{..} = (UnionAlt { altType = ty , altLoc = FieldLoc { flId = getOffsets sAnnsEnd flId , flIdRep = flIdRep , flColon = getOffsets (lLocation flId) flColon , flName = getOffsets tyEnd flName , flEqual = Nothing , flSeparator = sep } , altAnns = anns , altSAnns = sAnns , altResolvedType = unsafeCoerce altResolvedType , .. }, sepEnd) where (sAnns, sAnnsEnd) = foldO sAnnOffsets origin altSAnns (ty, tyEnd) = computeTypeOffsets (lLocation flColon) altType (anns, annsEnd) = annsOffsets (lLocation flName) altAnns (sep, sepEnd) = separatorOffsets annsEnd flSeparator FieldLoc{..} = altLoc -- Fields ---------------------------------------------------------------------- computeFieldOffsets :: Loc -> Field u s l Loc -> (Field u s l Offset, Loc) computeFieldOffsets origin Field{..} = (Field { fieldVal = fst <$> defVal , fieldRequiredness = req , fieldLoc = FieldLoc { flId = getOffsets sAnnsEnd flId , flIdRep = flIdRep , flColon = getOffsets (lLocation flId) flColon , flName = getOffsets tyEnd flName , flEqual = getOffsets (lLocation flName) <$> flEqual , flSeparator = sep } , fieldType = ty , fieldAnns = anns , fieldSAnns = sAnns , .. }, sepEnd) where (sAnns, sAnnsEnd) = foldO sAnnOffsets origin fieldSAnns (req, reqEnd) = case fieldRequiredness of Default -> (Default, lLocation flColon) Required loc -> (Required $ getOffsets (lLocation flColon) loc, lLocation loc) Optional loc -> (Optional $ getOffsets (lLocation flColon) loc, lLocation loc) (ty, tyEnd) = computeTypeOffsets reqEnd fieldType defVal = constOffsets . lLocation <$> flEqual <*> fieldVal fend = maybe (lLocation flName) snd defVal (anns, annsEnd) = annsOffsets fend fieldAnns (sep, sepEnd) = separatorOffsets annsEnd flSeparator FieldLoc{..} = fieldLoc -- Enums ----------------------------------------------------------------------- computeEnumOffsets :: Loc -> Enum s l Loc -> (Enum s l Offset, Loc) computeEnumOffsets origin Enum{..} = (Enum { enumConstants = consts , enumLoc = offsets , enumAnns = anns , enumSAnns = sAnns , .. }, enumEnd) where (sAnns, sAnnsEnd) = foldO sAnnOffsets origin enumSAnns (offsets, anns, enumEnd) = computeStructLoc sAnnsEnd enumLoc enumAnns constEnd (consts, constEnd) = foldO enumValOffsets (lLocation slOpenBrace) enumConstants StructLoc{..} = enumLoc enumValOffsets :: Loc -> EnumValue s l Loc -> (EnumValue s l Offset, Loc) enumValOffsets origin EnumValue{..} = (EnumValue { evLoc = EnumValLoc { evlName = getOffsets sAnnsEnd evlName , evlEqual = getOffsets (lLocation evlName) evlEqual , evlValue = getOffsets (lLocation evlEqual) evlValue , evlRep = evlRep , evlSeparator = sep } , evAnns = anns , evSAnns = sAnns , .. }, sepEnd) where (sAnns, sAnnsEnd) = foldO sAnnOffsets origin evSAnns (anns, annsEnd) = annsOffsets (lLocation evlValue) evAnns (sep, sepEnd) = separatorOffsets annsEnd evlSeparator EnumValLoc{..} = evLoc -- Typedefs -------------------------------------------------------------------- computeTypedefOffsets :: Loc -> Typedef s l Loc -> (Typedef s l Offset, Loc) computeTypedefOffsets origin Typedef{..} = (Typedef { tdType = ty , tdLoc = TypedefLoc { tdlKeyword = getOffsets sAnnsEnd tdlKeyword , tdlName = getOffsets tyEnd tdlName } , tdAnns = anns , tdResolvedType = unsafeCoerce tdResolvedType , tdSAnns = sAnns , .. }, annsEnd) where (sAnns, sAnnsEnd) = foldO sAnnOffsets origin tdSAnns (ty, tyEnd) = computeTypeOffsets (lLocation tdlKeyword) tdType (anns, annsEnd) = annsOffsets (lLocation tdlName) tdAnns TypedefLoc{..} = tdLoc -- Constants ------------------------------------------------------------------- computeConstOffsets :: Loc -> Const s l Loc -> (Const s l Offset, Loc) computeConstOffsets origin Const{..} = (Const { constType = ty , constVal = val , constLoc = ConstLoc { clKeyword = getOffsets sAnnsEnd clKeyword , clName = getOffsets tyEnd clName , clEqual = getOffsets (lLocation clName) clEqual , clSeparator = sep } , constResolvedType = unsafeCoerce constResolvedType , constResolvedVal = unsafeCoerce constResolvedVal , constSAnns = sAnns , .. }, sepEnd) where (sAnns, sAnnsEnd) = foldO sAnnOffsets origin constSAnns (ty, tyEnd) = computeTypeOffsets (lLocation clKeyword) constType (val, valEnd) = constOffsets (lLocation clEqual) constVal (sep, sepEnd) = separatorOffsets valEnd clSeparator ConstLoc{..} = constLoc constOffsets :: Loc -> UntypedConst Loc -> (UntypedConst Offset, Loc) constOffsets origin UntypedConst{..} = (UntypedConst { ucLoc = getOffsets origin ucLoc , ucConst = c }, lLocation end) where (c, end) = case ucConst of IntConst i r -> (IntConst i r, ucLoc) DoubleConst d r -> (DoubleConst d r, ucLoc) StringConst s q -> (StringConst s q, ucLoc) IdConst i -> (IdConst i, ucLoc) BoolConst b -> (BoolConst b, ucLoc) ListConst{..} -> (ListConst { lvElems = elems , lvCloseBrace = getOffsets elemsEnd lvCloseBrace }, lvCloseBrace) where (elems, elemsEnd) = foldO (listElemOffsets constOffsets) (lLocation ucLoc) lvElems MapConst{..} -> (MapConst { mvElems = elems , mvCloseBrace = getOffsets elemsEnd mvCloseBrace }, mvCloseBrace) where (elems, elemsEnd) = foldO (listElemOffsets mapPairOffsets) (lLocation ucLoc) mvElems StructConst{..} -> (StructConst { svType = svType , svOpenBrace = openBrace , svElems = elems , svCloseBrace = getOffsets elemsEnd svCloseBrace }, svCloseBrace) where openBrace = getOffsets (lLocation ucLoc) svOpenBrace (elems, elemsEnd) = foldO (listElemOffsets structPairOffsets) (lLocation svOpenBrace) svElems listElemOffsets :: (Loc -> f Loc -> (f Offset, Loc)) -> Loc -> ListElem f Loc -> (ListElem f Offset, Loc) listElemOffsets f origin ListElem{..} = (ListElem { leElem = el , leSeparator = sep }, sepEnd) where (sep, sepEnd) = separatorOffsets elEnd leSeparator (el, elEnd) = f origin leElem mapPairOffsets :: Loc -> MapPair Loc -> (MapPair Offset, Loc) mapPairOffsets origin MapPair{..} = (MapPair { mpKey = key , mpColon = getOffsets keyEnd mpColon , mpVal = val }, valEnd) where (key, keyEnd) = constOffsets origin mpKey (val, valEnd) = constOffsets (lLocation mpColon) mpVal structPairOffsets :: Loc -> StructPair Loc -> (StructPair Offset, Loc) structPairOffsets origin StructPair{..} = (StructPair { spKey = spKey , spKeyLoc = keyLoc , spEquals = getOffsets (lLocation spKeyLoc) spEquals , spVal = val }, valEnd) where keyLoc = getOffsets origin spKeyLoc (val, valEnd) = constOffsets (lLocation spEquals) spVal -- Services -------------------------------------------------------------------- computeServiceOffsets :: Loc -> Service s l Loc -> (Service s l Offset, Loc) computeServiceOffsets origin Service{..} = (Service { serviceSuper = super , serviceFunctions = funcs , serviceLoc = StructLoc { slKeyword = getOffsets sAnnsEnd slKeyword , slName = getOffsets (lLocation slKeyword) slName , slOpenBrace = getOffsets superEnd slOpenBrace , slCloseBrace = getOffsets funcEnd slCloseBrace } , serviceAnns = anns , serviceSAnns = sAnns , .. }, annsEnd) where (super, superEnd) = case serviceSuper of Nothing -> (Nothing, lLocation slName) Just Super{..} -> (Just Super { supExtends = getOffsets (lLocation slName) supExtends , supLoc = getOffsets (lLocation supExtends) supLoc , .. }, lLocation supLoc) (sAnns, sAnnsEnd) = foldO sAnnOffsets origin serviceSAnns (funcs, funcEnd) = foldO functionOffsets (lLocation slOpenBrace) serviceFunctions (anns, annsEnd) = annsOffsets (lLocation slCloseBrace) serviceAnns StructLoc{..} = serviceLoc functionOffsets :: Loc -> Function s l Loc -> (Function s l Offset, Loc) functionOffsets origin fun@Function{..} = (Function { funType = ty , funArgs = args , funExceptions = exs , funLoc = FunLoc { fnlOneway = getOffsets sAnnsEnd <$> fnlOneway , fnlIdempotency = getOffsets onewayEnd <$> fnlIdempotency , fnlName = getOffsets tyEnd fnlName , fnlOpenParen = getOffsets (lLocation fnlName) fnlOpenParen , fnlCloseParen = getOffsets argsEnd fnlCloseParen , fnlThrows = throws , fnlSeparator = sep } , funAnns = anns , funSAnns = sAnns , .. }, sepEnd) where (sAnns, sAnnsEnd) = foldO sAnnOffsets origin funSAnns onewayEnd = maybe sAnnsEnd lLocation fnlOneway idempotencyEnd = maybe onewayEnd lLocation fnlIdempotency (ty, tyEnd) = case funType of FunType (This t) -> first (FunType . This) $ computeTypeOffsets idempotencyEnd t FunTypeVoid loc -> (FunTypeVoid $ getOffsets idempotencyEnd loc, lLocation loc) FunTypeResponseAndStreamReturn ResponseAndStreamReturn{..} -> (FunTypeResponseAndStreamReturn $ ResponseAndStreamReturn { rsStream = stream , rsComma = comma , rsReturn = ret }, streamEnd) where (ret, retEnd) = case rsReturn of Just r -> first Just $ computeTypeOffsets idempotencyEnd r Nothing -> (Nothing, idempotencyEnd) (comma, commaEnd) = case rsComma of Just c -> (Just $ getOffsets retEnd c, lLocation c) Nothing -> (Nothing, retEnd) (stream, streamEnd) = streamOffsets commaEnd rsStream (args, argsEnd) = foldO computeFieldOffsets (lLocation fnlOpenParen) funArgs (throws, exs, throwsEnd) = case funThrows fun of Just w -> (Just throwsLoc, throwsFields, throwsEnd') where (Throws{..}, throwsEnd') = throwsOffsets (lLocation fnlCloseParen) w Nothing -> (Nothing, [], lLocation fnlCloseParen) (anns, annsEnd) = annsOffsets throwsEnd funAnns (sep, sepEnd) = separatorOffsets annsEnd fnlSeparator FunLoc{..} = funLoc streamOffsets :: Loc -> Stream s l Loc -> (Stream s l Offset, Loc) streamOffsets origin Stream{streamLoc=Arity1Loc{..}, ..} = (Stream { streamType = ty , streamThrows = throws , streamLoc = Arity1Loc { a1Ty = getOffsets origin a1Ty , a1OpenBrace = getOffsets (lLocation a1Ty) a1OpenBrace , a1CloseBrace = getOffsets throwsEnd a1CloseBrace } }, lLocation a1CloseBrace ) where (throws, throwsEnd) = maybeThrowsOffsets tyEnd streamThrows (ty, tyEnd) = computeTypeOffsets (lLocation a1OpenBrace) streamType throwsOffsets:: Loc -> Throws s l Loc -> (Throws s l Offset, Loc) throwsOffsets origin Throws{..} = (Throws { throwsLoc = throws , throwsFields = exs }, throwsEnd) where ThrowsLoc{..} = throwsLoc (throws, exs, throwsEnd) = (ThrowsLoc { tlThrows = getOffsets origin tlThrows , tlOpenParen = getOffsets (lLocation tlThrows) tlOpenParen , tlCloseParen = getOffsets exEnd tlCloseParen }, ex, lLocation tlCloseParen) where (ex, exEnd) = foldO computeFieldOffsets (lLocation tlOpenParen) throwsFields maybeThrowsOffsets :: Loc -> Maybe (Throws s l Loc) -> (Maybe (Throws s l Offset), Loc) maybeThrowsOffsets origin m = case m of Just w -> (Just throws, throwsEnd) where (throws, throwsEnd) = throwsOffsets origin w Nothing -> (Nothing, origin) -- Types ----------------------------------------------------------------------- computeTypeOffsets :: forall t. Loc -> AnnotatedType Loc t -> (AnnotatedType Offset t, Loc) computeTypeOffsets origin AnnotatedType{..} = (,annsEnd) $ case atType of -- Arity 0 Types I8 -> arity0Offsets I8 atLoc I16 -> arity0Offsets I16 atLoc I32 -> arity0Offsets I32 atLoc I64 -> arity0Offsets I64 atLoc TFloat -> arity0Offsets TFloat atLoc TDouble -> arity0Offsets TDouble atLoc TBool -> arity0Offsets TBool atLoc TText -> arity0Offsets TText atLoc TBytes -> arity0Offsets TBytes atLoc TNamed n -> arity0Offsets (TNamed n) atLoc -- Arity 1 Types TList u -> arity1Offsets TList u atLoc TSet u -> arity1Offsets TSet u atLoc THashSet u -> arity1Offsets THashSet u atLoc -- Arity 2 Types TMap k v -> arity2Offsets TMap k v atLoc THashMap k v -> arity2Offsets THashMap k v atLoc where arity0Offsets :: GetArity t ~ 0 => TType 'Unresolved () Offset t -> TypeLoc 0 Loc -> AnnotatedType Offset t arity0Offsets ty Arity0Loc{..} = AnnotatedType { atType = ty , atLoc = Arity0Loc { a0Ty = getOffsets origin a0Ty } , atAnnotations = anns } arity1Offsets :: (GetArity t ~ 1, f a ~ t) => (AnnotatedType Offset a -> TType 'Unresolved () Offset (f a)) -> AnnotatedType Loc a -> TypeLoc 1 Loc -> AnnotatedType Offset (f a) arity1Offsets f a Arity1Loc{..} = AnnotatedType { atType = f ty , atLoc = Arity1Loc { a1Ty = getOffsets origin a1Ty , a1OpenBrace = getOffsets (lLocation a1Ty) a1OpenBrace , a1CloseBrace = getOffsets tyEnd a1CloseBrace } , atAnnotations = anns } where (ty, tyEnd) = computeTypeOffsets (lLocation a1OpenBrace) a arity2Offsets :: (GetArity t ~ 2, f a b ~ t) => (AnnotatedType Offset a -> AnnotatedType Offset b -> TType 'Unresolved () Offset (f a b)) -> AnnotatedType Loc a -> AnnotatedType Loc b -> TypeLoc 2 Loc -> AnnotatedType Offset (f a b) arity2Offsets f a b Arity2Loc{..} = AnnotatedType { atType = f a' b' , atLoc = Arity2Loc { a2Ty = getOffsets origin a2Ty , a2OpenBrace = getOffsets (lLocation a2Ty) a2OpenBrace , a2Comma = getOffsets aEnd a2Comma , a2CloseBrace = getOffsets bEnd a2CloseBrace } , atAnnotations = anns } where (a', aEnd) = computeTypeOffsets (lLocation a2OpenBrace) a (b', bEnd) = computeTypeOffsets (lLocation a2Comma) b (anns, annsEnd) = annsOffsets (getTyEnd atLoc) atAnnotations getTyEnd :: TypeLoc n Loc -> Loc getTyEnd Arity0Loc{..} = lLocation a0Ty getTyEnd Arity1Loc{..} = lLocation a1CloseBrace getTyEnd Arity2Loc{..} = lLocation a2CloseBrace -- Annotations ----------------------------------------------------------------- annsOffsets :: Loc -> Maybe (Annotations Loc) -> (Maybe (Annotations Offset), Loc) annsOffsets origin Nothing = (Nothing, origin) annsOffsets origin (Just Annotations{..}) = (Just Annotations { annList = list , annOpenParen = getOffsets origin annOpenParen , annCloseParen = getOffsets listEnd annCloseParen }, lLocation annCloseParen) where (list, listEnd) = foldO annOffsets (lLocation annOpenParen) annList annOffsets :: Loc -> Annotation Loc -> (Annotation Offset, Loc) annOffsets origin SimpleAnn{..} = (SimpleAnn { saLoc = getOffsets origin saLoc , saSep = sep , .. }, sepEnd) where (sep, sepEnd) = separatorOffsets (lLocation saLoc) saSep annOffsets origin ValueAnn{..} = (ValueAnn { vaTagLoc = getOffsets origin vaTagLoc , vaEqual = getOffsets (lLocation vaTagLoc) vaEqual , vaValLoc = getOffsets (lLocation vaEqual) vaValLoc , vaSep = sep , .. }, sepEnd) where (sep, sepEnd) = separatorOffsets (lLocation vaValLoc) vaSep sAnnOffsets :: Loc -> StructuredAnnotation s l Loc -> (StructuredAnnotation s l Offset, Loc) sAnnOffsets origin StructuredAnn{..} = (StructuredAnn { saAt = getOffsets origin saAt , saMaybeElems = maybeElems , saTypeLoc = typeLoc , saResolvedType = unsafeCoerce saResolvedType , .. }, elemsEnd) where (maybeElems, elemsEnd) = sAnnElemOffsets (lLocation $ a0Ty saTypeLoc) saMaybeElems typeLoc = Arity0Loc { a0Ty = getOffsets (lLocation saAt) (a0Ty saTypeLoc) } sAnnElemOffsets :: Loc -> Maybe (StructuredAnnotationElems Loc) -> (Maybe (StructuredAnnotationElems Offset), Loc) sAnnElemOffsets origin Nothing = (Nothing, origin) sAnnElemOffsets origin (Just StructuredAnnElems{..}) = (Just $ StructuredAnnElems { saOpenBrace = getOffsets origin saOpenBrace , saElems = elems , saCloseBrace = getOffsets elemsEnd saCloseBrace , .. }, lLocation saCloseBrace) where (elems, elemsEnd) = foldO (listElemOffsets structPairOffsets) (lLocation saOpenBrace) saElems eClassnOffsets :: Loc -> ErrorClassification Loc -> (ErrorClassification Offset, Loc) eClassnOffsets origin ErrorClassification{..} = (ErrorClassification { ecKeywordLoc = getOffsets origin ecKeywordLoc , .. }, lLocation ecKeywordLoc) -- Helpers --------------------------------------------------------------------- separatorOffsets :: Loc -> Separator Loc -> (Separator Offset, Loc) separatorOffsets origin = \case NoSep -> (NoSep, origin) Semicolon loc -> (Semicolon $ getOffsets origin loc, lLocation loc) Comma loc -> (Comma $ getOffsets origin loc, lLocation loc) foldO :: (Loc -> a -> (b, Loc)) -> Loc -> [a] -> ([b], Loc) foldO f origin xs = first reverse $ foldl' (\(ys, loc) x -> first (:ys) $ f loc x) ([], origin) xs getOffsets :: Loc -> Located Loc -> Located Offset getOffsets origin Located{..} = Located { lLocation = getDelta commentsEnd lLocation , lComments = comments } where (comments, commentsEnd) = foldO commentOffset origin lComments commentOffset :: Loc -> Comment Loc -> (Comment Offset, Loc) commentOffset origin (Comment loc txt) = (Comment (getDelta origin loc) txt, loc) getDelta :: Loc -> Loc -> Offset getDelta l1 l2 = Offset{..} where offsRows = locStartLine l2 - locEndLine l1 offsCols | offsRows > 0 = locStartCol l2 - 1 | otherwise = locStartCol l2 - locEndCol l1