-- This example refactoring swaps the first two arguments of a user-selected function. -- For simplicity reason, we assume that partial application of the selected function does -- not affect the first two arguments. -- If the function is exported by the module, then this refactoring affected not only the -- current module, but also those module where this function is used. -- To apply this refactoring, point the cursor to the function name, then select the 'Swap -- argument' menu from the 'definitions' sub-menu in 'Refactor'. -- The refactoring name is 'swapArgs' and is the only name exported by this module. module RefacSwapArgs(swapArgs) where import RefacUtils swapArgs args = do let fileName = ghead "filename" args -- The first argument is always the filename of the current module. row = read (args!!1)::Int -- The line number of the selected function name. col = read (args!!2)::Int -- The column number of the selected function name. -- Parse the source file. In the returned result, 'inscps' in the in-scope relation which has the -- information about which top-level identifiers are visible to this module; 'exps' is the export -- relation which contains the information about which identifiers are exported by this module; -- 'mod' is the AST for the module and toks is the Token stream for the parsed module. (inscps, exps, mod, toks)<-parseSourceFile fileName -- get the identifier pointed by the cursor. let pnt =locToPNT fileName row col mod -- The identifier in PNT format pn = pNTtoPN pnt -- The identifier in PName format which contains less information than PNT. -- Was the cursor pointed to a function name defined in this module? if (pn /= defaultPN && isFunName pn mod ) then do -- Yes. Do the transformation in the current module. -- 'doSwap' is the function that really does the transformation. We run this function in a state monad -- in order to manipulate the token stream. In the state, we have the token stream, ie. toks, a flag -- indicating whether the token stream has been modified, and an integer which can be used when creating -- new names(although it is not used in this refactoring). (mod',((toks',m),_))<-runStateT (doSwap pn mod) ((toks,False), 0) -- Is this function name exported by the module? if isExported pnt exps -- Yes, it is exported. Then process those modules which possibly import this function. then do clients <- clientModsAndFiles =< do -- function 'swap' swaps two syntax phrases of the same type in both the AST and the token stream. pats'<-swap p1 p2 pats return (HsMatch loc fun pats' rhs ds) _ -> error "Insufficient arguments to swap." inMatch m = return m -- This function does not define the selected identifier, so don't change it. inExp exp@((Exp (HsApp (Exp (HsApp e e1)) e2))::HsExpP) | expToPN e == pn -- This is the call-site of the selected function name. = swap e1 e2 exp -- Swap the first two actual arguments in both the AST and the token stream. inExp e = return e -- This function handles type signatures. Haskell allows different function names share the same type signature. -- In this case, we might need to split a type signature into two, which means we take a type signature as input, -- and output a List of type signatures (this is not type-preserving as required by the full_buTP). In order to -- be type preserving, we need to go up a syntactic level. inDecls (decls::[HsDeclP]) = if after==[] then return decls --'decls' does not contain the type signature for the function, so do nothing, else do -- 'decls' contains the type signature for the function. -- Swap the types in the type signature. newSig <- swapInSig' [head after] -- return the new result. return (before++newSig++(tail after)) where -- split the declaration list into two parts. (before, after) = break (definesTypeSig pn) decls -- do swapping in the type signature. swapInSig' sig@[(Dec (HsTypeSig loc is c tp))] = let ts = tyFunToList tp in if length is == 1 -- True means the type signature only defines this function's type. then swap (ts!!0) (ts!!1) sig -- Swap the type for the first two parameters. else do tp' <- swap (ts!!0) (ts!!1) tp -- Split the original type signature into two. let newSig = [Dec (HsTypeSig loc (filter (\x->pNTtoPN x==pn) is) c tp'), Dec (HsTypeSig loc (filter (\x->pNTtoPN x/=pn) is) c tp)] -- Replace the old type signature by the new one in both the AST and the token stream. -- Note: In the token stream, we pretty-print the 'newSig', and this means that we -- lost the layout (and possibly some comments) of the old one. Will try to see whether -- this can be improved. update sig newSig sig tyFunToList (Typ (HsTyFun t1 t2)) = t1:(tyFunToList t2) tyFunToList t = [t] -- Run the swapping algorithm on a client module of the current module. swapInClientMod pn (modName, fileName) = do (_, _, mod, toks)<-parseSourceFile fileName -- Parse the program source. (mod',((toks',m),_))<-runStateT (doSwap pn mod) ((toks,False), 0) -- do the swapping. return ((fileName,m),(toks',mod')) -- return the result.