reprinter 0.1.0.0 → 0.2.0.0
raw patch · 3 files changed
+226/−392 lines, 3 filesdep +textdep −bytestringdep ~basePVP ok
version bump matches the API change (PVP)
Dependencies added: text
Dependencies removed: bytestring
Dependency ranges changed: base
API changes (from Hackage documentation)
- Text.Reprinter: Position :: Int -> Int -> Position
- Text.Reprinter: [posColumn] :: Position -> Int
- Text.Reprinter: [posLine] :: Position -> Int
- Text.Reprinter: data Position
- Text.Reprinter: instance Data.Data.Data Text.Reprinter.Position
- Text.Reprinter: instance GHC.Show.Show Text.Reprinter.Position
- Text.Reprinter: splitBySpan :: (Position, Position) -> Source -> (Source, Source)
- Text.Reprinter: type Reprinting m = forall b. Typeable b => b -> m (Maybe (RefactorType, Source, (Position, Position)))
+ Text.Reprinter: advanceCol :: Position -> Position
+ Text.Reprinter: advanceLine :: Position -> Position
+ Text.Reprinter: initCol :: Col
+ Text.Reprinter: initLine :: Line
+ Text.Reprinter: instance Data.Data.Data Text.Reprinter.Col
+ Text.Reprinter: instance Data.Data.Data Text.Reprinter.Line
+ Text.Reprinter: instance GHC.Classes.Eq Text.Reprinter.Col
+ Text.Reprinter: instance GHC.Classes.Eq Text.Reprinter.Line
+ Text.Reprinter: instance GHC.Classes.Ord Text.Reprinter.Col
+ Text.Reprinter: instance GHC.Classes.Ord Text.Reprinter.Line
+ Text.Reprinter: instance GHC.Show.Show Text.Reprinter.Col
+ Text.Reprinter: instance GHC.Show.Show Text.Reprinter.Line
+ Text.Reprinter: instance GHC.Show.Show Text.Reprinter.RefactorType
+ Text.Reprinter: mkCol :: Int -> Either String Col
+ Text.Reprinter: mkLine :: Int -> Either String Line
+ Text.Reprinter: reprintSort :: (Monad m, Data ast) => Reprinting m -> ast -> Source -> m Source
+ Text.Reprinter: type Position = (Line, Col)
+ Text.Reprinter: type Span = (Position, Position)
+ Text.Reprinter: type Reprinting m = forall node. Typeable node => node -> m (Maybe (RefactorType, Source, Span))
- Text.Reprinter: genReprinting :: (Monad m, Refactorable t, Typeable t) => (t -> m Source) -> t -> m (Maybe (RefactorType, Source, (Position, Position)))
+ Text.Reprinter: genReprinting :: (Monad m, Refactorable t, Typeable t) => (t -> m Source) -> t -> m (Maybe (RefactorType, Source, Span))
- Text.Reprinter: getSpan :: Refactorable t => t -> (Position, Position)
+ Text.Reprinter: getSpan :: Refactorable t => t -> Span
- Text.Reprinter: reprint :: (Monad m, Data p) => Reprinting m -> p -> Source -> m Source
+ Text.Reprinter: reprint :: (Monad m, Data ast) => Reprinting m -> ast -> Source -> m Source
- Text.Reprinter: type Source = ByteString
+ Text.Reprinter: type Source = Text
Files
- LICENSE +0/−202
- reprinter.cabal +30/−57
- src/Text/Reprinter.hs +196/−133
− LICENSE
@@ -1,202 +0,0 @@-- Apache License- Version 2.0, January 2004- http://www.apache.org/licenses/-- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION-- 1. Definitions.-- "License" shall mean the terms and conditions for use, reproduction,- and distribution as defined by Sections 1 through 9 of this document.-- "Licensor" shall mean the copyright owner or entity authorized by- the copyright owner that is granting the License.-- "Legal Entity" shall mean the union of the acting entity and all- other entities that control, are controlled by, or are under common- control with that entity. For the purposes of this definition,- "control" means (i) the power, direct or indirect, to cause the- direction or management of such entity, whether by contract or- otherwise, or (ii) ownership of fifty percent (50%) or more of the- outstanding shares, or (iii) beneficial ownership of such entity.-- "You" (or "Your") shall mean an individual or Legal Entity- exercising permissions granted by this License.-- "Source" form shall mean the preferred form for making modifications,- including but not limited to software source code, documentation- source, and configuration files.-- "Object" form shall mean any form resulting from mechanical- transformation or translation of a Source form, including but- not limited to compiled object code, generated documentation,- and conversions to other media types.-- "Work" shall mean the work of authorship, whether in Source or- Object form, made available under the License, as indicated by a- copyright notice that is included in or attached to the work- (an example is provided in the Appendix below).-- "Derivative Works" shall mean any work, whether in Source or Object- form, that is based on (or derived from) the Work and for which the- editorial revisions, annotations, elaborations, or other modifications- represent, as a whole, an original work of authorship. For the purposes- of this License, Derivative Works shall not include works that remain- separable from, or merely link (or bind by name) to the interfaces of,- the Work and Derivative Works thereof.-- "Contribution" shall mean any work of authorship, including- the original version of the Work and any modifications or additions- to that Work or Derivative Works thereof, that is intentionally- submitted to Licensor for inclusion in the Work by the copyright owner- or by an individual or Legal Entity authorized to submit on behalf of- the copyright owner. For the purposes of this definition, "submitted"- means any form of electronic, verbal, or written communication sent- to the Licensor or its representatives, including but not limited to- communication on electronic mailing lists, source code control systems,- and issue tracking systems that are managed by, or on behalf of, the- Licensor for the purpose of discussing and improving the Work, but- excluding communication that is conspicuously marked or otherwise- designated in writing by the copyright owner as "Not a Contribution."-- "Contributor" shall mean Licensor and any individual or Legal Entity- on behalf of whom a Contribution has been received by Licensor and- subsequently incorporated within the Work.-- 2. Grant of Copyright License. Subject to the terms and conditions of- this License, each Contributor hereby grants to You a perpetual,- worldwide, non-exclusive, no-charge, royalty-free, irrevocable- copyright license to reproduce, prepare Derivative Works of,- publicly display, publicly perform, sublicense, and distribute the- Work and such Derivative Works in Source or Object form.-- 3. Grant of Patent License. Subject to the terms and conditions of- this License, each Contributor hereby grants to You a perpetual,- worldwide, non-exclusive, no-charge, royalty-free, irrevocable- (except as stated in this section) patent license to make, have made,- use, offer to sell, sell, import, and otherwise transfer the Work,- where such license applies only to those patent claims licensable- by such Contributor that are necessarily infringed by their- Contribution(s) alone or by combination of their Contribution(s)- with the Work to which such Contribution(s) was submitted. If You- institute patent litigation against any entity (including a- cross-claim or counterclaim in a lawsuit) alleging that the Work- or a Contribution incorporated within the Work constitutes direct- or contributory patent infringement, then any patent licenses- granted to You under this License for that Work shall terminate- as of the date such litigation is filed.-- 4. Redistribution. You may reproduce and distribute copies of the- Work or Derivative Works thereof in any medium, with or without- modifications, and in Source or Object form, provided that You- meet the following conditions:-- (a) You must give any other recipients of the Work or- Derivative Works a copy of this License; and-- (b) You must cause any modified files to carry prominent notices- stating that You changed the files; and-- (c) You must retain, in the Source form of any Derivative Works- that You distribute, all copyright, patent, trademark, and- attribution notices from the Source form of the Work,- excluding those notices that do not pertain to any part of- the Derivative Works; and-- (d) If the Work includes a "NOTICE" text file as part of its- distribution, then any Derivative Works that You distribute must- include a readable copy of the attribution notices contained- within such NOTICE file, excluding those notices that do not- pertain to any part of the Derivative Works, in at least one- of the following places: within a NOTICE text file distributed- as part of the Derivative Works; within the Source form or- documentation, if provided along with the Derivative Works; or,- within a display generated by the Derivative Works, if and- wherever such third-party notices normally appear. The contents- of the NOTICE file are for informational purposes only and- do not modify the License. You may add Your own attribution- notices within Derivative Works that You distribute, alongside- or as an addendum to the NOTICE text from the Work, provided- that such additional attribution notices cannot be construed- as modifying the License.-- You may add Your own copyright statement to Your modifications and- may provide additional or different license terms and conditions- for use, reproduction, or distribution of Your modifications, or- for any such Derivative Works as a whole, provided Your use,- reproduction, and distribution of the Work otherwise complies with- the conditions stated in this License.-- 5. Submission of Contributions. Unless You explicitly state otherwise,- any Contribution intentionally submitted for inclusion in the Work- by You to the Licensor shall be under the terms and conditions of- this License, without any additional terms or conditions.- Notwithstanding the above, nothing herein shall supersede or modify- the terms of any separate license agreement you may have executed- with Licensor regarding such Contributions.-- 6. Trademarks. This License does not grant permission to use the trade- names, trademarks, service marks, or product names of the Licensor,- except as required for reasonable and customary use in describing the- origin of the Work and reproducing the content of the NOTICE file.-- 7. Disclaimer of Warranty. Unless required by applicable law or- agreed to in writing, Licensor provides the Work (and each- Contributor provides its Contributions) on an "AS IS" BASIS,- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or- implied, including, without limitation, any warranties or conditions- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A- PARTICULAR PURPOSE. You are solely responsible for determining the- appropriateness of using or redistributing the Work and assume any- risks associated with Your exercise of permissions under this License.-- 8. Limitation of Liability. In no event and under no legal theory,- whether in tort (including negligence), contract, or otherwise,- unless required by applicable law (such as deliberate and grossly- negligent acts) or agreed to in writing, shall any Contributor be- liable to You for damages, including any direct, indirect, special,- incidental, or consequential damages of any character arising as a- result of this License or out of the use or inability to use the- Work (including but not limited to damages for loss of goodwill,- work stoppage, computer failure or malfunction, or any and all- other commercial damages or losses), even if such Contributor- has been advised of the possibility of such damages.-- 9. Accepting Warranty or Additional Liability. While redistributing- the Work or Derivative Works thereof, You may choose to offer,- and charge a fee for, acceptance of support, warranty, indemnity,- or other liability obligations and/or rights consistent with this- License. However, in accepting such obligations, You may act only- on Your own behalf and on Your sole responsibility, not on behalf- of any other Contributor, and only if You agree to indemnify,- defend, and hold each Contributor harmless for any liability- incurred by, or claims asserted against, such Contributor by reason- of your accepting any such warranty or additional liability.-- END OF TERMS AND CONDITIONS-- APPENDIX: How to apply the Apache License to your work.-- To apply the Apache License to your work, attach the following- boilerplate notice, with the fields enclosed by brackets "[]"- replaced with your own identifying information. (Don't include- the brackets!) The text should be enclosed in the appropriate- comment syntax for the file format. We also recommend that a- file or class name and description of purpose be included on the- same "printed page" as the copyright notice for easier- identification within third-party archives.-- Copyright 2017 Dominic Orchard-- Licensed under the Apache License, Version 2.0 (the "License");- you may not use this file except in compliance with the License.- You may obtain a copy of the License at-- http://www.apache.org/licenses/LICENSE-2.0-- Unless required by applicable law or agreed to in writing, software- distributed under the License is distributed on an "AS IS" BASIS,- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.- See the License for the specific language governing permissions and- limitations under the License.
reprinter.cabal view
@@ -1,64 +1,37 @@--- Initial reprinter.cabal generated by cabal init. For further --- documentation, see http://haskell.org/cabal/users-guide/---- The name of the package.-name: reprinter---- The package version. See the Haskell package versioning policy (PVP) --- for standards guiding when and how versions should be incremented.--- https://wiki.haskell.org/Package_versioning_policy--- PVP summary: +-+------- breaking API changes--- | | +----- non-breaking API additions--- | | | +--- code changes with no API change-version: 0.1.0.0---- A short (one-line) description of the package.-synopsis: Scrap Your Reprinter--description: A datatype generic algorithm for layout-preserving refactoring---- A longer description of the package.--- description: -license: Apache-2.0-license-file: LICENSE---- The package author(s).-author: Dominic Orchard, Harry Clarke---- An email address to which users can send suggestions, bug reports, and --- patches.-maintainer: d.a.orchard@kent.ac.uk---- A copyright notice.--- copyright: --category: Text--build-type: Simple+-- This file has been generated from package.yaml by hpack version 0.17.1.+--+-- see: https://github.com/sol/hpack --- Constraint on the version of Cabal needed to build this package.-cabal-version: >=1.10+name: reprinter+version: 0.2.0.0+synopsis: Scrap Your Reprinter+description: A datatype generic algorithm for layout-preserving refactoring+license: Apache-2.0+author: Dominic Orchard, Harry Clarke+maintainer: d.a.orchard@kent.ac.uk+category: Text+homepage: https://github.com/camfort/reprinter#readme+bug-reports: https://github.com/camfort/reprinter/issues+build-type: Simple+cabal-version: >= 1.10 source-repository head type: git location: https://github.com/camfort/reprinter library- -- Modules exported by the library.- exposed-modules: Text.Reprinter- - -- Modules included in this library but not exported.- -- other-modules: - - -- LANGUAGE extensions used by modules in this package.- other-extensions: RankNTypes, DeriveDataTypeable, FlexibleInstances, Rank2Types, GADTs, KindSignatures, ConstraintKinds- - -- Other library packages from which modules are imported.- build-depends: base >=4.9 && <4.10, bytestring >=0.10 && <0.11, transformers >=0.5 && <0.6, syb >=0.6 && <0.7, uniplate >=1.6 && <1.7, mtl >=2.2 && <2.3, syz == 0.2.0.0- - -- Directories containing source files.- hs-source-dirs: src- - -- Base language which the package is written in.- default-language: Haskell2010- + exposed-modules:+ Text.Reprinter+ other-modules:+ Paths_reprinter+ build-depends:+ base >=4.9 && <5+ , text >=1.2.2 && <2+ , transformers >=0.5 && <0.6+ , syb >=0.6 && <0.7+ , uniplate >=1.6 && <1.7+ , mtl >=2.2 && <2.3+ , syz ==0.2.0.0+ hs-source-dirs:+ src+ default-language: Haskell2010
src/Text/Reprinter.hs view
@@ -2,182 +2,245 @@ {-# LANGUAGE DeriveDataTypeable #-} module Text.Reprinter- ( reprint- , splitBySpan- , Position(..)+ (+ reprintSort+ , reprint , Source- , Reprinting+ , Position , initPosition+ , initLine+ , initCol+ , mkLine+ , mkCol+ , advanceLine+ , advanceCol+ , Span+ , Reprinting , catchAll , genReprinting , Refactorable(..) , RefactorType(..) ) where -import Data.Generics.Zipper-import Debug.Trace-import qualified Data.ByteString.Char8 as B-import Data.Data+import Control.Monad (forM) import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.State.Lazy+import qualified Data.Text.Lazy as Text+import Data.Data+import Data.Generics.Zipper+import Data.Monoid ((<>), mempty)+import Data.List (sortOn) -type Source = B.ByteString+-- | Text from source file+type Source = Text.Text -data Position = Position { posColumn :: Int, posLine :: Int }- deriving (Data, Show)+-- | A line within the source text+newtype Line = Line Int deriving (Data, Eq, Ord, Show) -initPosition = Position { posColumn = 1, posLine = 1 }+-- | Lines start at 1+initLine :: Line+initLine = Line 1 -type Reprinting m =- forall b . Typeable b- => b -> m (Maybe (RefactorType, Source, (Position, Position)))+-- | Smart constructor for a Line, checks that line >= 1+mkLine :: Int -> Either String Line+mkLine l+ | l < 1 = Left $ "mkLine: called with: " <> show l <> ". Minimum is 1."+ | otherwise = Right (Line l) --- | The reprint algorithm takes a refactoring (parameteric in--- | some monad m) and turns an arbitrary pretty-printable type 'p'--- | into a monadic Source transformer.-reprint :: (Monad m, Data p)- => Reprinting m -> p -> Source -> m Source-reprint reprinting tree input- -- If the inupt is null then null is returned- | B.null input = return B.empty+-- | A column within the source text+newtype Col = Col Int deriving (Data, Eq, Ord, Show) - -- Otherwise go with the normal algorithm- | otherwise = do- -- Initial state comprises start cursor and input source- let state0 = (initPosition, input)- -- Enter the top-node of a zipper for 'tree'- (out, (_, remaining)) <- runStateT (enter reprinting (toZipper tree)) state0- -- Add to the output source the reamining input source- return $ out `B.append` remaining+-- | Columns start at 1+initCol :: Col+initCol = Col 1 --- The enter, enterDown, enterRight each take a refactoring and a--- zipper producing a stateful Source transformer with Position--- state.+-- | Smart constructor for a Col, checks that column >= 1+mkCol :: Int -> Either String Col+mkCol l+ | l < 1 = Left $ "mkCol: called with: " <> show l <> ". Minimum is 1."+ | otherwise = Right (Col l) -enter, enterDown, enterRight- :: Monad m- => Reprinting m -> Zipper a -> StateT (Position, Source) m Source+-- | A position in a text (imagine a cursor)+type Position = (Line,Col) --- `enter` applies the generic refactoring to the current context--- of the zipper-enter reprinting z = do+-- | The initial position+initPosition :: Position+initPosition = (initLine,initCol) - -- Step 1.- -- Apply a refactoring- refactoringInfo <- lift $ query reprinting z+-- | Given a position, go down a line, going back to the initial column+advanceLine :: Position -> Position+advanceLine (Line x, _) = (Line (x+1), initCol) - -- Step 2.- output <-- case refactoringInfo of- -- No refactoring, so go into the children- Nothing -> enterDown reprinting z+-- | Given a position, advance by one column+advanceCol :: Position -> Position+advanceCol (ln, Col x) = (ln, Col (x+1)) - -- A refactoring was applied- Just (typ, output, (lb, ub)) -> do- (cursor, inp) <- get- case typ of- Replace -> do- -- Get the soure text up to the start of the refactored expr- let (p0, inp') = splitBySpan (cursor, lb) inp- -- Cut out the portion of source text consumed by the refactoring- let (_, inp'') = splitBySpan (lb, ub) inp'- put (ub, inp'')- return $ B.concat [p0, output]- After -> do- -- Get the soure text up to the end of the refactored expr- let (p0, inp') = splitBySpan (cursor, ub) inp- put (ub, inp')- return $ B.concat [p0, output]- Before -> do- -- Get the soure text up to the start of the refactored expr- let (p0, inp') = splitBySpan (cursor, lb) inp- -- Cut out the portion of source text consumed by the refactoring- let (p1, inp'') = splitBySpan (lb, ub) inp'- put (ub, inp'')- return $ B.concat [p0, output, p1]+-- | Two positions give the lower and upper bounds of a source span+type Span = (Position, Position) - -- Part 3.- -- Enter the right sibling of the current context- output' <- enterRight reprinting z+-- | Type of a reprinting function+type Reprinting m = forall node . Typeable node => node -> m (Maybe (RefactorType, Source, Span)) - -- Concat the output for the current context, children, and right sibling- return $ B.concat [output, output']+-- | Specify a refactoring type+data RefactorType = Before | After | Replace+ deriving Show -- for debugging --- `enterDown` navigates to the children of the current context-enterDown reprinting z =- case down' z of- -- Go to children- Just dz -> enter reprinting dz- -- No children- Nothing -> return B.empty+-- | The reprint algorithm takes a refactoring (parameteric in+-- | some monad m) and turns an arbitrary pretty-printable type 'ast'+-- | into a monadic Source transformer.+reprint :: (Monad m, Data ast) => Reprinting m -> ast -> Source -> m Source+reprint reprinting ast input+ -- If the input is empty return empty+ | Text.null input = return mempty --- `enterRight` navigates to the right sibling of the current context-enterRight reprinting z =- case right z of- -- Go to right sibling- Just rz -> enter reprinting rz- -- No right sibling- Nothing -> return B.empty+ -- Otherwise proceed with the algorithm+ | otherwise = do+ -- Initial state comprises start cursor and input source+ let state_0 = (initPosition, input)+ -- Enter the top-node of a zipper for `ast'+ let comp = enter reprinting (toZipper ast)+ (out, (_, remaining)) <- runStateT comp state_0+ -- Add to the output source the remaining input source+ return (out <> remaining) --- | Given a lower-bound and upper-bound pair of Positions, split the--- | incoming Source based on the distanceF between the Position pairs-splitBySpan :: (Position, Position) -> Source -> (Source, Source)-splitBySpan (l, u) = subtext (ll, lc) (ll, lc) (ul, uc)- where (Position lc ll) = l- (Position uc ul) = u+-- | Take a refactoring and a zipper producing a stateful Source transformer with Position state.+enter :: Monad m => Reprinting m -> Zipper ast -> StateT (Position, Source) m Source+enter reprinting zipper = do+ -- Step 1: Apply a refactoring+ refactoringInfo <- lift (query reprinting zipper) -{-- Split a text.+ -- Step 2: Deal with refactored code or go to children+ output <- case refactoringInfo of+ -- No refactoring; go to children+ Nothing -> go down'+ -- A refactoring was applied+ Just r -> splice r+ -- Step 3: Enter the right sibling of the current context+ outputSib <- go right - Returns a tuple containing:- 1. the bit of input text between upper and lower bounds- 2. the remaining input text+ -- Finally append output of current context/children+ -- and right sibling+ return (output <> outputSib) - Takes:- 1. current cursor position- 2. lower bound- 3. upper bound- 4. input text--}-subtext :: (Int, Int) -> (Int, Int) -> (Int, Int) -> B.ByteString -> (B.ByteString, B.ByteString)-subtext cursor (lowerLn, lowerCol) (upperLn, upperCol) =- subtext' B.empty cursor where- subtext' acc (cursorLn, cursorCol) input+ go direction =+ case direction zipper of+ -- Go to next node if there is one+ Just zipper -> enter reprinting zipper+ -- Otherwise return the empty string+ Nothing -> return mempty - | cursorLn <= lowerLn && (cursorCol >= lowerCol ==> cursorLn < lowerLn) =- case B.uncons input of- Nothing -> (B.reverse acc, input)- Just ('\n', input') -> subtext' acc (cursorLn+1, 1) input'- Just (_, input') -> subtext' acc (cursorLn, cursorCol+1) input' - | cursorLn <= upperLn && (cursorCol >= upperCol ==> cursorLn < upperLn) =- case B.uncons input of- Nothing -> (B.reverse acc, input)- Just ('\n', input') -> subtext' (B.cons '\n' acc) (cursorLn+1, 1) input'- Just (x, input') -> subtext' (B.cons x acc) (cursorLn, cursorCol+1) input'+-- | The reprint algorithm takes a refactoring (parameteric in+-- | some monad m) and turns an arbitrary pretty-printable type 'ast'+-- | into a monadic Source transformer.+reprintSort :: (Monad m, Data ast) => Reprinting m -> ast -> Source -> m Source+reprintSort reprinting ast input+ -- If the input is empty return empty+ | Text.null input = return mempty - | otherwise =- (B.reverse acc, input)+ -- Otherwise proceed with the algorithm+ | otherwise = do+ -- Initial state comprises start cursor and input source+ let state_0 = (initPosition, input)+ -- Enter the top-node of a zipper for `ast'+ let comp = enter' reprinting (toZipper ast)+ (out, (_, remaining)) <- runStateT comp state_0+ -- Add to the output source the remaining input source+ return (out <> remaining) --- Logical implication operator.-(==>) :: Bool -> Bool -> Bool; infix 2 ==>-a ==> b = a <= b --- | Specify a refactoring type-data RefactorType = Before | After | Replace+-- | Take a refactoring and a zipper to produce a list of refactorings+enter' :: Monad m => Reprinting m -> Zipper ast+ -> StateT (Position, Source) m Source+enter' reprinting zipper = do+ -- Step 1: Get refactorings via AST zipper traversal+ rs <- lift $ getRefactorings reprinting zipper []+ -- Step 2: Do the splicing on the sorted refactorings+ srcs <- mapM splice (sortBySpan . reverse $ rs)+ return $ Text.concat srcs+ where+ sortBySpan = sortOn (\(_,_,sp) -> sp) +getRefactorings :: Monad m => Reprinting m -> Zipper ast -> [(RefactorType, Source, Span)]+ -> m [(RefactorType, Source, Span)]+getRefactorings reprinting zipper acc = do+ -- Step 1: Apply a refactoring+ refactoringInfo <- query reprinting zipper+ -- Step 2: Deal with refactored code or go to children+ acc <- case refactoringInfo of+ -- No refactoring; go to children+ Nothing -> go down' acc+ -- A refactoring was applied, add it to the accumulator+ Just r -> return (r : acc)+ -- Step 3: Enter the left sibling of the current focus+ acc <- go right acc+ -- Finally return the accumulated refactorings+ return acc++ where+ go direction acc =+ case direction zipper of+ -- Go to next node if there is one+ Just zipper -> getRefactorings reprinting zipper acc+ -- Otherwise return the empty string+ Nothing -> return acc++splice :: Monad m => (RefactorType, Source, Span) -> StateT (Position, Source) m Source+splice (typ, output, (lb, ub)) = do+ (cursor, inp) <- get+ case typ of+ Replace -> do+ -- Get soure up to start of refactored node+ let (pre, inp') = splitBySpan (cursor, lb) inp+ -- Remove source covered by refactoring+ let (_, inp'') = splitBySpan (lb, ub) inp'+ put (ub, inp'')+ return (pre <> output)+ After -> do+ -- Get source up to end of the refactored node+ let (pre, inp') = splitBySpan (cursor, ub) inp+ put (ub, inp')+ return (pre <> output)+ Before -> do+ -- Get source up to start of refactored node+ let (pre, inp') = splitBySpan (cursor, lb) inp+ -- Discard portion consumed by the refactoring+ let (post, inp'') = splitBySpan (lb, ub) inp'+ put (ub, inp'')+ return (pre <> output <> post)++-- Given a lower-bound and upper-bound pair of Positions, split the+-- incoming Source based on the distance between the Position pairs+splitBySpan :: Span -> Source -> (Source, Source)+splitBySpan (lower, upper) =+ subtext mempty lower+ where+ subtext acc cursor input+ | cursor < lower =+ case Text.uncons input of+ Nothing -> done+ Just ('\n', input') -> subtext acc (advanceLine cursor) input'+ Just (_, input') -> subtext acc (advanceCol cursor) input'+ | cursor < upper =+ case Text.uncons input of+ Nothing -> done+ Just ('\n', input') -> subtext (Text.cons '\n' acc) (advanceLine cursor) input'+ Just (x, input') -> subtext (Text.cons x acc) (advanceCol cursor) input'+ | otherwise = done+ where done = (Text.reverse acc, input)+++ -- | Infrastructure for building the reprinter "plugins" class Refactorable t where isRefactored :: t -> Maybe RefactorType- getSpan :: t -> (Position, Position)+ getSpan :: t -> Span -- | Essentially wraps the refactorable interface genReprinting :: (Monad m, Refactorable t, Typeable t)- => (t -> m Source)- -> t -> m (Maybe (RefactorType, Source, (Position, Position)))-genReprinting f z = do- case isRefactored z of+ => (t -> m Source) -> t -> m (Maybe (RefactorType, Source, Span))+genReprinting f z = case isRefactored z of Nothing -> return Nothing Just refactorType -> do output <- f z