HaRe-0.7.2.5: src/Language/Haskell/Refact/Utils/TokenUtils.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
-- |
--
-- This module contains an API to manage a token stream.
--
-- This API is used internally by MonadFunctions and the other utility
-- modules, it should probably never be used directly in a refactoring.
module Language.Haskell.Refact.Utils.TokenUtils (
-- * Operations at 'TokenCache' level
putDeclToksInCache
, syncAstToLatestCache
-- * Operations at 'Tree' 'Entry' level
, addDeclToksAfterSrcSpan
, syncAST
-- * Utility
, posToSrcSpan
, posToSrcSpanTok
-- * Internal, for testing
, nonCommentSpan
, showSrcSpan
, showSrcSpanF
, ghcSpanStartEnd
, stripForestLineFromGhc
, ghcSrcSpanToForestSpan
, deleteGapsToks
) where
-- import qualified FastString as GHC
import qualified GHC as GHC
import qualified Data.Generics as SYB
import qualified GHC.SYB.Utils as SYB
import Language.Haskell.Refact.Utils.GhcUtils
import Language.Haskell.Refact.Utils.LocUtils
import Language.Haskell.Refact.Utils.TypeSyn
import Language.Haskell.TokenUtils.GHC.Layout
import Language.Haskell.TokenUtils.TokenUtils
import Language.Haskell.TokenUtils.Types
import Language.Haskell.TokenUtils.Utils
import Data.Tree
import qualified Data.Map as Map
-- import Debug.Trace
-- debug = flip trace
-- ---------------------------------------------------------------------
ghcSrcSpanToForestSpan :: GHC.SrcSpan -> ForestSpan
ghcSrcSpanToForestSpan sspan = ((ghcLineToForestLine startRow,startCol),(ghcLineToForestLine endRow,endCol))
where
(startRow,startCol) = getGhcLoc sspan
(endRow,endCol) = getGhcLocEnd sspan
-- ---------------------------------------------------------------------
putDeclToksInCache :: (SYB.Data t) =>
TokenCache PosToken -> GHC.SrcSpan -> [PosToken] -> GHC.Located t
-> (TokenCache PosToken,GHC.SrcSpan,GHC.Located t)
putDeclToksInCache tk sspan toks t = (tk'',ss2gs newSpan,t')
where
(tk'',newSpan) = putToksInCache tk (gs2ss sspan) toks
t' = syncAST t (ss2f newSpan)
-- ---------------------------------------------------------------------
-- |Assuming most recent operation has stashed the old tokens, sync
-- the given AST to the most recent stash entry
syncAstToLatestCache :: (SYB.Data t) => TokenCache PosToken -> GHC.Located t -> GHC.Located t
syncAstToLatestCache tk t = t'
where
mainForest = (tkCache tk) Map.! mainTid
(Node (Entry fspan _ _) _) = (tkCache tk) Map.! (tkLastTreeId tk)
pos = forestSpanToGhcPos fspan
sspan = posToSrcSpan mainForest pos
t' = syncAST t (gs2f sspan)
-- ---------------------------------------------------------------------
-- | Process the leaf nodes of a tree to remove all deleted spans
deleteGapsToks :: [Entry PosToken] -> [PosToken]
deleteGapsToks toks = goDeleteGapsToks (0,0) toks
goDeleteGapsToks :: SimpPos -> [Entry PosToken] -> [PosToken]
goDeleteGapsToks _ [] = []
goDeleteGapsToks offset [Entry _ _ t] = map (increaseSrcSpan offset) t
goDeleteGapsToks _ [Deleted _ _ _] = []
goDeleteGapsToks offset (Deleted _ _ _:ts) = goDeleteGapsToks offset ts
goDeleteGapsToks offset [Entry _ _ t,Deleted _ _ _] = map (increaseSrcSpan offset) t
goDeleteGapsToks offset (Entry _ _ t1:e@(Entry _ _ _):ts) = (map (increaseSrcSpan offset) t1) ++goDeleteGapsToks offset (e:ts)
goDeleteGapsToks (fr,fc) (Entry ss _lay1 t1:Deleted _ _ eg:t2:ts)
= t1' ++ goDeleteGapsToks offset' (t2:ts)
where
-- TODO: use actual first and last toks, may be comments
-- TODO: what about deletion within a line?
(deltaR,_deltaC) = eg
(_,(sr,_sc)) = forestSpanToSimpPos ss
((dr,_dc),_) = forestSpanToSimpPos $ forestSpanFromEntry t2
offset' = (fr + (sr - dr) + deltaR, fc)
t1' = map (increaseSrcSpan (fr,fc)) t1
-- ---------------------------------------------------------------------
stripForestLineFromGhc :: GHC.SrcSpan -> GHC.SrcSpan
stripForestLineFromGhc l = l'
where
((ForestLine _ _ _ ls,_),(_,_)) = ghcSrcSpanToForestSpan l
l' = insertForestLineInSrcSpan (ForestLine False 0 0 ls) l
-- ---------------------------------------------------------------------
-- |Add new tokens belonging to an AST fragment after a given SrcSpan,
-- and re-sync the AST fragment to match the new location
addDeclToksAfterSrcSpan :: (SYB.Data t) =>
Tree (Entry PosToken) -- ^TokenTree to be modified
-> GHC.SrcSpan -- ^Preceding location for new tokens
-> Positioning
-> [PosToken] -- ^New tokens to be added
-> GHC.Located t -- ^Declaration the tokens belong to, to be synced
-> (Tree (Entry PosToken), GHC.SrcSpan,GHC.Located t) -- ^ updated TokenTree ,SrcSpan location for
-- -> (Tree (Entry PosToken), GHC.SrcSpan,t) -- ^ updated TokenTree ,SrcSpan location for
-- the new tokens in the TokenTree, and
-- updated AST element
addDeclToksAfterSrcSpan forest oldSpan pos toks t = (forest',(ss2gs newSpan),t')
where
(forest',newSpan) = addToksAfterSrcSpan forest (gs2ss oldSpan) pos toks
t' = syncAST t (ss2f newSpan)
-- ---------------------------------------------------------------------
-- |Convert a simple (start,end) position to a SrcSpan belonging to
-- the file in the tree
posToSrcSpan :: Tree (Entry PosToken) -> (SimpPos,SimpPos) -> GHC.SrcSpan
posToSrcSpan forest ((rs,cs),(re,ce)) = sspan
where
(GHC.L l _,_) = ghead "posToSrcSpan" $ retrieveTokensInterim forest -- ++AZ++ Ouch, performance??
sspan = case l of
GHC.RealSrcSpan ss ->
let
locStart = GHC.mkSrcLoc (GHC.srcSpanFile ss) rs cs
locEnd = GHC.mkSrcLoc (GHC.srcSpanFile ss) re ce
in
GHC.mkSrcSpan locStart locEnd
_ -> error "posToSrcSpan: invalid SrcSpan in first tok"
-- ---------------------------------------------------------------------
-- |Convert a simple (start,end) position to a SrcSpan belonging to
-- the file in the given token
posToSrcSpanTok :: PosToken -> (SimpPos,SimpPos) -> GHC.SrcSpan
posToSrcSpanTok tok ((rs,cs),(re,ce)) = sspan
where
(GHC.L l _,_) = tok
sspan = case l of
GHC.RealSrcSpan ss ->
let
locStart = GHC.mkSrcLoc (GHC.srcSpanFile ss) rs cs
locEnd = GHC.mkSrcLoc (GHC.srcSpanFile ss) re ce
in
GHC.mkSrcSpan locStart locEnd
_ -> error "posToSrcSpan: invalid SrcSpan in first tok"
-- ---------------------------------------------------------------------
{-
-- |Get the start and end position of a SrcSpan
-- spanStartEnd :: GHC.SrcSpan -> (SimpPos,SimpPos)
-- spanStartEnd sspan = (getGhcLoc sspan,getGhcLocEnd sspan)
spanStartEnd :: GHC.SrcSpan -> ForestSpan
spanStartEnd sspan = ((ghcLineToForestLine sr,sc),(ghcLineToForestLine er,ec))
where
((sr,sc),(er,ec)) = (getGhcLoc sspan,getGhcLocEnd sspan)
-}
-- ---------------------------------------------------------------------
ghcSpanStartEnd :: GHC.SrcSpan -> ((Int, Int), (Int, Int))
ghcSpanStartEnd sspan = (getGhcLoc sspan,getGhcLocEnd sspan)
-- ---------------------------------------------------------------------
-- |Synchronise a located AST fragment to use a newly created SrcSpan
-- in the token tree.
-- TODO: Should this indent the tokens as well?
syncAST :: (SYB.Data t)
=> GHC.Located t -- ^The AST (or fragment)
-> ForestSpan -- ^The SrcSpan created in the Tree (Entry PosToken)
-> (GHC.Located t) -- ^Updated AST and tokens
syncAST ast@(GHC.L l _t) fspan = GHC.L sspan xx
where
sspan = f2gs fspan
(( sr, sc),( _er, _ec)) = ghcSpanStartEnd l
((nsr,nsc),(_ner,_nec)) = ghcSpanStartEnd sspan
rowOffset = nsr - sr
colOffset = nsc - sc
-- TODO: take cognizance of the ForestLines encoded in srcspans
-- when calculating the offsets etc
syncSpan s = addOffsetToSrcSpan (rowOffset,colOffset) s
(GHC.L _s xx) = everywhereStaged SYB.Renamer (
SYB.mkT hsbindlr
`SYB.extT` sig
`SYB.extT` ty
`SYB.extT` name
`SYB.extT` lhsexpr
`SYB.extT` lpat
`SYB.extT` limportdecl
`SYB.extT` lmatch
) ast
hsbindlr (GHC.L s b) = (GHC.L (syncSpan s) b) :: GHC.Located (GHC.HsBindLR GHC.Name GHC.Name)
sig (GHC.L s n) = (GHC.L (syncSpan s) n) :: GHC.LSig GHC.Name
ty (GHC.L s typ) = (GHC.L (syncSpan s) typ) :: (GHC.LHsType GHC.Name)
name (GHC.L s n) = (GHC.L (syncSpan s) n) :: GHC.Located GHC.Name
lhsexpr (GHC.L s e) = (GHC.L (syncSpan s) e) :: GHC.LHsExpr GHC.Name
lpat (GHC.L s p) = (GHC.L (syncSpan s) p) :: GHC.LPat GHC.Name
limportdecl (GHC.L s n) = (GHC.L (syncSpan s) n) :: GHC.LImportDecl GHC.Name
lmatch (GHC.L s m) = (GHC.L (syncSpan s) m) :: GHC.LMatch GHC.Name
-- ---------------------------------------------------------------------
addOffsetToSrcSpan :: (Int,Int) -> GHC.SrcSpan -> GHC.SrcSpan
addOffsetToSrcSpan (lineOffset,colOffset) sspan = sspan'
where
sspan' = case sspan of
GHC.RealSrcSpan ss ->
let
locStart = GHC.mkSrcLoc (GHC.srcSpanFile ss) (lineOffset + GHC.srcSpanStartLine ss) (colOffset + GHC.srcSpanStartCol ss)
locEnd = GHC.mkSrcLoc (GHC.srcSpanFile ss) (lineOffset + GHC.srcSpanEndLine ss) (colOffset + GHC.srcSpanEndCol ss)
in
GHC.mkSrcSpan locStart locEnd
_ -> sspan
-- ---------------------------------------------------------------------
-- EOF