haskell-token-utils 0.0.0.1 → 0.0.0.2
raw patch · 7 files changed
+1996/−271 lines, 7 filesdep −kuredep ~basePVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies removed: kure
Dependency ranges changed: base
API changes (from Hackage documentation)
+ Language.Haskell.TokenUtils.API: isMarked :: IsToken a => a -> Bool
+ Language.Haskell.TokenUtils.API: markToken :: IsToken a => a -> a
+ Language.Haskell.TokenUtils.API: mkZeroToken :: IsToken a => a
+ Language.Haskell.TokenUtils.GHC.Layout: mkToken :: Token -> SimpPos -> String -> GhcPosToken
+ Language.Haskell.TokenUtils.TokenUtils: PlaceAbsCol :: !Int -> !Int -> !Int -> Positioning
+ Language.Haskell.TokenUtils.TokenUtils: PlaceAbsolute :: !Int -> !Int -> Positioning
+ Language.Haskell.TokenUtils.TokenUtils: PlaceAdjacent :: Positioning
+ Language.Haskell.TokenUtils.TokenUtils: PlaceIndent :: !Int -> !Int -> !Int -> Positioning
+ Language.Haskell.TokenUtils.TokenUtils: PlaceOffset :: !Int -> !Int -> !Int -> Positioning
+ Language.Haskell.TokenUtils.TokenUtils: RT :: [a] -> ReversedToks a
+ Language.Haskell.TokenUtils.TokenUtils: addNewSrcSpanAndToksAfter :: IsToken a => Tree (Entry a) -> Span -> Span -> Positioning -> [a] -> (Tree (Entry a), Span)
+ Language.Haskell.TokenUtils.TokenUtils: addOffsetToSpan :: (Int, Int) -> Span -> Span
+ Language.Haskell.TokenUtils.TokenUtils: addToksAfterSrcSpan :: IsToken a => Tree (Entry a) -> Span -> Positioning -> [a] -> (Tree (Entry a), Span)
+ Language.Haskell.TokenUtils.TokenUtils: calcEndGap :: IsToken a => Tree (Entry a) -> ForestSpan -> SimpPos
+ Language.Haskell.TokenUtils.TokenUtils: containsEnd :: ForestSpan -> ForestSpan -> Bool
+ Language.Haskell.TokenUtils.TokenUtils: containsMiddle :: ForestSpan -> ForestSpan -> Bool
+ Language.Haskell.TokenUtils.TokenUtils: containsStart :: ForestSpan -> ForestSpan -> Bool
+ Language.Haskell.TokenUtils.TokenUtils: data Positioning
+ Language.Haskell.TokenUtils.TokenUtils: data ReversedToks a
+ Language.Haskell.TokenUtils.TokenUtils: forestSpanToGhcPos :: ForestSpan -> (SimpPos, SimpPos)
+ Language.Haskell.TokenUtils.TokenUtils: getIndentOffset :: IsToken a => [a] -> SimpPos -> Int
+ Language.Haskell.TokenUtils.TokenUtils: getSrcSpanFor :: IsToken a => Tree (Entry a) -> ForestSpan -> (Tree (Entry a), Tree (Entry a))
+ Language.Haskell.TokenUtils.TokenUtils: getTokensBefore :: IsToken a => Tree (Entry a) -> Span -> (Tree (Entry a), ReversedToks a)
+ Language.Haskell.TokenUtils.TokenUtils: getTokensFor :: IsToken a => Bool -> Tree (Entry a) -> Span -> (Tree (Entry a), [a])
+ Language.Haskell.TokenUtils.TokenUtils: getTokensForNoIntros :: IsToken a => Bool -> Tree (Entry a) -> Span -> (Tree (Entry a), [a])
+ Language.Haskell.TokenUtils.TokenUtils: getTreeSpansAsList :: IsToken a => Tree (Entry a) -> [(Int, ForestSpan)]
+ Language.Haskell.TokenUtils.TokenUtils: indentDeclToks :: (IsToken a, HasLoc t) => (t -> ForestSpan -> t) -> t -> Tree (Entry a) -> Int -> (t, Tree (Entry a))
+ Language.Haskell.TokenUtils.TokenUtils: initTokenCache :: IsToken a => [a] -> TokenCache a
+ Language.Haskell.TokenUtils.TokenUtils: initTokenCacheLayout :: IsToken a => Tree (Entry a) -> TokenCache a
+ Language.Haskell.TokenUtils.TokenUtils: insertLenChangedInSrcSpan :: Bool -> Bool -> Span -> Span
+ Language.Haskell.TokenUtils.TokenUtils: insertNodeAfter :: IsToken a => Tree (Entry a) -> Tree (Entry a) -> Tree (Entry a) -> Tree (Entry a)
+ Language.Haskell.TokenUtils.TokenUtils: insertSrcSpan :: IsToken a => Tree (Entry a) -> ForestSpan -> Tree (Entry a)
+ Language.Haskell.TokenUtils.TokenUtils: insertVersionsInSrcSpan :: Int -> Int -> Span -> Span
+ Language.Haskell.TokenUtils.TokenUtils: instance Show Positioning
+ Language.Haskell.TokenUtils.TokenUtils: instance Show a => Show (ReversedToks a)
+ Language.Haskell.TokenUtils.TokenUtils: limitPrevToks :: IsToken a => ReversedToks a -> Span -> ReversedToks a
+ Language.Haskell.TokenUtils.TokenUtils: mkTreeFromSpanTokens :: IsToken a => ForestSpan -> [a] -> Tree (Entry a)
+ Language.Haskell.TokenUtils.TokenUtils: mkTreeFromTokens :: IsToken a => [a] -> Tree (Entry a)
+ Language.Haskell.TokenUtils.TokenUtils: newLnToken :: IsToken a => a -> a
+ Language.Haskell.TokenUtils.TokenUtils: nullForestPos :: ForestPos
+ Language.Haskell.TokenUtils.TokenUtils: nullForestSpan :: ForestSpan
+ Language.Haskell.TokenUtils.TokenUtils: openZipperToSpan :: IsToken a => ForestSpan -> TreePos Full (Entry a) -> TreePos Full (Entry a)
+ Language.Haskell.TokenUtils.TokenUtils: openZipperToSpanAdded :: IsToken a => ForestSpan -> TreePos Full (Entry a) -> TreePos Full (Entry a)
+ Language.Haskell.TokenUtils.TokenUtils: openZipperToSpanOrig :: IsToken a => ForestSpan -> TreePos Full (Entry a) -> TreePos Full (Entry a)
+ Language.Haskell.TokenUtils.TokenUtils: placeToksForSpan :: IsToken a => Tree (Entry a) -> Span -> Tree (Entry a) -> Positioning -> [a] -> [a]
+ Language.Haskell.TokenUtils.TokenUtils: putToksInCache :: IsToken a => TokenCache a -> Span -> [a] -> (TokenCache a, Span)
+ Language.Haskell.TokenUtils.TokenUtils: reAlignMarked :: IsToken a => [a] -> [a]
+ Language.Haskell.TokenUtils.TokenUtils: reAlignOneLine :: IsToken a => [a] -> [a]
+ Language.Haskell.TokenUtils.TokenUtils: reIndentToks :: IsToken a => Positioning -> [a] -> [a] -> [a]
+ Language.Haskell.TokenUtils.TokenUtils: removeSrcSpan :: IsToken a => Tree (Entry a) -> ForestSpan -> (Tree (Entry a), Tree (Entry a))
+ Language.Haskell.TokenUtils.TokenUtils: removeToksFromCache :: IsToken a => TokenCache a -> Span -> TokenCache a
+ Language.Haskell.TokenUtils.TokenUtils: retrievePrevLineToks :: IsToken a => TreePos Full (Entry a) -> ReversedToks a
+ Language.Haskell.TokenUtils.TokenUtils: retrieveTokensInterim :: IsToken a => Tree (Entry a) -> [a]
+ Language.Haskell.TokenUtils.TokenUtils: reverseToks :: IsToken a => [a] -> ReversedToks a
+ Language.Haskell.TokenUtils.TokenUtils: reversedToks :: IsToken a => ReversedToks a -> [a]
+ Language.Haskell.TokenUtils.TokenUtils: showToks :: IsToken a => [a] -> String
+ Language.Haskell.TokenUtils.TokenUtils: showTree :: IsToken a => Tree (Entry a) -> String
+ Language.Haskell.TokenUtils.TokenUtils: simpPosToForestSpan :: (SimpPos, SimpPos) -> ForestSpan
+ Language.Haskell.TokenUtils.TokenUtils: splitOnNewLn :: IsToken a => [a] -> ([a], [a])
+ Language.Haskell.TokenUtils.TokenUtils: splitSubToks :: IsToken a => Tree (Entry a) -> (ForestPos, ForestPos) -> ([Tree (Entry a)], [Tree (Entry a)], [Tree (Entry a)])
+ Language.Haskell.TokenUtils.TokenUtils: splitSubtree :: IsToken a => Tree (Entry a) -> ForestSpan -> ([Tree (Entry a)], [Tree (Entry a)], [Tree (Entry a)])
+ Language.Haskell.TokenUtils.TokenUtils: startEndLocIncComments' :: IsToken a => [a] -> (SimpPos, SimpPos) -> (SimpPos, SimpPos)
+ Language.Haskell.TokenUtils.TokenUtils: unReverseToks :: IsToken a => ReversedToks a -> [a]
+ Language.Haskell.TokenUtils.TokenUtils: updateTokensForSrcSpan :: IsToken a => Tree (Entry a) -> Span -> [a] -> (Tree (Entry a), Span, Tree (Entry a))
+ Language.Haskell.TokenUtils.Types: forestLenChangedMask :: Int
+ Language.Haskell.TokenUtils.Types: isMarked :: IsToken a => a -> Bool
+ Language.Haskell.TokenUtils.Types: markToken :: IsToken a => a -> a
+ Language.Haskell.TokenUtils.Types: mkZeroToken :: IsToken a => a
+ Language.Haskell.TokenUtils.Utils: divideComments :: IsToken a => Int -> Int -> [a] -> ([a], [a])
+ Language.Haskell.TokenUtils.Utils: drawTokenCache :: IsToken a => TokenCache a -> String
+ Language.Haskell.TokenUtils.Utils: drawTokenCacheDetailed :: IsToken a => TokenCache a -> String
+ Language.Haskell.TokenUtils.Utils: nonCommentSpan :: IsToken a => [a] -> (SimpPos, SimpPos)
+ Language.Haskell.TokenUtils.Utils: startEndLocIncComments' :: IsToken a => [a] -> (SimpPos, SimpPos) -> (SimpPos, SimpPos)
- Language.Haskell.TokenUtils.TokenUtils: invariant :: a
+ Language.Haskell.TokenUtils.TokenUtils: invariant :: IsToken a => Tree (Entry a) -> [String]
Files
- haskell-token-utils.cabal +3/−3
- src-ghc/Language/Haskell/TokenUtils/GHC/Layout.hs +50/−0
- src-hse/Language/Haskell/TokenUtils/HSE/Layout.hs +2/−0
- src/Language/Haskell/TokenUtils/Pretty.hs +9/−1
- src/Language/Haskell/TokenUtils/TokenUtils.hs +1854/−266
- src/Language/Haskell/TokenUtils/Types.hs +10/−1
- src/Language/Haskell/TokenUtils/Utils.hs +68/−0
haskell-token-utils.cabal view
@@ -1,5 +1,5 @@ name: haskell-token-utils-version: 0.0.0.1+version: 0.0.0.2 synopsis: Utilities to tie up tokens to an AST description: This library is currently experimental. .@@ -42,7 +42,7 @@ , Language.Haskell.TokenUtils.HSE.Layout -- other-modules: -- other-extensions: - build-depends: base >=4.6 && <4.7+ build-depends: base >=4.5 && <4.7 , containers , dual-tree , semigroups@@ -89,7 +89,7 @@ , mtl , pretty -- experimentation- , kure+ -- , kure , syb -- For testing the GHC version
src-ghc/Language/Haskell/TokenUtils/GHC/Layout.hs view
@@ -16,6 +16,7 @@ , retrieveTokens , getLoc , nullSrcSpan+ , mkToken -- * For testing , addEndOffsets@@ -2060,6 +2061,18 @@ -- --------------------------------------------------------------------- +-- |Compose a new token using the given arguments.+mkToken::GHC.Token -> SimpPos -> String -> GhcPosToken+mkToken t (row,col) c = ((GHC.L l t),c)+ where+ filename = (GHC.mkFastString "f")+ l = GHC.mkSrcSpan (GHC.mkSrcLoc filename row col) (GHC.mkSrcLoc filename row (col + (length c) ))++ghcZeroToken :: GhcPosToken+ghcZeroToken = mkToken GHC.ITsemi (0,0) ""++-- ---------------------------------------------------------------------+ nullSrcSpan :: GHC.SrcSpan nullSrcSpan = GHC.UnhelpfulSpan $ GHC.mkFastString "HaRe nullSrcSpan" @@ -2091,6 +2104,7 @@ isComment = ghcIsComment isEmpty = ghcIsEmpty+ mkZeroToken = ghcZeroToken isDo = ghcIsDo isElse = ghcIsElse isIn = ghcIsIn@@ -2102,6 +2116,9 @@ tokenToString (_,s) = s showTokenStream = GHC.showRichTokenStream + markToken = ghcMarkToken+ isMarked = ghcIsMarked+ instance (HasLoc (GHC.Located a)) where getLoc (GHC.L l _) = start where Span start end = g2s l getLocEnd (GHC.L l _) = end where Span start end = g2s l@@ -2113,9 +2130,41 @@ instance Show (GHC.GenLocated GHC.SrcSpan GHC.Token) where show t@(GHC.L _l tok) = show ((getLocatedStart t, getLocatedEnd t),tok)+ -- show t@(GHC.L _l tok) = show ((getLocatedStart t),tok) -- --------------------------------------------------------------------- +-- |Used as a marker in the filename part of the SrcSpan on modified+-- tokens, to trigger re-alignment when retrieving the tokens.+tokenFileMark :: GHC.FastString+tokenFileMark = GHC.mkFastString "HaRe"++-- |Mark a token so that it can be use to trigger layout checking+-- later when the toks are retrieved+ghcMarkToken :: GhcPosToken -> GhcPosToken+ghcMarkToken tok = tok'+ where+ (GHC.L l t,s) = tok+ tok' = (GHC.L (GHC.RealSrcSpan l') t,s)++ l' = case l of+ GHC.RealSrcSpan ss ->+ GHC.mkRealSrcSpan+ (GHC.mkRealSrcLoc tokenFileMark (GHC.srcSpanStartLine ss) (GHC.srcSpanStartCol ss))+ (GHC.mkRealSrcLoc tokenFileMark (GHC.srcSpanEndLine ss) (GHC.srcSpanEndCol ss))++ _ -> error $ "markToken: expecting a real SrcSpan, got" -- ++ (showGhc l)+++-- |Does a token have the file mark in it+ghcIsMarked :: GhcPosToken -> Bool+ghcIsMarked (GHC.L l _,_) =+ case l of+ GHC.RealSrcSpan ss -> GHC.srcSpanFile ss == tokenFileMark+ _ -> False++-- ---------------------------------------------------------------------+ ghcGetSpan :: GhcPosToken -> Span ghcGetSpan (GHC.L l _,_) = g2s l @@ -2196,6 +2245,7 @@ -} +ghcTokenLen :: (t, [a]) -> Int ghcTokenLen (_,s) = length s
src-hse/Language/Haskell/TokenUtils/HSE/Layout.hs view
@@ -88,6 +88,8 @@ -- No empty tokens in HSE isEmpty _ = False + mkZeroToken = (Loc (s2ss nullSpan) (T (VarId "")))+ isDo (Loc _ (T KW_Do)) = True isDo (Loc _ (T KW_MDo)) = True isDo _ = False
src/Language/Haskell/TokenUtils/Pretty.hs view
@@ -95,9 +95,17 @@ ppr (Line r c o s f str) = parens $ text "Line" <+> ppr r <+> ppr c <+> ppr o <+> ppr s <+> ppr f- <+> text ("\"" ++ (init $ showTokenStream str) ++ "\"")+ <+> text ("\"" ++ (safeShowTokenStream str) ++ "\"")+ -- <+> text ("\"" ++ (init $ showTokenStream str) ++ "\"") -- <+> text ("\"" ++ (init $ showFriendlyToks str) ++ "\"") -- <+> text (show str) -- ++AZ++ debug++safeShowTokenStream :: IsToken a => [a] -> String+safeShowTokenStream str =+ let+ s = showTokenStream str+ in+ if s == "" then s else init s instance Outputable Source where ppr SOriginal = text "SOriginal"
src/Language/Haskell/TokenUtils/TokenUtils.hs view
@@ -1,268 +1,1856 @@ module Language.Haskell.TokenUtils.TokenUtils (- replaceTokenInCache- , replaceTokenForSrcSpan- , invariant- ) where--import Control.Exception-import Data.List-import Data.Tree--import Language.Haskell.TokenUtils.Types-import Language.Haskell.TokenUtils.Utils--import qualified Data.Map as Map-import qualified Data.Tree.Zipper as Z---- -----------------------------------------------------------------------invariant :: a-invariant = assert False undefined---- -----------------------------------------------------------------------replaceTokenInCache :: (IsToken a) => TokenCache a -> Span -> a -> TokenCache a-replaceTokenInCache tk sspan tok = tk'- where- forest = getTreeFromCache sspan tk- forest' = replaceTokenForSrcSpan forest sspan tok- tk' = replaceTreeInCache sspan forest' tk---- -----------------------------------------------------------------------getTreeFromCache :: (IsToken a) => Span -> TokenCache a -> Tree (Entry a)-getTreeFromCache sspan tk = (tkCache tk) Map.! tid- where- tid = treeIdFromForestSpan $ srcSpanToForestSpan sspan---- -----------------------------------------------------------------------replaceTreeInCache :: (IsToken a) => Span -> Tree (Entry a) -> TokenCache a -> TokenCache a-replaceTreeInCache sspan tree tk = tk'- where- tid = treeIdFromForestSpan $ srcSpanToForestSpan sspan- -- tree' = treeIdIntoTree tid tree- tree' = putTidInTree tid tree- tk' = tk {tkCache = Map.insert tid tree' (tkCache tk) }--putTidInTree :: (IsToken a) => TreeId -> Tree (Entry a) -> Tree (Entry a)-putTidInTree tid (Node (Deleted fspan pg eg) subs) = (Node (Deleted fs' pg eg) subs)- where fs' = treeIdIntoForestSpan tid fspan-putTidInTree tid (Node (Entry fspan lay toks) subs) = tree'- where- subs' = map (putTidInTree tid) subs- fs' = treeIdIntoForestSpan tid fspan- tree' = Node (Entry fs' lay toks) subs'---- ------------------------------------------------------------------------- |Replace a single token in a token tree, without changing the--- structure of the tree--- NOTE: the GHC.SrcSpan may have been used to select the appropriate--- forest in the first place, and is required to select the correct--- span in the tree, due to the ForestLine annotations that may be present---- TODO: work at the token level, not the sspan level--- TODO: Use start of token span only, with length 1.-replaceTokenForSrcSpan :: (IsToken a) => Tree (Entry a) -> Span -> a -> Tree (Entry a)-replaceTokenForSrcSpan forest sspan tok = forest'- where- -- (GHC.L tl _,_) = tok- tl = getSpan tok- -- First open to the sspan, making use of any Forestline annotations- z = openZipperToSpanDeep (srcSpanToForestSpan sspan) $ Z.fromTree forest-- -- Then drill down to the specific subtree containing the token- -- z' = openZipperToSpan (srcSpanToForestSpan tl) z- z' = z -- No, pass in original token span as sspan.-- -- Note: with LayoutTree, the full tree matching the AST has been- -- built, still need to drill down to the nearest enclosing span- (tspan,lay,toks) = case Z.tree z' of- (Node (Entry ss ly tks) []) -> (ss,ly,tks)- (Node (Entry _ _ _nullToks) _sub) -> error $ "replaceTokenForSrcSpan:tok pos" ++ (showForestSpan $ sf sspan) ++ " expecting tokens, found: " ++ (show $ Z.tree z')- (Node (Deleted _ _ _) _sub) -> error $ "replaceTokenForSrcSpan:tok pos" ++ (showForestSpan $ sf sspan) ++ " expecting Entry, found: " ++ (show $ Z.tree z')-- ((row,col),_) = forestSpanToSimpPos $ srcSpanToForestSpan tl- toks' = replaceTokNoReAlign toks (row,col) tok-- zf = Z.setTree (Node (Entry tspan lay toks') []) z'- forest' = Z.toTree zf----- ------------------------------------------------------------------------- |Open a zipper so that its focus has the given SrcSpan in its--- subtree, or the location where the SrcSpan should go, if it is not--- in the tree.--- In the case of an 'Above' layout with the same SrcSpan below,--- return that instead-openZipperToSpanDeep- :: (IsToken a)- => ForestSpan- -> Z.TreePos Z.Full (Entry a)- -> Z.TreePos Z.Full (Entry a)-openZipperToSpanDeep sspan z = zf- where- z' = openZipperToSpan sspan z-- zf = case Z.tree z' of- (Node (Entry _ (Above _ _ _ _) _) _) ->- case getChildrenAsZ z' of- [] -> z'- [x] -> if (treeStartEnd (Z.tree x) == sspan) then x else z'- _ -> z'- _ -> z'----- ------------------------------------------------------------------------- |Open a zipper so that its focus has the given SrcSpan in its--- subtree, or the location where the SrcSpan should go, if it is not--- in the tree-openZipperToSpan ::- (IsToken a)- => ForestSpan- -> Z.TreePos Z.Full (Entry a)- -> Z.TreePos Z.Full (Entry a)-openZipperToSpan sspan z- | hasVersions = openZipperToSpanAdded sspan z- | otherwise = openZipperToSpanOrig sspan z- where- (vs,_ve) = forestSpanVersions sspan- hasVersions = vs /= 0----- ------------------------------------------------------------------------- |Open a zipper so that its focus has the given SrcSpan in its--- subtree, or the location where the SrcSpan should go, if it is not--- in the tree-openZipperToSpanOrig ::- (IsToken a)- => ForestSpan- -> Z.TreePos Z.Full (Entry a)- -> Z.TreePos Z.Full (Entry a)-openZipperToSpanOrig sspan z- = if (treeStartEnd (Z.tree z) == sspan) || (Z.isLeaf z)- then z- else z'- where- -- go through all of the children to find the one that- -- either is what we are looking for, or contains it-- -- childrenAsZ = go [] (Z.firstChild z)- childrenAsZ = getChildrenAsZ z- z' = case (filter contains childrenAsZ) of- [] -> z -- Not directly in a subtree, this is as good as- -- it gets- [x] -> -- exactly one, drill down- openZipperToSpan sspan x-- xx -> case (filter (\zt -> (treeStartEnd $ Z.tree zt) == sspan) xx) of - [] -> -- more than one matches, see if we can get- -- rid of the ones that have been lengthened- case (filter (not .forestSpanLenChanged . treeStartEnd . Z.tree) xx) of- [] -> z -- we tried...- [w] -> openZipperToSpan sspan w- -- ww -> error $ "openZipperToSpan:can't resolve:(sspan,ww)="++(show (sspan,ww))- ww -> -- more than one candidate, break- -- the tie on version match- case (filter (\zt -> matchVersions sspan zt) ww) of- [v] -> openZipperToSpan sspan v- _ -> error $ "openZipperToSpan:can't resolve:(sspan,ww)="++(show (sspan,map (\zt -> treeStartEnd $ Z.tree zt) ww))- [y] -> openZipperToSpan sspan y- yy -> -- Multiple, check if we can separate out by- -- version- case (filter (\zt -> (fst $ forestSpanVersions $ treeStartEnd $ Z.tree zt) == (fst $ forestSpanVersions sspan)) xx) of- -- [] -> z- [] -> error $ "openZipperToSpan:no version match:(sspan,yy)=" ++ (show (sspan,yy)) -- ++AZ++- [w] -> openZipperToSpan sspan w- _ww -> error $ "openZipperToSpan:multiple version match:" ++ (show (sspan,yy)) -- ++AZ++-- contains zn = spanContains (treeStartEnd $ Z.tree zn) sspan-- matchVersions span1 z2 = isMatch- where- span2 = treeStartEnd $ Z.tree z2- isMatch = forestSpanVersions span1 == forestSpanVersions span2---- ------------------------------------------------------------------------- |Open a zipper to a SrcSpan that has been added in the tree, and--- thus does not necessarily fall in the logical hierarchy of the tree-openZipperToSpanAdded ::- (IsToken a)- => ForestSpan- -> Z.TreePos Z.Full (Entry a)- -> Z.TreePos Z.Full (Entry a)-openZipperToSpanAdded sspan z = zf- where- treeAsList = getTreeSpansAsList $ Z.tree z-- -- True if first span contains the second- myMatch (((ForestLine _ _ vs1 rs1),cs1),((ForestLine _ _ ve1 re1),ce1))- (((ForestLine _ _ vs2 rs2),cs2),((ForestLine _ _ ve2 re2),ce2))- = vs1 == vs2 && ve1 == ve2 && ((rs1,cs1) <= (rs2,cs2)) && ((re1,ce1) >= (re2,ce2))- tl2 = dropWhile (\(_,s) -> not (myMatch s sspan)) $ reverse treeAsList-- fff [] _ = []- fff acc@((cd,_cs):_) (v,sspan') = if v < cd then (v,sspan'):acc- else acc-- tl3 = foldl' fff [(head tl2)] tl2- -- tl3 now contains the chain of ForestSpans to open in order in the zipper-- zf = foldl' (flip openZipperToSpanOrig) z $ map snd tl3---- -----------------------------------------------------------------------getTreeSpansAsList :: (IsToken a) => Tree (Entry a) -> [(Int,ForestSpan)]-getTreeSpansAsList = getTreeSpansAsList' 0--getTreeSpansAsList' :: (IsToken a) => Int -> Tree (Entry a) -> [(Int,ForestSpan)]-getTreeSpansAsList' level (Node (Deleted sspan _pg _eg ) _ ) = [(level,sspan)]-getTreeSpansAsList' level (Node (Entry sspan _lay _toks) ts0) = (level,sspan)- : (concatMap (getTreeSpansAsList' (level + 1)) ts0)----- -----------------------------------------------------------------------getChildrenAsZ :: Z.TreePos Z.Full a -> [Z.TreePos Z.Full a]-getChildrenAsZ z = go [] (Z.firstChild z)- where- go acc Nothing = acc- go acc (Just zz) = go (acc ++ [zz]) (Z.next zz)---- ------------------------------------------------------------------------- |Replace a single token in the token stream by a new token, without--- adjusting the layout.--- Note1: does not re-align, else other later replacements may fail.--- Note2: must keep original end col, to know what the inter-token gap--- was when re-aligning-replaceTokNoReAlign:: (IsToken a) => [a] -> SimpPos -> a -> [a]-replaceTokNoReAlign toks pos newTok =- toks1 ++ [newTok'] ++ toksRest- where- (toks1,toks2) = break (\t -> tokenPos t >= pos && tokenLen t > 0) toks- toksRest = if (null toks2) then [] else (gtail "replaceTokNoReAlign" toks2)- oldTok = if (null toks2) then newTok else (ghead "replaceTokNoReAlign" toks2)- -- newTok' = markToken $ matchTokenPos oldTok newTok- newTok' = matchTokenPos oldTok newTok---- ------------------------------------------------------------------------- |Transfer the location information from the first param to the second-matchTokenPos :: (IsToken a) => a -> a -> a-matchTokenPos t1 t2 = putSpan t2 (getSpan t1)--{---- |Transfer the location information from the first param to the second-matchTokenPos :: PosToken -> PosToken -> PosToken-matchTokenPos (GHC.L l _,_) (GHC.L _ t,s) = (GHC.L l t,s)--}---- ----------------------------------------------------------------------+ -- * Creating+ initTokenCache+ , initTokenCacheLayout+ , mkTreeFromTokens+ , mkTreeFromSpanTokens++ -- * Operations at 'TokenCache' level+ , Positioning (..)+ , ReversedToks(..)+ , reverseToks+ , unReverseToks+ , reversedToks+ , putToksInCache+ , replaceTokenInCache+ , removeToksFromCache+ , replaceTokenForSrcSpan+ , invariant+ , getSrcSpanFor+ , indentDeclToks++ , addToksAfterSrcSpan+ , addOffsetToSpan+ , reIndentToks++ -- *+ , retrieveTokensInterim+ , getTokensForNoIntros+ , getTokensFor+ , getTokensBefore+ -- , retrieveTokensFinal+ , reAlignMarked++ -- *+ , splitOnNewLn+ , getIndentOffset+ , newLnToken+ , startEndLocIncComments'+ , forestSpanToGhcPos++ -- *+ , nullForestSpan+ , nullForestPos+++ -- * should be in utils+ , simpPosToForestSpan++ -- *+ , showTree+ , showToks++ -- * Exposed for testing only+ , addNewSrcSpanAndToksAfter+ , openZipperToSpan+ , openZipperToSpanAdded+ , retrievePrevLineToks+ , limitPrevToks+ , insertSrcSpan+ , insertLenChangedInSrcSpan+ , insertVersionsInSrcSpan+ , updateTokensForSrcSpan+ , removeSrcSpan+ , containsStart+ , containsMiddle+ , containsEnd+ , splitSubtree+ -- , splitForestOnSpan+ , insertNodeAfter+ , splitSubToks+ , placeToksForSpan+ , reAlignOneLine+ , calcEndGap+ , getTreeSpansAsList+ , openZipperToSpanOrig+ ) where++import Control.Exception+import Data.Bits+import Data.List+import Data.Tree++import Language.Haskell.TokenUtils.Types+import Language.Haskell.TokenUtils.Utils++import qualified Data.Foldable as F+import qualified Data.Map as Map+import qualified Data.Tree.Zipper as Z++-- ---------------------------------------------------------------------++-- |Keep track of when tokens are reversed, to avoid confusion+data ReversedToks a = RT [a]+ deriving (Show)++reverseToks :: (IsToken a) => [a] -> ReversedToks a+reverseToks toks = RT $ reverse toks++unReverseToks :: (IsToken a) => ReversedToks a -> [a]+unReverseToks (RT toks) = reverse toks++reversedToks :: (IsToken a) => ReversedToks a -> [a]+reversedToks (RT toks) = toks+++-- |How new SrcSpans should be inserted in the Token tree, relative to+-- the prior span+data Positioning = PlaceAdjacent -- ^Only a single space between the+ -- end of the prior span and the new one+ | PlaceAbsolute !Int !Int -- ^Start at the specified+ -- line and col+ | PlaceAbsCol !Int !Int !Int -- ^Line offset and+ -- absolute Col. Mainly+ -- for forcing start at+ -- left margin, number+ -- of lines to add at+ -- the end+ | PlaceOffset !Int !Int !Int -- ^Line and Col offset for+ -- start, num lines to add at the end+ -- relative to the indent level of the prior span+ | PlaceIndent !Int !Int !Int -- ^Line and Col offset for+ -- start, num lines to add at the end+ -- relative to the indent level of the prior line+ deriving (Show)++-- ---------------------------------------------------------------------++initTokenCache :: (IsToken a) => [a] -> TokenCache a+initTokenCache toks = TK (Map.fromList [((TId 0),(mkTreeFromTokens toks))]) (TId 0)++initTokenCacheLayout :: (IsToken a) => Tree (Entry a) -> TokenCache a+initTokenCacheLayout tree = TK (Map.fromList [((TId 0),tree)]) (TId 0)++-- ---------------------------------------------------------------------++-- |Make a tree representing a particular set of tokens+mkTreeFromTokens :: (IsToken a) => [a] -> Tree (Entry a)+mkTreeFromTokens [] = Node (Entry nullForestSpan NoChange []) []+mkTreeFromTokens toks = Node (Entry sspan NoChange toks) []+ where+ (startLoc',endLoc') = nonCommentSpan toks+ sspan = if (startLoc',endLoc') == ((0,0),(0,0))+ then error $ "mkTreeFromTokens:null span for:" ++ (show toks)+ else simpPosToForestSpan (startLoc',endLoc')++-- ---------------------------------------------------------------------++-- |Make a tree representing a particular set of tokens+mkTreeFromSpanTokens :: (IsToken a) => ForestSpan -> [a] -> Tree (Entry a)+mkTreeFromSpanTokens sspan toks = Node (Entry sspan NoChange toks) []++-- ---------------------------------------------------------------------++forestSpanStart :: ForestSpan -> ForestPos+forestSpanStart (start,_) = start++forestSpanEnd :: ForestSpan -> ForestPos+forestSpanEnd (_,end) = end++nullForestSpan :: ForestSpan+nullForestSpan = (nullForestPos,nullForestPos)++nullForestPos :: ForestPos+nullForestPos = (ForestLine False 0 0 0,0)++-- ---------------------------------------------------------------------++simpPosToForestSpan :: (SimpPos,SimpPos) -> ForestSpan+simpPosToForestSpan ((sr,sc),(er,ec))+ = ((ghcLineToForestLine sr,sc),(ghcLineToForestLine er,ec))++-- ---------------------------------------------------------------------++-- |Utility function to either return True or throw an error to report the problem+invariantOk :: (IsToken a) => Tree (Entry a) -> Bool+invariantOk forest = ok+ where+ inv = invariant forest+ ok = case inv of+ [] -> True+ _ -> error $ "Token Tree invariant fails:" ++ (intercalate "\n" inv)++-- ---------------------------------------------------------------------+-- |Check the invariant for the token cache. Returns list of any errors found.+-- Invariants:+-- 1. For each tree, either the rootLabel has a SrcSpan only, or the subForest /= [].+-- 2a. The trees making up the subForest of a given node fully include the parent SrcSpan.+-- i.e. the leaves contain all the tokens for a given SrcSpan.+-- 2b. The subForest is in SrcSpan order+-- 3. A given SrcSpan can only appear (or be included) in a single tree of the forest.+-- 4. The parent link for all sub-trees does exist, and actually points to the parent. +-- 5. There are no nullForestSpan entries in the tree+-- NOTE: the tokens may extend before or after the SrcSpan, due to comments only+-- NOTE2: this will have to be revisited when edits to the tokens are made+invariant :: (IsToken a) => Tree (Entry a) -> [String]+invariant forest = rsub+ where+ rsub = F.foldl checkOneTree [] [forest]++ checkOneTree :: (IsToken a) => [String] -> Tree (Entry a) -> [String]+ checkOneTree acc tree = acc ++ r+ where+ r = checkNode [] tree++ checkNode :: (IsToken a) => [String] -> Tree (Entry a) -> [String]+ checkNode _acc (Node (Deleted _sspan _ _) []) = []+ checkNode _acc node@(Node (Deleted _sspan _ _) _sub)+ = ["FAIL: deleted node with subtree: " ++ (prettyshow node)]+ checkNode acc node@(Node (Entry sspan _lay toks) sub) = acc ++ r ++ rinc ++ rsubs ++ rnull+ where+ r = if ( null toks && not (null sub)) ||+ (not (null toks) && null sub)+ then []+ else ["FAIL: exactly one of toks or subforest must be empty: " ++ (prettyshow node)]+ rsubs = foldl' checkNode [] sub++ rinc = checkInclusion node++ rnull = if (sspan == nullForestSpan)+ then ["FAIL: null SrcSpan in tree: " ++ (prettyshow node)]+ else []++ -- |Check invariant 2, assuming 1 ok+ -- NOTE: check that the subtree spans do not go outside the node+ -- span, they do not need to completely fill it, because some may+ -- have been removed during manipulation+ checkInclusion (Node _ []) = []+ checkInclusion (Node (Deleted _ _ _) _) = []+ checkInclusion node@(Node (Entry _sspan _lay _toks) sub) = rs ++ rseq+ where+ (start,end) = treeStartEnd node+ subs = map treeStartEnd sub+ (sstart, _) = ghead "invariant" subs+ (_, send) = last subs+ -- Do not count any custom added srcspans at the end for this+ -- test+ -- TODO: is this a reasonable approach?++ rs = if ((start <= sstart) &&+ ((end >= send) || (forestPosVersionSet send) || (forestPosAstVersionSet send)))+ || (forestPosLenChanged start)+ || (forestPosLenChanged sstart)+ || (forestPosLenChanged send)++ then []+ else ["FAIL: subForest start and end does not match entry: " ++ (prettyshow node)]+ -- else ["FAIL: subForest start and end does not match entry: " ++ (show node)]++ rseq = checkSequence node subs++ checkSequence :: (IsToken a) => Tree (Entry a) -> [ForestSpan] -> [String]+ checkSequence _ [] = []+ checkSequence _ [_x] = []+ checkSequence node' ((_s1,e1):s@(s2,_e2):ss)+ = r ++ checkSequence node' (s:ss)+ where+ -- r = if e1 <= s2+ r = if (before e1 s2) || (sizeChanged e1) {- ++AZ++ -} || (sizeChanged s2)+ then []+ else ["FAIL: subForest not in order: " +++ show e1 ++ " not < " ++ show s2 +++ ":" ++ prettyshow node']++ -- |Compare end of one span with beginning of another+ before (ForestLine _chs _trs ve er,ec) (ForestLine _che _tre vs sr,sc)+ = case (ve /= 0, vs /= 0) of+ (False, False) -> (er,ec) <= (sr,sc) -- e.g. (10,3) <= (11,5)+ (False, True) -> True -- e.g. (10,3) <= (100011,5)+ (True, False) -> True -- e.g. (100010,3) <= (11,5)+ (True, True) -> if vs < ve -- both have version, lowest wins + then False+ else True++ sizeChanged (ForestLine ch _ _ _,_) = ch++{-+ cs ce+ True _ -> True+ False _ -> before++-}++-- ---------------------------------------------------------------------++showTree :: (IsToken a) => Tree (Entry a) -> String+showTree = prettyshow++-- |Represent a tree in a more concise/pretty way+prettyshow :: (IsToken a) => Tree (Entry a) -> String+prettyshow (Node (Deleted sspan _pg eg) _nullSubs)+ = "Node (Deleted " ++ (showForestSpan sspan) ++ " " ++ (show eg) ++ ")"+prettyshow (Node (Entry sspan _lay toks) sub)+ = "Node (Entry " ++ (showForestSpan sspan) ++ " "+ ++ (prettyToks toks) ++ ") "+ -- ++ show (map prettyshow sub)+ ++ "[" ++ intercalate "," (map prettyshow sub) ++ "]"++prettyToks :: (IsToken a) => [a] -> String+prettyToks [] = "[]"+prettyToks toks@[_x] = showToks toks+prettyToks toks@[_t1,_t2] = showToks toks+prettyToks toks = showToks [ghead "prettyToks" toks] ++ ".." ++ showToks [last toks]++showToks :: (IsToken a) => [a] -> String+showToks toks = show $ map doOne toks+ where+ doOne tok = (s,e,tok)+ where (Span s e) = getSpan tok++-- ---------------------------------------------------------------------++getTreeFromCache :: (IsToken a) => Span -> TokenCache a -> Tree (Entry a)+getTreeFromCache sspan tk = (tkCache tk) Map.! tid+ where+ tid = treeIdFromForestSpan $ srcSpanToForestSpan sspan++-- ---------------------------------------------------------------------++replaceTreeInCache :: (IsToken a) => Span -> Tree (Entry a) -> TokenCache a -> TokenCache a+replaceTreeInCache sspan tree tk = tk'+ where+ tid = treeIdFromForestSpan $ srcSpanToForestSpan sspan+ -- tree' = treeIdIntoTree tid tree+ tree' = putTidInTree tid tree+ tk' = tk {tkCache = Map.insert tid tree' (tkCache tk) }++-- ---------------------------------------------------------------------++-- TODO: get rid of one of the following 2, it is a duplicate+putTidInTree :: (IsToken a) => TreeId -> Tree (Entry a) -> Tree (Entry a)+putTidInTree tid (Node (Deleted fspan pg eg) subs) = (Node (Deleted fs' pg eg) subs)+ where fs' = treeIdIntoForestSpan tid fspan+putTidInTree tid (Node (Entry fspan lay toks) subs) = tree'+ where+ subs' = map (putTidInTree tid) subs+ fs' = treeIdIntoForestSpan tid fspan+ tree' = Node (Entry fs' lay toks) subs'++treeIdIntoTree :: (IsToken a) => TreeId -> Tree (Entry a) -> Tree (Entry a)+treeIdIntoTree tid (Node (Entry fspan lay toks) subTree) = tree'+ where+ fs' = treeIdIntoForestSpan tid fspan+ tree' = Node (Entry fs' lay toks) subTree+treeIdIntoTree tid (Node (Deleted fspan pg eg) subTree) = tree'+ where+ fs' = treeIdIntoForestSpan tid fspan+ tree' = Node (Deleted fs' pg eg) subTree++-- ---------------------------------------------------------------------++stash :: (IsToken a) => TokenCache a -> Tree (Entry a) -> TokenCache a+stash tk oldTree = tk'+ where+ (TId lastTreeId) = tkLastTreeId tk+ lastTreeId' = TId (lastTreeId + 1)+ oldTree' = treeIdIntoTree lastTreeId' oldTree+ cache' = Map.insert lastTreeId' oldTree' (tkCache tk)+ tk' = tk {tkLastTreeId = lastTreeId', tkCache = cache' }++-- ---------------------------------------------------------------------++-- |Replace a single token in a token tree, without changing the+-- structure of the tree+-- NOTE: the GHC.SrcSpan may have been used to select the appropriate+-- forest in the first place, and is required to select the correct+-- span in the tree, due to the ForestLine annotations that may be present++-- TODO: work at the token level, not the sspan level+-- TODO: Use start of token span only, with length 1.+replaceTokenForSrcSpan :: (IsToken a) => Tree (Entry a) -> Span -> a -> Tree (Entry a)+replaceTokenForSrcSpan forest sspan tok = forest'+ where+ -- (GHC.L tl _,_) = tok+ tl = getSpan tok+ -- First open to the sspan, making use of any Forestline annotations+ z = openZipperToSpanDeep (srcSpanToForestSpan sspan) $ Z.fromTree forest++ -- Then drill down to the specific subtree containing the token+ -- z' = openZipperToSpan (srcSpanToForestSpan tl) z+ z' = z -- No, pass in original token span as sspan.++ -- Note: with LayoutTree, the full tree matching the AST has been+ -- built, still need to drill down to the nearest enclosing span+ (tspan,lay,toks) = case Z.tree z' of+ (Node (Entry ss ly tks) []) -> (ss,ly,tks)+ (Node (Entry _ _ _nullToks) _sub) -> error $ "replaceTokenForSrcSpan:tok pos" ++ (showForestSpan $ sf sspan) ++ " expecting tokens, found: " ++ (show $ Z.tree z')+ (Node (Deleted _ _ _) _sub) -> error $ "replaceTokenForSrcSpan:tok pos" ++ (showForestSpan $ sf sspan) ++ " expecting Entry, found: " ++ (show $ Z.tree z')++ ((row,col),_) = forestSpanToSimpPos $ srcSpanToForestSpan tl+ toks' = replaceTokNoReAlign toks (row,col) tok++ zf = Z.setTree (Node (Entry tspan lay toks') []) z'+ forest' = Z.toTree zf++-- ---------------------------------------------------------------------++replaceTokenInCache :: (IsToken a) => TokenCache a -> Span -> a -> TokenCache a+replaceTokenInCache tk sspan tok = tk'+ where+ forest = getTreeFromCache sspan tk+ forest' = replaceTokenForSrcSpan forest sspan tok+ tk' = replaceTreeInCache sspan forest' tk++-- ---------------------------------------------------------------------++putToksInCache :: (IsToken a) => TokenCache a -> Span -> [a] -> (TokenCache a,Span)+putToksInCache tk sspan toks = (tk'',newSpan)+ where+ forest = getTreeFromCache sspan tk+ (forest',newSpan,oldTree) = updateTokensForSrcSpan forest sspan toks+ tk' = replaceTreeInCache sspan forest' tk+ tk'' = stash tk' oldTree++-- ---------------------------------------------------------------------++removeToksFromCache :: (IsToken a) => TokenCache a -> Span -> TokenCache a+removeToksFromCache tk sspan = tk''+ where+ forest = getTreeFromCache sspan tk+ (forest',oldTree) = removeSrcSpan forest (srcSpanToForestSpan sspan)+ tk' = replaceTreeInCache sspan forest' tk+ tk'' = stash tk' oldTree++-- ---------------------------------------------------------------------++-- | Removes a ForestSpan and its tokens from the forest.+removeSrcSpan :: (IsToken a) => Tree (Entry a) -> ForestSpan+ -> (Tree (Entry a),Tree (Entry a)) -- ^Updated forest, removed span+removeSrcSpan forest sspan = (forest'', delTree)+ where+ forest' = insertSrcSpan forest sspan -- Make sure span is actually+ -- in the tree+ z = openZipperToSpan sspan $ Z.fromTree forest'+ zp = gfromJust "removeSrcSpan" $ Z.parent z++ ((pg,_),eg) = calcPriorAndEndGap forest' sspan++ pt = Z.tree zp++ subTree = map (\t -> if (treeStartEnd t == sspan) then (Node (Deleted sspan pg eg) []) else t) $ subForest pt++ z' = Z.setTree (pt { subForest = subTree}) zp+ forest'' = Z.toTree z'++ delTree = Z.tree z+ -- forest'' = error $ "removeSrcSpan: forest'=" ++ drawTreeCompact forest'++-- ---------------------------------------------------------------------++-- |Add a new SrcSpan and Tokens after a given one in the token stream+-- and forest. This will be given a unique SrcSpan in return, which+-- specifically indexes into the forest.+addNewSrcSpanAndToksAfter :: (IsToken a)+ => Tree (Entry a) -- ^The forest to update+ -> Span -- ^The new span comes after this one+ -> Span -- ^Existing span for the tokens+ -> Positioning+ -> [a] -- ^The new tokens belonging to the new SrcSpan+ -> (Tree (Entry a) -- Updated forest with the new span+ , Span) -- ^Unique SrcSpan allocated in the forest to+ -- identify this span in its position+addNewSrcSpanAndToksAfter forest oldSpan newSpan pos toks = (forest'',newSpan')+ where+ (forest',tree) = getSrcSpanForDeep forest (srcSpanToForestSpan oldSpan)++ (ghcl,_c) = getStartLoc newSpan+ (ForestLine ch tr v l) = ghcLineToForestLine ghcl+ newSpan' = insertForestLineInSpan (ForestLine ch tr (v+1) l) newSpan++ toks' = placeToksForSpan forest' oldSpan tree pos toks++ newNode = Node (Entry (srcSpanToForestSpan newSpan') NoChange toks') []++ forest'' = insertNodeAfter tree newNode forest'++-- ---------------------------------------------------------------------++placeToksForSpan :: (IsToken a)+ => Tree (Entry a)+ -> Span+ -> Tree (Entry a)+ -> Positioning+ -> [a]+ -> [a]+placeToksForSpan forest oldSpan tree pos toks = toks'+ where+ z = openZipperToSpanDeep (srcSpanToForestSpan oldSpan) $ Z.fromTree forest+ prevToks = case (retrievePrevLineToks z) of+ RT [] -> reverseToks $ retrieveTokensInterim tree+ xs -> xs++ prevToks' = limitPrevToks prevToks oldSpan+ toks' = reIndentToks pos (unReverseToks prevToks') toks+ -- toks' = error $ "placeToksForSpan: prevToks'=" ++ (show prevToks')+ -- toks' = error $ "placeToksForSpan: prevToks=" ++ (show prevToks)++-- ---------------------------------------------------------------------++limitPrevToks :: (IsToken a) => ReversedToks a -> Span -> ReversedToks a+limitPrevToks prevToks sspan = reverseToks prevToks''+ where+ ((ForestLine _ _ _ startRow,_startCol),(ForestLine _ _ _ endRow,_)) = srcSpanToForestSpan sspan++ -- Make sure the toks do not extend past where we are+ prevToks' = dropWhile (\t -> tokenRow t > endRow) $ unReverseToks prevToks++ -- Only use the toks for the given oldspan+ -- prevToks'' = dropWhile (\t -> tokenPos t < (startRow,startCol)) prevToks'+ prevToks'' = dropWhile (\t -> tokenRow t < startRow) prevToks'++-- ---------------------------------------------------------------------++-- |Add new tokens after the given SrcSpan, constructing a new SrcSpan+-- in the process+addToksAfterSrcSpan :: (IsToken a)+ => Tree (Entry a) -- ^TokenTree to be modified+ -> Span -- ^Preceding location for new tokens+ -> Positioning+ -> [a] -- ^New tokens to be added+ -> (Tree (Entry a), Span) -- ^ updated TokenTree and SrcSpan location for+ -- the new tokens in the TokenTree+addToksAfterSrcSpan forest oldSpan pos toks = (forest',newSpan')+ where+ (fwithspan,tree) = getSrcSpanForDeep forest (srcSpanToForestSpan oldSpan)++ toks'' = placeToksForSpan fwithspan oldSpan tree pos toks++ (startPos,endPos) = nonCommentSpan toks''++ newSpan = posToSpan (startPos,endPos)++ (forest',newSpan') = addNewSrcSpanAndToksAfter forest oldSpan newSpan pos toks++-- ---------------------------------------------------------------------++-- |For a span about to be deleted, calculate the gap between the end+-- of the span being deleted and the start of the next one, at a token+-- level.+calcPriorAndEndGap :: (IsToken a) => Tree (Entry a) -> ForestSpan -> (SimpPos,SimpPos)+calcPriorAndEndGap tree sspan = (pg,eg)+ where+ ((spanStartRow,spanStartCol),(spanRow,spanCol)) = forestSpanToSimpPos sspan+ (spanStart,spanEnd) = sspan+ entries = retrieveTokens' tree+ -- NOTE: the entries are the fringe of the tree, the sspan in+ -- question may be represented by several entries+ (before,rest) = span (\e -> (forestSpanStart $ forestSpanFromEntry e) < spanStart) entries+ (rafter,rmiddle) = break (\e -> (forestSpanEnd $ forestSpanFromEntry e) <= spanEnd) $ reverse rest+ _middle = reverse rmiddle+ after = reverse rafter+ -- last element of before should be the sspan we care about, first+ -- of after is the one we are looking for.++ -- NOTE: `after` may contain zero or more Deleted segments in the+ -- front. These get merged later in mergeDeletes+ (tokRow,tokCol) = if null after+ then (spanRow + 2,spanCol)+ else (r,c)+ where+ (r,c) = case ghead ("calcEndGap:after="++(show after)) after of+ (Entry _ _ toks) -> (tokenRow t,tokenCol t)+ where t = ghead "calcEndGap" toks+ (Deleted ss _ _) -> fst $ forestSpanToSimpPos ss++ eg = (tokRow - spanRow, tokCol - spanCol)+ -- eg = error $ "calcEndGap: (sspan,(before,middle,after))=" ++ (show (sspan,(_before,middle,after)))++ (tokRowPg,tokColPg) = if null before+ then (spanStartRow - 1,spanStartCol)+ else (r,c)+ where+ (r,c) = case glast ("calcEndGap:before="++(show before)) before of+ (Entry _ _ toks) -> (tokenRow t,tokenCol t)+ where t = glast "calcEndGap pg" toks+ (Deleted ss _ _) -> snd $ forestSpanToSimpPos ss++ -- TODO: what about comments before the span? spanStartRow may be off+ pg = (spanStartRow - tokRowPg, spanStartCol - tokColPg)+++-- ---------------------------------------------------------------------++-- TODO: delete this, superseded by calcPriorAndEndGap+-- |For a span about to be deleted, calculate the gap between the end+-- of the span being deleted and the start of the next one, at a token+-- level.+calcEndGap :: (IsToken a) => Tree (Entry a) -> ForestSpan -> SimpPos+calcEndGap tree sspan = gap+ where+ (_sspanStart,(spanRow,spanCol)) = forestSpanToSimpPos sspan+ (spanStart,spanEnd) = sspan+ entries = retrieveTokens' tree+ -- NOTE: the entries are the fringe of the tree, the sspan in+ -- question may be represented by several entries+ (_before,rest) = span (\e -> (forestSpanStart $ forestSpanFromEntry e) < spanStart) entries+ (rafter,rmiddle) = break (\e -> (forestSpanEnd $ forestSpanFromEntry e) <= spanEnd) $ reverse rest+ _middle = reverse rmiddle+ after = reverse rafter+ -- last element of before should be the sspan we care about, first+ -- of after is the one we are looking for.++ -- NOTE: `after` may contain zero or more Deleted segments in the+ -- front. These get merged later in mergeDeletes+ (tokRow,tokCol) = if null after+ then (spanRow + 2,spanCol)+ else (r,c)+ where+ (r,c) = case ghead ("calcEndGap:after="++(show after)) after of+ (Entry _ _ toks) -> (tokenRow t,tokenCol t)+ where t = ghead "calcEndGap" toks+ (Deleted ss _ _) -> fst $ forestSpanToSimpPos ss++ gap = (tokRow - spanRow, tokCol - spanCol)+ -- gap = error $ "calcEndGap: (sspan,(before,middle,after))=" ++ (show (sspan,(_before,middle,after)))++-- ---------------------------------------------------------------------++-- |Replace the tokens for a given SrcSpan with new ones. The SrcSpan+-- will be inserted into the tree if it is not already there.+-- If the SrcSpan changes size, replace the SrcSpan with a new one+-- (marked), and return it, as well as the old one+-- TODO: What about trailing comments? Preserve or replace?+updateTokensForSrcSpan :: (IsToken a)+ => Tree (Entry a) -> Span -> [a] -> (Tree (Entry a),Span,Tree (Entry a))+updateTokensForSrcSpan forest sspan toks = (forest'',newSpan,oldTree)+ where+ (forest',tree@(Node (Entry _s _ _) _)) = getSrcSpanFor forest (srcSpanToForestSpan sspan)+ prevToks = retrieveTokensInterim tree++ endComments = reverse $ takeWhile isWhiteSpaceOrIgnored $ reverse toks+ startComments = takeWhile isWhiteSpaceOrIgnored $ toks++ newTokStart = if (null prevToks)+ then mkZeroToken+ else ghead "updateTokensForSrcSpan.1" prevToks++ toks'' = if (not (null startComments) || not (null endComments))+ then -- toks have comments, discard originals+ reIndentToks (PlaceAbsolute (tokenRow newTokStart) (tokenCol newTokStart)) prevToks toks+ else -- Must reuse any pre-existing start or end comments, and+ -- resync the tokens across all three.+ let+ origEndComments = reverse $ takeWhile isWhiteSpaceOrIgnored $ reverse prevToks+ origStartComments = takeWhile isWhiteSpaceOrIgnored $ prevToks++ ((startRow,startCol),_) = forestSpanToGhcPos $ srcSpanToForestSpan sspan+ core = reIndentToks (PlaceAbsolute startRow startCol) prevToks toks+ trail = if (null origEndComments)+ then []+ else addOffsetToToks (lineOffset,colOffset) origEndComments+ where+ lineOffset = 0 -- tokenRow (head origEndComments) - tokenRow (head origEndComments)+ colOffset = 0 -- tokenCol (head origEndComments)++ toks' = origStartComments ++ core ++ trail+ in toks'++ (startPos,endPos) = nonCommentSpan toks''++ -- if the original sspan had a ForestLine version, preserve it+ (((ForestLine _chs _trs vs _),_),(ForestLine _che _tre ve _,_)) = srcSpanToForestSpan sspan+ -- Note: adding one to end version, so invariant won't fail+ -- newSpan = insertVersionsInSrcSpan vs ve $ posToSrcSpan forest (startPos,endPos) + newSpan = insertLenChangedInSrcSpan True True+ $ insertVersionsInSrcSpan vs ve $ posToSpan (startPos,endPos)++ zf = openZipperToNode tree $ Z.fromTree forest'++ zf' = Z.setTree (Node (Entry (srcSpanToForestSpan newSpan) NoChange toks'') []) zf+ forest'' = Z.toTree zf'++ oldTree = tree++-- ---------------------------------------------------------------------++-- |Get the tokens preceding a given 'SrcSpan'+getTokensBefore :: (IsToken a) => Tree (Entry a) -> Span -> (Tree (Entry a),ReversedToks a)+getTokensBefore forest sspan = (forest', prevToks')+ where+ (forest',tree@(Node (Entry _s _ _) _)) = getSrcSpanFor forest (srcSpanToForestSpan sspan)++ z = openZipperToSpan (srcSpanToForestSpan sspan) $ Z.fromTree forest'++ prevToks = case (retrievePrevLineToks z) of+ RT [] -> reverseToks $ retrieveTokensInterim tree+ xs -> xs++ (_,rtoks) = break (\t->tokenPos t < (getStartLoc sspan)) $ reversedToks prevToks+ prevToks' = RT rtoks++getStartLoc :: Span -> SimpPos+getStartLoc (Span s _) = s++-- ---------------------------------------------------------------------++-- |Get the (possible cached) tokens for a given source span, and+-- cache their being fetched.+-- NOTE: The SrcSpan may be one introduced by HaRe, rather than GHC.+getTokensFor :: (IsToken a) => Bool -> Tree (Entry a) -> Span -> (Tree (Entry a),[a])+getTokensFor checkInvariant forest sspan = (forest'', tokens)+ where+ forest' = if (not checkInvariant) || invariantOk forest+ -- short circuit eval+ then forest+ else error $ "getTokensFor:invariant failed:" ++ (show $ invariant forest)+ (forest'',tree) = getSrcSpanFor forest' (srcSpanToForestSpan sspan)++ tokens = retrieveTokensInterim tree++-- ---------------------------------------------------------------------++-- |Get the (possible cached) tokens for a given source span, and+-- cache their being fetched.+-- NOTE: The SrcSpan may be one introduced by HaRe, rather than GHC.+getTokensForNoIntros :: (IsToken a) => Bool -> Tree (Entry a) -> Span -> (Tree (Entry a),[a])+getTokensForNoIntros checkInvariant forest sspan = (forest', tokens')+ where+ (forest',tokens) = getTokensFor checkInvariant forest sspan+ -- (lead,rest) = break (not . isWhiteSpaceOrIgnored) tokens+ (lead,rest) = break (not . isIgnoredNonComment) tokens+ tokens' = (filter (not . isIgnored) lead) ++ rest++-- ---------------------------------------------------------------------++-- |Starting from a point in the zipper, retrieve all tokens backwards+-- until the line changes for a non-comment/non-empty token or+-- beginning of file.+retrievePrevLineToks :: (IsToken a) => Z.TreePos Z.Full (Entry a) -> ReversedToks a+retrievePrevLineToks z = RT res' -- error $ "retrievePrevLineToks:done notWhite=" ++ (show (done notWhite)) -- ++AZ+++ where+ -- Assuming the zipper has been opened to the span we care about,+ -- we will start with the tokens in the current tree, and work+ -- back.+ -- prevToks = retrieveTokens $ Z.tree z+ prevToks = retrieveTokensInterim $ Z.tree z++ -- Next one is the usual one+ -- res' = reverse $ (concat (go z)) ++ prevToks+ res' = reverse $ concat $ reverse (prevToks : (go z))++ -- TODO: ++AZ++ what is this actually doing?+ go :: (IsToken a) => Z.TreePos Z.Full (Entry a) -> [[a]]+ go zz+ | not (Z.isRoot zz) = toks : (go $ gfromJust "retrievePrevLineToks" (Z.parent zz))+ | otherwise = [toks]+ where+ toks = concat $ reverse $ map retrieveTokensInterim $ Z.before zz+ -- toks = concat $ map retrieveTokensInterim $ Z.before zz+++-- ---------------------------------------------------------------------++posToSpan :: (SimpPos,SimpPos) -> Span+posToSpan (s,e) = Span s e++-- ---------------------------------------------------------------------++reIndentToks :: (IsToken a) => Positioning -> [a] -> [a] -> [a]+reIndentToks _ _ [] = []+reIndentToks pos prevToks toks = toks''+ where+ newTokStart = ghead "reIndentToks.1"+ $ dropWhile (\tok -> isComment tok || isEmpty tok) $ toks++ firstTok = ghead "reIndentToks.2" toks+ lastTok = glast "reIndentToks.1" prevToks++ lastNonCommentTok = ghead "reIndentToks.3"+ $ dropWhile (\tok -> isComment tok || isEmpty tok) $ reverse prevToks++ prevOffset = getIndentOffset prevToks (tokenPos (glast "reIndentToks.2" prevToks))++ (lastTokEndLine,_) = tokenPosEnd lastTok+++ (lineOffset,colOffset,endNewlines) = case pos of+ PlaceAdjacent -> (lineOffset',colOffset',0)+ where+ colStart = (tokenColEnd (lastTok)) + 1+ lineStart = (tokenRow (lastTok))++ lineOffset' = lineStart - (tokenRow firstTok)+ colOffset' = colStart - (tokenCol newTokStart)++ PlaceAbsolute row col -> (lineOffset', colOffset', 0)+ where+ lineOffset' = row - (tokenRow firstTok)+ colOffset' = col - (tokenCol firstTok)++ PlaceAbsCol rowIndent col numLines -> (lineOffset', colOffset', numLines)+ where+ colOffset' = col - (tokenCol firstTok)+ lineStart = (tokenRow (lastTok)) -- + 1++ lineOffset' = rowIndent + lineStart - (tokenRow firstTok)++ PlaceOffset rowIndent colIndent numLines -> (lineOffset',colOffset',numLines)+ where+ -- TODO: Should this not be prevOffset?+ colStart = tokenCol $ ghead "reIndentToks.4"+ $ dropWhile isWhiteSpaceOrIgnored prevToks++ lineStart = (tokenRow (lastTok)) -- + 1++ lineOffset' = rowIndent + lineStart - (tokenRow firstTok)+ colOffset' = colIndent + colStart - (tokenCol newTokStart)+ -- colOffset' = error $ "reIndentToks:placeOffset lineOffset=" ++ show lineOffset++ PlaceIndent rowIndent colIndent numLines -> (lineOffset',colOffset',numLines)+ where+ colStart = prevOffset+ lineStart = if ((isComment lastTok) && (tokenRow lastNonCommentTok /= lastTokEndLine))+ then (tokenRow (lastTok)) + 1+ else (tokenRow (lastTok))++ lineOffset' = rowIndent + lineStart - (tokenRow firstTok)+ colOffset' = colIndent + colStart - (tokenCol newTokStart) + 1 -- ++AZ++ Why +1?++ toks' = addOffsetToToks (lineOffset,colOffset) toks+ toks'' = if endNewlines > 0+ then toks' ++ [(newLinesToken (endNewlines - 1) $ glast "reIndentToks.3" toks')]+ else toks'++-- ---------------------------------------------------------------------++-- | indent the tree and tokens by the given offset, and sync the AST+-- to the tree too.+indentDeclToks :: (IsToken a,HasLoc t) --(SYB.Data t)+ => (t -> ForestSpan -> t)+ -> t -- ^The AST (or fragment)+ -> Tree (Entry a) -- ^Existing token tree+ -> Int -- ^ (signed) number of columns to indent/dedent+ -> (t, Tree (Entry a)) -- ^Updated AST and tokens+indentDeclToks syncAST decl forest offset = (decl',forest'')+ where+ sspan = posToSpan (getLoc decl,getLocEnd decl)++ -- make sure the span is in the forest+ (forest',tree) = getSrcSpanFor forest (srcSpanToForestSpan sspan)++ z = openZipperToSpan (srcSpanToForestSpan sspan) $ Z.fromTree forest'++ tree' = go tree+ -- The invariant will fail if we do not propagate this change+ -- upward. But it needs to sync with the AST, which we do not have+ -- the upward version of.+ -- Instead, set the lengthChanged flag, in the parent.++ -- sss = forestSpanFromEntry entry+ -- sss' = insertLenChangedInForestSpan True sss+ -- tree'' = Node (putForestSpanInEntry entry sss') subs++ markLenChanged (Node entry subs) = (Node entry' subs)+ where+ sss = forestSpanFromEntry entry+ sss' = insertLenChangedInForestSpan True sss+ entry' = putForestSpanInEntry entry sss'++ z' = Z.setTree tree' z+ -- forest'' = Z.toTree (Z.setTree tree'' z)++ forest'' = case Z.parent z' of+ Nothing -> Z.toTree (Z.setTree (markLenChanged $ Z.tree z' ) z' )+ Just z'' -> Z.toTree (Z.setTree (markLenChanged $ Z.tree z'') z'')+++ -- decl' = syncAST decl (addOffsetToSpan off sspan) tree+ decl' = syncAST decl (sf $ addOffsetToSpan off sspan)++ off = (0,offset)++ -- Pretty sure this could be a fold of some kind+ go (Node (Deleted ss pg eg) sub) = (Node (Deleted (addOffsetToForestSpan off ss) pg eg) sub)+ go (Node (Entry ss lay []) sub) = (Node (Entry (addOffsetToForestSpan off ss) lay []) (map go sub))+ go (Node (Entry ss lay toks) []) = (Node (Entry (addOffsetToForestSpan off ss) lay (addOffsetToToks off toks)) [])+ go n = error $ "indentDeclToks:strange node:" ++ (show n)++-- ---------------------------------------------------------------------++addOffsetToSpan :: (Int,Int) -> Span -> Span+addOffsetToSpan (lineOffset,colOffset) sspan = sspan'+ where+ Span (sl,sc) (el,ec) = sspan+ sspan' = Span (sl+lineOffset,sc+colOffset) (el+lineOffset,ec+colOffset)++-- ---------------------------------------------------------------------++-- |Open a zipper so that its focus has the given SrcSpan in its+-- subtree, or the location where the SrcSpan should go, if it is not+-- in the tree.+-- In the case of an 'Above' layout with the same SrcSpan below,+-- return that instead+openZipperToSpanDeep+ :: (IsToken a)+ => ForestSpan+ -> Z.TreePos Z.Full (Entry a)+ -> Z.TreePos Z.Full (Entry a)+openZipperToSpanDeep sspan z = zf+ where+ z' = openZipperToSpan sspan z++ zf = case Z.tree z' of+ (Node (Entry _ (Above _ _ _ _) _) _) ->+ case getChildrenAsZ z' of+ [] -> z'+ [x] -> if (treeStartEnd (Z.tree x) == sspan) then x else z'+ _ -> z'+ _ -> z'+++-- ---------------------------------------------------------------------+-- |Retrieve a path to the tree containing a ForestSpan from the forest,+-- inserting it if not already present+getSrcSpanFor :: (IsToken a) => Tree (Entry a) -> ForestSpan -> (Tree (Entry a), Tree (Entry a))+getSrcSpanFor forest sspan = (forest',tree)+ where+ forest' = insertSrcSpan forest sspan -- Will NO-OP if already+ -- there+ z = openZipperToSpan sspan $ Z.fromTree forest'+ tree = Z.tree z++-- ---------------------------------------------------------------------+-- |Retrieve a path to the tree containing a ForestSpan from the forest,+-- inserting it if not already present.+-- In the case where there is a nested series of spans as in an+-- 'Above' layout, return the deepest one+getSrcSpanForDeep :: (IsToken a)+ => Tree (Entry a) -> ForestSpan -> (Tree (Entry a), Tree (Entry a))+getSrcSpanForDeep forest sspan = (forest',tree)+ where+ forest' = insertSrcSpan forest sspan -- Will NO-OP if already+ -- there+ z = openZipperToSpanDeep sspan $ Z.fromTree forest'++ tree = Z.tree z++-- ---------------------------------------------------------------------+-- |Insert a ForestSpan into the forest, if it is not there already.+-- Assumes the forest was populated with the tokens containing the+-- ForestSpan already+insertSrcSpan :: (IsToken a) => Tree (Entry a) -> ForestSpan -> Tree (Entry a)+insertSrcSpan forest sspan = forest'+ where+ z = openZipperToSpan sspan $ Z.fromTree forest+ forest' = if treeStartEnd (Z.tree z) == sspan+ then forest -- Already in, exactly+ -- else error $ "insertSrcSpan:span not in tree " ++ show sspan++ else if (Z.isLeaf z)+ then -- TODO: This should be in splitSubToks+ let+ -- If we are at a leaf, retrieve the toks+ (Entry _ _ toks) = Z.label z++ (tokStartPos,tokEndPos) = forestSpanToSimpPos sspan++ -- Tokens here, must introduce sub-spans with split, taking+ -- cognizance of start and end comments+ -- TODO: does startEndLocIncComments' give the same boundary+ -- if approached from one side as the other?+ (startLoc,endLoc) = startEndLocIncComments' toks (tokStartPos,tokEndPos)++ (startToks,middleToks,endToks) = splitToks (startLoc,endLoc) toks++ tree1 = if (nonCommentSpan startToks == ((0,0),(0,0)))+ then []+ else [mkTreeFromTokens startToks]+ tree2 = [mkTreeFromSpanTokens sspan middleToks]++ tree3 = if (nonCommentSpan endToks == ((0,0),(0,0)))+ then []+ else [mkTreeFromTokens endToks]++ subTree = tree1 ++ tree2 ++ tree3+ subTree' = filter (\t -> treeStartEnd t /= nullForestSpan) subTree+ -- (Entry sspan2 _ _) = Z.label z+ sspan2 = case Z.label z of+ (Entry ss _ _) -> ss+ (Deleted ss _ _) -> ss+++ -- z' = Z.setTree (Node (Entry sspan2 NoChange []) subTree') z+ z' = case Z.label z of+ (Entry _ _ _) -> Z.setTree (Node (Entry sspan2 NoChange []) subTree') z+ (Deleted _ _ _) -> Z.setTree (Node (Entry sspan2 NoChange []) subTreeD) z+ where+ (tb,tm,te) = splitSubToks (Z.tree z) sspan+ subTreeD = tb ++ tm ++ te++ forest'' = Z.toTree z'+ in forest''+ else+ let+ (before,middle,end) = doSplitTree (Z.tree z) sspan+ newTree = case middle of+ [x] -> x+ _xs -> (Node (Entry sspan NoChange []) middle)+ subTree' = before ++ [newTree] ++ end+ (Entry sspan2 _ _) = Z.label z++ z' = Z.setTree (Node (Entry sspan2 NoChange []) subTree') z+ forest'' = Z.toTree z'+ in+ forest''+++-- ---------------------------------------------------------------------++doSplitTree :: (IsToken a)+ => Tree (Entry a) -> ForestSpan+ -> ([Tree (Entry a)], [Tree (Entry a)], [Tree (Entry a)])+doSplitTree tree@(Node (Deleted _ss _ _) []) sspan = splitSubToks tree sspan -- ++AZ+ What is correct?+doSplitTree tree@(Node (Entry _ss _ _toks) []) sspan = splitSubToks tree sspan+doSplitTree tree sspan = (b'',m'',e'')+ -- error $ "doSplitTree:(sspan,tree,(b1,m1,e1))=" ++ (show (sspan,tree,(b1,m1,e1)))+ where+ (b1,m1,e1) = splitSubtree tree sspan+ (b,m,e) = case m1 of+ [] -> -- NOTE: This may have happened through a span being+ -- deleted from the tree+ -- Hence, correct solution is to kick it up a level and+ -- rebuild using tokens etc+ error $ "doSplitTree:no middle:(tree,sspan,b1,m1,e1)=" ++ (show (tree,sspan,b1,m1,e1))+ [x] -> -- only one tree+ doSplitTree x sspan++ xx -> -- more than one tree+ (b',m',e')+ where+ (bb,mb,_eb) = case (doSplitTree (ghead "doSplitTree.2" xx) sspan) of+ (x,y,[]) -> (x,y,[])+ xxx -> error $ "doSplitTree:eb populated:" ++ (show (sspan,tree,xxx))+++ ( [],me,ee) = doSplitTree (glast "doSplitTree.2" xx) sspan++ mm = tail $ init xx -- xx = (head xx) ++ mm ++ (last xx)++ b' = bb+ m' = mb ++ mm ++ me+ e' = ee+ (b'',m'',e'') = (b1++b,m,e++e1)+++-- ---------------------------------------------------------------------++-- TODO: The Bool is horrible+mkTreeListFromTokens :: (IsToken a) => [a] -> ForestSpan -> Bool -> [Tree (Entry a)]+mkTreeListFromTokens [] _sspan _ = []+mkTreeListFromTokens toks sspan useOriginalSpan = res+ where+ (Node (Entry tspan NoChange treeToks) sub) = mkTreeFromTokens toks++ ((ForestLine chs ts vs _, _),(ForestLine che te ve _, _)) = sspan+ ((ForestLine _ _ _ ls,cs),(ForestLine _ _ _ le,ce)) = tspan++ span' = ((ForestLine chs ts vs ls, cs),(ForestLine che te ve le, ce))++ res = if nonCommentSpan toks == ((0,0),(0,0))+ then []+ else if useOriginalSpan+ then [(Node (Entry sspan NoChange treeToks) sub)]+ else [(Node (Entry span' NoChange treeToks) sub)]++-- ---------------------------------------------------------------------++splitSubToks :: (IsToken a)+ => Tree (Entry a)+ -> (ForestPos, ForestPos)+ -> ([Tree (Entry a)], [Tree (Entry a)], [Tree (Entry a)])+splitSubToks n@(Node (Deleted (treeStart,treeEnd) _pg _eg) []) (sspanStart,sspanEnd) = (b',m',e')+ where+ egs = (0,0) -- TODO: calculate this+ ege = (0,0) -- TODO: calculate this+ pg = 0 -- TODO: calculate this+ b' = if sspanStart > treeStart+ then [Node (Deleted (treeStart,treeStart) pg egs) []]+ else []+ m' = [n]+ e' = if treeEnd > sspanEnd+ then [Node (Deleted (sspanEnd,treeEnd) pg ege) []]+ else []++splitSubToks tree sspan = (b',m',e')+ -- error $ "splitSubToks:(sspan,tree)=" ++ (show (sspan,tree))+ where+ (Node (Entry ss@(treeStart,treeEnd) _lay toks) []) = tree+ (sspanStart,sspanEnd) = sspan+ -- TODO: ignoring comment boundaries to start++ -- There are three possibilities+ -- 1. The span starts only in these tokens+ -- 2. The span starts and ends in these tokens+ -- 3. The span ends only in these tokens+ (b',m',e') = case (containsStart ss sspan,containsEnd ss sspan) of+ (True, False) -> (b'',m'',e'') -- Start only+ -- error $ "splitSubToks:StartOnly:(sspan,tree,(b'',m''))=" ++ (show (sspan,tree,(b'',m'')))+ where+ (_,toksb,toksm) = splitToks (forestSpanToSimpPos (nullForestPos,sspanStart)) toks+-- b'' = if (emptyList toksb) then [] else [Node (Entry (treeStart, sspanEnd) toksb) []]+ b'' = if (null toksb || nonCommentSpan toksb == ((0,0),(0,0)))+ then []+ else [mkTreeFromTokens toksb] -- Need to get end from actual toks+ m'' = let+ (ForestLine _ch _ts _v le,ce) = sspanEnd+ tl =+ if (treeStart == sspanStart) -- Eq does not compare all flags+ then mkTreeListFromTokens toksm (treeStart, treeEnd) False+ else mkTreeListFromTokens toksm (sspanStart,treeEnd) False+ _tl' = if null tl+ then []+ else [Node (Entry (st,(ForestLine ch ts v le,ce)) lay tk) []]+ where [Node (Entry (st,(ForestLine ch ts v _l,_c)) lay tk) []] = tl+ in+ -- tl'+ tl+ e'' = []++ (True, True) -> (b'',m'',e'') -- Start and End+ where+ (toksb,toksm,tokse) = splitToks (forestSpanToSimpPos (sspanStart,sspanEnd)) toks+ b'' = mkTreeListFromTokens toksb (treeStart, sspanStart) False+ m'' = mkTreeListFromTokens toksm (sspanStart, sspanEnd) True+ e'' = mkTreeListFromTokens tokse (sspanEnd, treeEnd) False++ (False,True) -> (b'',m'',e'') -- End only+ where+ (_,toksm,tokse) = splitToks (forestSpanToSimpPos (nullForestPos,sspanEnd)) toks+ b'' = []+ m'' = let -- If the last span is changed, make sure it stays+ -- as it was+ tl = mkTreeListFromTokens toksm (treeStart,sspanEnd) False+ tl' = if null tl+ then []+ else [Node (Entry (st,sspanEnd) lay tk) []]+ where [Node (Entry (st,_en) lay tk) []] = mkTreeListFromTokens toksm (treeStart,sspanEnd) False+ in+ tl'+ e'' = mkTreeListFromTokens tokse (sspanEnd,treeEnd) False++ (False,False) -> if (containsMiddle ss sspan)+ then ([],[tree],[])+ else error $ "splitSubToks: error (ss,sspan)=" ++ (show (ss,sspan))++-- ---------------------------------------------------------------------++-- |True if the start of the second param lies in the span of the first+containsStart :: ForestSpan -> ForestSpan -> Bool+containsStart (nodeStart,nodeEnd) (startPos,_endPos)+ = (startPos >= nodeStart && startPos <= nodeEnd)++-- |True if the start of the second param lies before the first, and+-- ends after or on the second+containsMiddle :: ForestSpan -> ForestSpan -> Bool+containsMiddle (nodeStart,nodeEnd) (startPos,endPos)+ = (startPos <= nodeStart) && (endPos >= nodeEnd)++-- |True if the end of the second param lies in the span of the first+containsEnd :: ForestSpan -> ForestSpan -> Bool+containsEnd (nodeStart,nodeEnd) (_startPos,endPos)+ = (endPos >= nodeStart && endPos <= nodeEnd)++-- ---------------------------------------------------------------------++-- |Split a given tree into a possibly empty part that lies before the+-- srcspan, the part that is wholly included in the srcspan and the+-- part the lies outside of it at the end.+splitSubtree :: (IsToken a)+ => Tree (Entry a) -> ForestSpan+ -> ([Tree (Entry a)], [Tree (Entry a)], [Tree (Entry a)])+splitSubtree tree sspan = (before,middle,end)+ -- error $ "splitSubtree:(sspan,tree,middle',end')=" ++ (show (sspan,tree,middle',end'))+ where+ containsStart' t = containsStart (treeStartEnd t) sspan+ containsMiddle' t = containsMiddle (treeStartEnd t) sspan+ containsEnd' t = containsEnd (treeStartEnd t) sspan++ cond t = containsStart' t || containsMiddle' t || containsEnd' t++ (Node _entry children) = tree+ (before,rest) = break (\x -> cond x) children+ (endr,middler) = break (\x -> cond x) $ reverse rest++ (middle,end) = (reverse middler,reverse endr)++-- ---------------------------------------------------------------------++-- |Open a zipper so that its focus has the given SrcSpan in its+-- subtree, or the location where the SrcSpan should go, if it is not+-- in the tree+openZipperToSpan ::+ (IsToken a)+ => ForestSpan+ -> Z.TreePos Z.Full (Entry a)+ -> Z.TreePos Z.Full (Entry a)+openZipperToSpan sspan z+ | hasVersions = openZipperToSpanAdded sspan z+ | otherwise = openZipperToSpanOrig sspan z+ where+ (vs,_ve) = forestSpanVersions sspan+ hasVersions = vs /= 0+++-- ---------------------------------------------------------------------++-- |Open a zipper so that its focus has the given SrcSpan in its+-- subtree, or the location where the SrcSpan should go, if it is not+-- in the tree+openZipperToSpanOrig ::+ (IsToken a)+ => ForestSpan+ -> Z.TreePos Z.Full (Entry a)+ -> Z.TreePos Z.Full (Entry a)+openZipperToSpanOrig sspan z+ = if (treeStartEnd (Z.tree z) == sspan) || (Z.isLeaf z)+ then z+ else z'+ where+ -- go through all of the children to find the one that+ -- either is what we are looking for, or contains it++ -- childrenAsZ = go [] (Z.firstChild z)+ childrenAsZ = getChildrenAsZ z+ z' = case (filter contains childrenAsZ) of+ [] -> z -- Not directly in a subtree, this is as good as+ -- it gets+ [x] -> -- exactly one, drill down+ openZipperToSpan sspan x++ xx -> case (filter (\zt -> (treeStartEnd $ Z.tree zt) == sspan) xx) of + [] -> -- more than one matches, see if we can get+ -- rid of the ones that have been lengthened+ case (filter (not .forestSpanLenChanged . treeStartEnd . Z.tree) xx) of+ [] -> z -- we tried...+ [w] -> openZipperToSpan sspan w+ -- ww -> error $ "openZipperToSpan:can't resolve:(sspan,ww)="++(show (sspan,ww))+ ww -> -- more than one candidate, break+ -- the tie on version match+ case (filter (\zt -> matchVersions sspan zt) ww) of+ [v] -> openZipperToSpan sspan v+ _ -> error $ "openZipperToSpan:can't resolve:(sspan,ww)="++(show (sspan,map (\zt -> treeStartEnd $ Z.tree zt) ww))+ [y] -> openZipperToSpan sspan y+ yy -> -- Multiple, check if we can separate out by+ -- version+ case (filter (\zt -> (fst $ forestSpanVersions $ treeStartEnd $ Z.tree zt) == (fst $ forestSpanVersions sspan)) xx) of+ -- [] -> z+ [] -> error $ "openZipperToSpan:no version match:(sspan,yy)=" ++ (show (sspan,yy)) -- ++AZ+++ [w] -> openZipperToSpan sspan w+ _ww -> error $ "openZipperToSpan:multiple version match:" ++ (show (sspan,yy)) -- ++AZ++++ contains zn = spanContains (treeStartEnd $ Z.tree zn) sspan++ matchVersions span1 z2 = isMatch+ where+ span2 = treeStartEnd $ Z.tree z2+ isMatch = forestSpanVersions span1 == forestSpanVersions span2++-- ---------------------------------------------------------------------++-- |Open a zipper to a SrcSpan that has been added in the tree, and+-- thus does not necessarily fall in the logical hierarchy of the tree+openZipperToSpanAdded ::+ (IsToken a)+ => ForestSpan+ -> Z.TreePos Z.Full (Entry a)+ -> Z.TreePos Z.Full (Entry a)+openZipperToSpanAdded sspan z = zf+ where+ treeAsList = getTreeSpansAsList $ Z.tree z++ -- True if first span contains the second+ myMatch (((ForestLine _ _ vs1 rs1),cs1),((ForestLine _ _ ve1 re1),ce1))+ (((ForestLine _ _ vs2 rs2),cs2),((ForestLine _ _ ve2 re2),ce2))+ = vs1 == vs2 && ve1 == ve2 && ((rs1,cs1) <= (rs2,cs2)) && ((re1,ce1) >= (re2,ce2))+ tl2 = dropWhile (\(_,s) -> not (myMatch s sspan)) $ reverse treeAsList++ fff [] _ = []+ fff acc@((cd,_cs):_) (v,sspan') = if v < cd then (v,sspan'):acc+ else acc++ tl3 = foldl' fff [(head tl2)] tl2+ -- tl3 now contains the chain of ForestSpans to open in order in the zipper++ zf = foldl' (flip openZipperToSpanOrig) z $ map snd tl3++-- ---------------------------------------------------------------------++getTreeSpansAsList :: (IsToken a) => Tree (Entry a) -> [(Int,ForestSpan)]+getTreeSpansAsList = getTreeSpansAsList' 0++getTreeSpansAsList' :: (IsToken a) => Int -> Tree (Entry a) -> [(Int,ForestSpan)]+getTreeSpansAsList' level (Node (Deleted sspan _pg _eg ) _ ) = [(level,sspan)]+getTreeSpansAsList' level (Node (Entry sspan _lay _toks) ts0) = (level,sspan)+ : (concatMap (getTreeSpansAsList' (level + 1)) ts0)+++-- ---------------------------------------------------------------------++-- |Insert a new node after the designated one in the tree+insertNodeAfter :: (IsToken a)+ => Tree (Entry a) -> Tree (Entry a) -> Tree (Entry a) -> Tree (Entry a)+insertNodeAfter oldNode newNode forest = forest'+ where+ zf = openZipperToNodeDeep oldNode $ Z.fromTree forest++ zp = gfromJust ("insertNodeAfter:" ++ (show (oldNode,newNode,forest))) $ Z.parent zf+ tp = Z.tree zp++ -- now go through the children of the parent tree, and find the+ -- right spot for the new node+ (f,s) = break (\t -> treeStartEnd t == treeStartEnd oldNode) $ subForest tp+ (f',s') = (f++[ghead "insertNodeAfter" s],tail s) -- break does not include the found point+ subForest' = f' ++ [newNode] ++ s'++ tp' = tp { subForest = subForest' }+ forest' = Z.toTree $ Z.setTree tp' zp++-- ---------------------------------------------------------------------++-- |Open a zipper so that its focus is the given node+-- NOTE: the node must already be in the tree+openZipperToNode :: (IsToken a)+ => Tree (Entry a)+ -> Z.TreePos Z.Full (Entry a)+ -> Z.TreePos Z.Full (Entry a)+openZipperToNode (Node (Entry sspan _ _) _) z+ = openZipperToSpan sspan z+openZipperToNode (Node (Deleted sspan _ _) _) z+ = openZipperToSpan sspan z++-- |Open a zipper so that its focus is the given node+-- NOTE: the node must already be in the tree+openZipperToNodeDeep :: (IsToken a)+ => Tree (Entry a)+ -> Z.TreePos Z.Full (Entry a)+ -> Z.TreePos Z.Full (Entry a)+openZipperToNodeDeep (Node (Entry sspan _ _) _) z+ = openZipperToSpanDeep sspan z+openZipperToNodeDeep (Node (Deleted sspan _ _) _) z+ = openZipperToSpanDeep sspan z++-- ---------------------------------------------------------------------++getChildrenAsZ :: Z.TreePos Z.Full a -> [Z.TreePos Z.Full a]+getChildrenAsZ z = go [] (Z.firstChild z)+ where+ go acc Nothing = acc+ go acc (Just zz) = go (acc ++ [zz]) (Z.next zz)++-- ---------------------------------------------------------------------++-- |Replace a single token in the token stream by a new token, without+-- adjusting the layout.+-- Note1: does not re-align, else other later replacements may fail.+-- Note2: must keep original end col, to know what the inter-token gap+-- was when re-aligning+replaceTokNoReAlign:: (IsToken a) => [a] -> SimpPos -> a -> [a]+replaceTokNoReAlign toks pos newTok =+ toks1 ++ [newTok'] ++ toksRest+ where+ (toks1,toks2) = break (\t -> tokenPos t >= pos && tokenLen t > 0) toks+ toksRest = if (null toks2) then [] else (gtail "replaceTokNoReAlign" toks2)+ oldTok = if (null toks2) then newTok else (ghead "replaceTokNoReAlign" toks2)+ -- newTok' = markToken $ matchTokenPos oldTok newTok+ newTok' = matchTokenPos oldTok newTok++-- ---------------------------------------------------------------------++-- |Transfer the location information from the first param to the second+matchTokenPos :: (IsToken a) => a -> a -> a+matchTokenPos t1 t2 = putSpan t2 (getSpan t1)++{-+-- |Transfer the location information from the first param to the second+matchTokenPos :: PosToken -> PosToken -> PosToken+matchTokenPos (GHC.L l _,_) (GHC.L _ t,s) = (GHC.L l t,s)+-}++-- ---------------------------------------------------------------------++-- ---------------------------------------------------------------------++-- |Retrieve all the tokens at the leaves of the tree, in order. No+-- adjustments are made to address gaps or re-alignment of the tokens+retrieveTokensInterim :: (IsToken a) => Tree (Entry a) -> [a]+retrieveTokensInterim forest = monotonicLineToks $ stripForestLines {- reAlignMarked -}+ $ concat $ map (\t -> F.foldl accum [] t) [forest]+ where+ accum :: [a] -> (Entry a) -> [a]+ accum acc (Entry _ _ []) = acc+ accum acc (Entry _ _ toks) = acc ++ toks+ accum acc (Deleted _ _ _) = acc+++retrieveTokens' :: (IsToken a) => Tree (Entry a) -> [Entry a]+retrieveTokens' forest = mergeDeletes $ concat $ map (\t -> F.foldl accum [] t) [forest]+ where+ accum :: [Entry a] -> Entry a -> [Entry a]+ accum acc (Entry _ _ []) = acc+ accum acc e@(Entry _ _ _toks) = acc ++ [e]+ accum acc e@(Deleted _ _ _) = acc ++ [e]++-- |Merge adjacent Deleted entries+mergeDeletes :: (IsToken a) => [Entry a] -> [Entry a]+mergeDeletes [] = []+mergeDeletes [x] = [x]+mergeDeletes ((Deleted ss1 pg1 (r1,_)):(Deleted ss2 _ (r2,c2)):xs) = (Deleted ss pg1 o):xs+ where+ (start,_) = ss1+ (_, end) = ss2+ ss = (start,end)+ o = (r1+r2,c2)+mergeDeletes (x:xs) = x:mergeDeletes xs++-- ---------------------------------------------------------------------++-- | sort out line numbering so that they are always monotonically+-- increasing.+monotonicLineToks :: (IsToken a) => [a] -> [a]+monotonicLineToks toks = goMonotonicLineToks (0,0) toks++goMonotonicLineToks :: (IsToken a) => SimpPos -> [a] -> [a]+goMonotonicLineToks _ [] = []+goMonotonicLineToks _ [t] = [t]+goMonotonicLineToks (orow,ocol) (t1:t2:ts)+ = t1:goMonotonicLineToks offset' (t2':ts)+ where+ offset' = if (tokenRow t1 - orow) > (tokenRow t2)+ then (orow + (tokenRow t1) - tokenRow t2 + 1, ocol)+ else (orow,ocol)++ -- t1' = increaseSrcSpan (orow,ocol) t1+ t2' = increaseSrcSpan offset' t2++-- ---------------------------------------------------------------------+{- No longer used+-- |Retrieve all the tokens at the leaves of the tree, in order.+-- Marked tokens are re-aligned, and gaps are closed.+retrieveTokensFinal :: (IsToken a) => Tree (Entry a) -> [a]+retrieveTokensFinal forest = monotonicLineToks $ stripForestLines $ reAlignMarked+ $ deleteGapsToks $ retrieveTokens' forest+-}+-- ---------------------------------------------------------------------++reAlignMarked :: (IsToken a) => [a] -> [a]+reAlignMarked toks = concatMap alignOne $ groupTokensByLine toks+ where+ -- alignOne toksl = unmarked ++ (reAlignToks marked)+ alignOne toksl = unmarked ++ (reAlignOneLine marked)+ where+ (unmarked,marked) = break isMarked toksl++-- ---------------------------------------------------------------------++-- | Some tokens are marked if they belong to identifiers which have+-- been renamed. When the renaming takes place, no layout adjustment+-- is done. This function adjusts the spacing for the rest of the line+-- to match as far as possible the original spacing, except for the+-- name change.+reAlignOneLine :: (IsToken a) => [a] -> [a]+reAlignOneLine toks = go (0,0) toks+ where+ go _ [] = []+ go (l,c) (t:ts) = (increaseSrcSpan (l,c) t') : (go (l,c') ts)+ where+ (t',dc) = adjustToken t+ c' = c + dc++ adjustToken tt+ | tokenLen tt == 0 = (tt,0)+ | otherwise = (tt',deltac)+ where+ Span (sl,sc) (el,ec) = getSpan tt+ deltac = (tokenLen tt) - (ec - sc)+ newPos = Span (sl,sc) (el,ec+deltac)+ tt' = putSpan tt newPos+{-+ adjustToken tt@(_,"") = (tt,0)+ adjustToken tt@(lt@(GHC.L _ t),s) = ((GHC.L newL t,s),deltac)+ where+ (sl,sc) = getLocatedStart lt+ (el,ec) = getLocatedEnd lt+ deltac = (length s) - (ec - sc)++ filename = fileNameFromTok tt+ newL = GHC.mkSrcSpan (GHC.mkSrcLoc filename sl sc)+ (GHC.mkSrcLoc filename el (ec + deltac))+-}++++{-+reAlignToks :: (IsToken a) => [a] -> [a]+reAlignToks [] = []+reAlignToks [t] = [t]+reAlignToks (tok1@(_,""):ts) = tok1 : reAlignToks ts+reAlignToks (tok1@((GHC.L l1 _t1),_s1):tok2@((GHC.L l2 t2),s2):ts)+ = tok1:reAlignToks (tok2':ts)+ where+ ((_sr1,_sc1),(er1,ec1)) = (getGhcLoc l1,getGhcLocEnd l1)+ (( sr2, sc2),(er2,ec2)) = (getGhcLoc l2,getGhcLocEnd l2)++ ((sr,sc),(er,ec)) = if (er1 == sr2 && ec1 >= sc2)+ then ((sr2,ec1+1),(er2,ec1+1 + tokenLen tok2))+ else ((sr2,sc2),(er2,ec2))++ fname = case l2 of+ GHC.RealSrcSpan ss -> GHC.srcSpanFile ss+ _ -> GHC.mkFastString "foo"+ l2' = GHC.mkRealSrcSpan (GHC.mkRealSrcLoc fname sr sc)+ (GHC.mkRealSrcLoc fname er ec)+ tok2' = ((GHC.L (GHC.RealSrcSpan l2') t2),s2)+-}+-- ---------------------------------------------------------------------+{-+-- TODO: deprecated, doing this at the Ppr level now+adjustLinesForDeleted :: (IsToken a) => Tree (Entry a) -> Tree (Entry a)+adjustLinesForDeleted forest = forest'+ where+ (_,forest') = go (0,0) forest++ go :: (IsToken a) => (Int,Int) -> Tree (Entry a) -> ((Int,Int),Tree (Entry a))+ go (ro,co) (n@(Node (Entry _ss _lay _toks) [])) = ((ro,co),applyOffsetToTreeShallow (ro,co) n)+ go (ro,co) (n@(Node (Deleted _ _ (gr,_gc)) _nullSubs)) = ((ro-gr,co),n)+ go (ro,co) (n@(Node (Entry _ss _lay []) _subs)) = ((ro',co'),Node (Entry ss lay []) subs')+ where+ (Node (Entry ss lay []) subs) = applyOffsetToTreeShallow (ro,co) n+ ((ro',co'),subs') = foldl' go' ((ro,co),[]) subs++ go (_ro,_co) (n@(Node (Entry _ss _lay _toks) _subs))+ = error $ "adjustLinesForDeleted: Entry with toks and subs:" ++ (show n)++ go' ((ro,co),acc) tree = ((ro',co'),acc++[tree'])+ where+ ((ro',co'),tree') = go (ro,co) tree+-}+-- ---------------------------------------------------------------------++applyOffsetToTreeShallow :: (IsToken a) => (Int,Int) -> Tree (Entry a) -> Tree (Entry a)+applyOffsetToTreeShallow (ro,co) (Node (Entry sspan lay toks) subs)+ = (Node (Entry sspan' lay toks') subs')+ where+ sspan' = addOffsetToForestSpan (ro,co) sspan+ toks' = addOffsetToToks (ro,co) toks+ subs' = subs+applyOffsetToTreeShallow _ n@(Node (Deleted _ _ _) _) = n++-- ---------------------------------------------------------------------++addOffsetToForestSpan :: (Int,Int) -> ForestSpan -> ForestSpan+addOffsetToForestSpan (lineOffset,colOffset) fspan = fspan'+ where+ ((ForestLine sch str sv sl,sc),(ForestLine ech etr ev el,ec)) = fspan+ fspan' = ((ForestLine sch str sv (sl+lineOffset),sc+colOffset),+ (ForestLine ech etr ev (el+lineOffset),ec+colOffset))++-- ---------------------------------------------------------------------++stripForestLines :: (IsToken a) => [a] -> [a]+stripForestLines toks = map doOne toks+ where+ doOne tok = tok'+ where+ l = getSpan tok+ tok' = putSpan tok l'+ ((ForestLine _ _ _ ls,_),(_,_)) = srcSpanToForestSpan l+ l' = insertForestLineInSpan (ForestLine False 0 0 ls) l++-- ---------------------------------------------------------------------++insertVersionsInSrcSpan :: Int -> Int -> Span -> Span+insertVersionsInSrcSpan vs ve ss = ss'+ where+ Span (sl,sc) (el,ec) = ss+ (chs,che) = forestSpanLenChangedFlags $ srcSpanToForestSpan ss+ (trs,tre) = forestSpanAstVersions $ srcSpanToForestSpan ss+ lineStart = forestLineToGhcLine (ForestLine chs trs vs sl)+ lineEnd = forestLineToGhcLine (ForestLine che tre ve el)+ ss' = Span (lineStart,sc) (lineEnd,ec)++-- ---------------------------------------------------------------------++insertLenChangedInSrcSpan :: Bool -> Bool -> Span -> Span+insertLenChangedInSrcSpan chs che ss = ss'+ where+ Span (sl,sc) (el,ec) = ss+ sl' = if chs then sl .|. forestLenChangedMask+ else sl .&. (complement forestLenChangedMask)++ el' = if che then el .|. forestLenChangedMask+ else el .&. (complement forestLenChangedMask)++ ss' = Span (sl',sc) (el',ec)+++-- ---------------------------------------------------------------------++-- | Replace any ForestLine flags already in a SrcSpan with the given ones+insertForestLineInSpan :: ForestLine -> Span -> Span+insertForestLineInSpan fl@(ForestLine ch tr v _l) ss = ss'+ where+ lineStart = forestLineToGhcLine fl+ ((_,cs),(ForestLine _ _ _ le,ce)) = srcSpanToForestSpan ss+ lineEnd = forestLineToGhcLine (ForestLine ch tr v le)+ ss' = Span (lineStart,cs) (lineEnd,ce)++-- ---------------------------------------------------------------------+++-- |Strip out the version markers+forestSpanToGhcPos :: ForestSpan -> (SimpPos,SimpPos)+forestSpanToGhcPos ((fls,sc),(fle,ec))+ = ((forestLineToGhcLine fls,sc),(forestLineToGhcLine fle,ec))++-- ---------------------------------------------------------------------++-- | Get the indent of the line before, taking into account in-line+-- 'where', 'let', 'in' and 'do' tokens+getIndentOffset :: (IsToken a) => [a] -> SimpPos -> Int+getIndentOffset [] _pos = 1+getIndentOffset _toks (0,0) = 1+getIndentOffset toks pos+ = let (ts1, ts2) = break (\t->tokenPos t >= pos) toks+ in if (null ts2)+ then error "haskell-token-utils error: position does not exist in the token stream!"+ else let (sl,_) = splitOnNewLn $ reverse ts1+ -- sl is the reversed tokens of the previous line+ (sls,_) = break isWhereOrLet $ filter (\t -> tokenLen t > 0) sl+ firstTok = (glast "getIndentOffset" sls)+ in if startLayout firstTok+ then if (length sls > 1)+ then tokenOffset (last $ init sls)+ else 4 + tokenOffset firstTok+ else tokenOffset firstTok++ where+ tokenOffset t = (tokenCol t) - 1++ startLayout tok = isDo tok || isIn tok || isLet tok || isWhere tok+ {-+ startLayout ((GHC.L _ (GHC.ITdo)),_) = True+ startLayout ((GHC.L _ (GHC.ITin)),_) = True+ startLayout ((GHC.L _ (GHC.ITlet)),_) = True+ startLayout ((GHC.L _ (GHC.ITwhere)),_) = True+ startLayout _ = False+ -}++-- ---------------------------------------------------------------------++splitOnNewLn :: (IsToken a) => [a] -> ([a],[a])+splitOnNewLn toks = go [] toks+ -- ++AZ++ : TODO: is this simpler? : (toks1,toks2)=break (\x' -> tokenRow x /= tokenRow x') rtoks++ where+ go [] [] = ([],[])+ go ss [] = (ss,[])+ go [] xs = go [head xs] (tail xs)+ go ss xs+ | onSameLn (glast "splitOnNewLn" ss) (head xs) = go (ss ++ [head xs]) (tail xs)+ | otherwise = (ss,xs)++-- ---------------------------------------------------------------------++onSameLn :: (IsToken a) => a -> a -> Bool+onSameLn t1 t2 = r1 == r2+ where+ Span (r1,_) _ = getSpan t1+ Span (r2,_) _ = getSpan t2+-- ---------------------------------------------------------------------++newLnToken :: (IsToken a) => a -> a+newLnToken tok = newLinesToken 1 tok++-- ---------------------------------------------------------------------+++newLinesToken :: (IsToken a) => Int -> a -> a+newLinesToken jump tok = tok'+ where+ Span (sl,_) _ = getSpan tok+ nl = sl + jump+ tok' = putSpan mkZeroToken (Span (nl,1) (nl,1))+{-+ l' = case l of+ GHC.RealSrcSpan ss ->+ let+ loc = GHC.mkSrcLoc (GHC.srcSpanFile ss) (jump + GHC.srcSpanEndLine ss) 1+ in+ GHC.mkSrcSpan loc loc+ _ -> l+-}++-- ---------------------------------------------------------------------++-- This stuff may be needed+{-+-- ---------------------------------------------------------------------++-- |Open a zipper so that its focus has the given SrcSpan in its+-- subtree, or the location where the SrcSpan should go, if it is not+-- in the tree+openZipperToSpan+ :: ForestSpan+ -> Z.TreePos Z.Full (Entry PosToken)+ -> Z.TreePos Z.Full (Entry PosToken)+openZipperToSpan sspan z+ | hasVersions = openZipperToSpanAdded sspan z+ | otherwise = openZipperToSpanOrig sspan z+ where+ (vs,_ve) = forestSpanVersions sspan+ hasVersions = vs /= 0+++-- ---------------------------------------------------------------------++-- |Open a zipper so that its focus has the given SrcSpan in its+-- subtree, or the location where the SrcSpan should go, if it is not+-- in the tree+openZipperToSpanOrig+ :: ForestSpan+ -> Z.TreePos Z.Full (Entry PosToken)+ -> Z.TreePos Z.Full (Entry PosToken)+openZipperToSpanOrig sspan z+ = if (treeStartEnd (Z.tree z) == sspan) || (Z.isLeaf z)+ then z+ else z'+ where+ -- go through all of the children to find the one that+ -- either is what we are looking for, or contains it++ -- childrenAsZ = go [] (Z.firstChild z)+ childrenAsZ = getChildrenAsZ z+ z' = case (filter contains childrenAsZ) of+ [] -> z -- Not directly in a subtree, this is as good as+ -- it gets+ [x] -> -- exactly one, drill down+ openZipperToSpan sspan x++ xx -> case (filter (\zt -> (treeStartEnd $ Z.tree zt) == sspan) xx) of + [] -> -- more than one matches, see if we can get+ -- rid of the ones that have been lengthened+ case (filter (not .forestSpanLenChanged . treeStartEnd . Z.tree) xx) of+ [] -> z -- we tried...+ [w] -> openZipperToSpan sspan w+ -- ww -> error $ "openZipperToSpan:can't resolve:(sspan,ww)="++(show (sspan,ww))+ ww -> -- more than one candidate, break+ -- the tie on version match+ case (filter (\zt -> matchVersions sspan zt) ww) of+ [v] -> openZipperToSpan sspan v+ _ -> error $ "openZipperToSpan:can't resolve:(sspan,ww)="++(show (sspan,map (\zt -> treeStartEnd $ Z.tree zt) ww))+ [y] -> openZipperToSpan sspan y+ yy -> -- Multiple, check if we can separate out by+ -- version+ case (filter (\zt -> (fst $ forestSpanVersions $ treeStartEnd $ Z.tree zt) == (fst $ forestSpanVersions sspan)) xx) of+ -- [] -> z+ [] -> error $ "openZipperToSpan:no version match:(sspan,yy)=" ++ (show (sspan,yy)) -- ++AZ+++ [w] -> openZipperToSpan sspan w+ _ww -> error $ "openZipperToSpan:multiple version match:" ++ (show (sspan,yy)) -- ++AZ++++ contains zn = spanContains (treeStartEnd $ Z.tree zn) sspan++ matchVersions span1 z2 = isMatch+ where+ span2 = treeStartEnd $ Z.tree z2+ isMatch = forestSpanVersions span1 == forestSpanVersions span2++-- ---------------------------------------------------------------------++-- |Open a zipper so that its focus has the given SrcSpan in its+-- subtree, or the location where the SrcSpan should go, if it is not+-- in the tree.+-- In the case of an 'Above' layout with the same SrcSpan below,+-- return that instead+openZipperToSpanDeep+ :: ForestSpan+ -> Z.TreePos Z.Full (Entry PosToken)+ -> Z.TreePos Z.Full (Entry PosToken)+openZipperToSpanDeep sspan z = zf+ where+ z' = openZipperToSpan sspan z++ zf = case Z.tree z' of+ (Node (Entry _ (Above _ _ _ _) _) _) ->+ case getChildrenAsZ z' of+ [] -> z'+ [x] -> if (treeStartEnd (Z.tree x) == sspan) then x else z'+ _ -> z'+ _ -> z'+++-- ---------------------------------------------------------------------++-- |Open a zipper to a SrcSpan that has been added in the tree, and+-- thus does not necessarily fall in the logical hierarchy of the tree+openZipperToSpanAdded+ :: ForestSpan+ -> Z.TreePos Z.Full (Entry PosToken)+ -> Z.TreePos Z.Full (Entry PosToken)+openZipperToSpanAdded sspan z = zf+ where+ treeAsList = getTreeSpansAsList $ Z.tree z++ -- True if first span contains the second+ myMatch (((ForestLine _ _ vs1 rs1),cs1),((ForestLine _ _ ve1 re1),ce1))+ (((ForestLine _ _ vs2 rs2),cs2),((ForestLine _ _ ve2 re2),ce2))+ = vs1 == vs2 && ve1 == ve2 && ((rs1,cs1) <= (rs2,cs2)) && ((re1,ce1) >= (re2,ce2))+ tl2 = dropWhile (\(_,s) -> not (myMatch s sspan)) $ reverse treeAsList++ fff [] _ = []+ fff acc@((cd,_cs):_) (v,sspan') = if v < cd then (v,sspan'):acc+ else acc++ tl3 = foldl' fff [(head tl2)] tl2+ -- tl3 now contains the chain of ForestSpans to open in order in the zipper++ zf = foldl' (flip openZipperToSpanOrig) z $ map snd tl3++-- ---------------------------------------------------------------------++getTreeSpansAsList :: Tree (Entry PosToken) -> [(Int,ForestSpan)]+getTreeSpansAsList = getTreeSpansAsList' 0++getTreeSpansAsList' :: Int -> Tree (Entry PosToken) -> [(Int,ForestSpan)]+getTreeSpansAsList' level (Node (Deleted sspan _pg _eg ) _ ) = [(level,sspan)]+getTreeSpansAsList' level (Node (Entry sspan _lay _toks) ts0) = (level,sspan)+ : (concatMap (getTreeSpansAsList' (level + 1)) ts0)+++-- ---------------------------------------------------------------------++-- |Split a forest of trees into a (begin,middle,end) according to a+-- ForestSpan, such that no tokens are included in begin or end belonging+-- to the ForestSpan, and all of middle has some part of the ForestSpan+splitForestOnSpan :: Forest (Entry PosToken) -> ForestSpan+ -> ([Tree (Entry PosToken)],[Tree (Entry PosToken)],[Tree (Entry PosToken)])+splitForestOnSpan forest sspan = (beginTrees,middleTrees,endTrees)+ where+ (spanStart,spanEnd) = sspan++ (beginTrees,rest) = break (\t -> not $ inBeginTrees t) forest+ (middleTrees,endTrees) = break (\t -> inEndTrees t) rest++ inBeginTrees tree = spanStart >= treeEnd+ where+ (_treeStart,treeEnd) = treeStartEnd tree++ inEndTrees tree = spanEnd <= treeStart+ where+ (treeStart,_treeEnd) = treeStartEnd tree++++++-}
src/Language/Haskell/TokenUtils/Types.hs view
@@ -38,6 +38,7 @@ , addOffsetToToks , ghcLineToForestLine , forestLineToGhcLine+ , forestLenChangedMask , IsToken(..) , notWhiteSpace@@ -207,7 +208,8 @@ isComment :: a -> Bool -- |Zero-length tokens, as appear in GHC as markers- isEmpty :: a -> Bool+ isEmpty :: a -> Bool+ mkZeroToken :: a isDo :: a -> Bool isElse :: a -> Bool@@ -220,6 +222,13 @@ tokenToString :: a -> String -- TODO: may be able to get rid of next due to former showTokenStream :: [a] -> String++ -- |Mark a token so that it can be use to trigger layout checking+ -- later when the toks are retrieved+ markToken :: a -> a+ isMarked :: a -> Bool++ -- derived functions isWhiteSpace :: (IsToken a) => a -> Bool
src/Language/Haskell/TokenUtils/Utils.hs view
@@ -22,6 +22,7 @@ , placeAbove , allocList , strip+ , startEndLocIncComments' -- * SrcSpan to ForestSpan conversions , sf@@ -42,12 +43,14 @@ , forestPosLenChanged , treeIdIntoForestSpan , spanContains+ -- , insertForestLineInSpan , insertVersionsInForestSpan , insertLenChangedInForestSpan -- * Spans , spanStartEnd , combineSpans+ , nonCommentSpan -- * drawing the various trees , drawTreeEntry@@ -56,6 +59,9 @@ , drawTreeCompact , drawTreeWithToks , showForestSpan+ , drawTokenCache+ , drawTokenCacheDetailed+ , divideComments ) where import Control.Exception@@ -66,6 +72,7 @@ -- import Language.Haskell.TokenUtils.Layout -- import Language.Haskell.TokenUtils.TokenUtils import Language.Haskell.TokenUtils.Types+import qualified Data.Map as Map -- --------------------------------------------------------------------- @@ -167,6 +174,16 @@ -- --------------------------------------------------------------------- +-- | Get the start&end location of t in the token stream, then extend+-- the start and end location to cover the preceding and following+-- comments.+--+{-+-- In this routine, 'then','else','do' and 'in' are treated as comments.+startEndLocIncComments::(SYB.Data t) => [PosToken] -> t -> (SimpPos,SimpPos)+startEndLocIncComments toks t = startEndLocIncComments' toks (getStartEndLoc t)+-}+ startEndLocIncComments' :: (IsToken a) => [a] -> (SimpPos,SimpPos) -> (SimpPos,SimpPos) startEndLocIncComments' toks (startLoc,endLoc) = let@@ -371,6 +388,18 @@ (nodeStart,nodeEnd) = insertVersionsInForestSpan nvs nvs span2 -- ---------------------------------------------------------------------+{-+-- | Replace any ForestLine flags already in a Span with the given ones+insertForestLineInSpan :: ForestLine -> Span -> Span+insertForestLineInSpan fl@(ForestLine ch tr v _l) ss = ss'+ where+ Span (lineStart,sc) (_,ec) = ss+ -- lineStart = forestLineToGhcLine fl+ (_,(ForestLine _ _ _ le,_)) = srcSpanToForestSpan ss+ lineEnd = forestLineToGhcLine (ForestLine ch tr v le)+ ss' = Span (lineStart,sc) (lineEnd,ec)+-}+-- --------------------------------------------------------------------- insertVersionsInForestSpan :: Int -> Int -> ForestSpan -> ForestSpan insertVersionsInForestSpan vsNew veNew ((ForestLine chs trs _vs ls,cs),(ForestLine che tre _ve le,ce))@@ -414,6 +443,22 @@ -- --------------------------------------------------------------------- +-- |Extract the start and end position of a span, without any leading+-- or trailing comments+nonCommentSpan :: (IsToken a) => [a] -> (SimpPos,SimpPos)+nonCommentSpan [] = ((0,0),(0,0))+nonCommentSpan toks = (startPos,endPos)+ where+ stripped = dropWhile isIgnoredNonComment $ toks+ (startPos,endPos) = case stripped of+ [] -> ((0,0),(0,0))+ _ -> (tokenPos startTok,tokenPosEnd endTok)+ where+ startTok = ghead "nonCommentSpan.1" $ dropWhile isIgnoredNonComment $ toks+ endTok = ghead "nonCommentSpan.2" $ dropWhile isIgnoredNonComment $ reverse toks++-- ---------------------------------------------------------------------+ -- | ForestSpan version of GHC combineSrcSpans combineSpans :: ForestSpan -> ForestSpan -> ForestSpan combineSpans fs1 fs2 = fs'@@ -536,5 +581,28 @@ showLevel :: Int -> String showLevel level = take level (repeat ' ')++-- ---------------------------------------------------------------------++-- |Call drawTreeEntry on the entire token cache+drawTokenCache :: (IsToken a) => TokenCache a -> String+drawTokenCache tk = Map.foldlWithKey' doOne "" (tkCache tk)+ where+ doOne :: String -> TreeId -> Tree (Entry a) -> String+ doOne s key val = s ++ "tree " ++ (show key) ++ ":\n"+ ++ (drawTreeEntry val)++-- ---------------------------------------------------------------------++-- |Call drawTreeEntry on the entire token cache+drawTokenCacheDetailed :: (IsToken a) => TokenCache a -> String+drawTokenCacheDetailed tk = Map.foldlWithKey' doOne "" (tkCache tk)+ where+ doOne :: (IsToken a) => String -> TreeId -> Tree (Entry a) -> String+ doOne s key val = s ++ "tree " ++ (show key) ++ ":\n"+ ++ (show val)++-- ---------------------------------------------------------------------+