1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
|
\begin{code}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE CPP #-}
#if __GLASGOW_HASKELL__ >= 800
{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
#endif
module Text.RE.Tools.Edit
(
-- * Editing
-- $tutorial
Edits(..)
, Edit(..)
, LineEdit(..)
, applyEdits
, applyEdit
, applyLineEdit
-- * IsRegex
, IsRegex(..)
, SearchReplace(..)
, searchReplaceAll
, searchReplaceFirst
-- * LineNo
, LineNo(..)
, firstLine
, getLineNo
, lineNo
-- * Replace
, module Text.RE.Replace
) where
import Data.Maybe
import Prelude.Compat
import Text.RE.Replace
import Text.RE.Tools.IsRegex
import Text.RE.ZeInternals.Types.LineNo
\end{code}
\begin{code}
-- | an 'Edits' script will, for each line in the file, either perform
-- the action selected by the first RE in the list, or perform all of the
-- actions on line, arranged as a pipeline
data Edits m re s
= Select ![Edit m re s] -- ^ for each line select the first @Edit@ to match each line and edit the line with it
| Pipe ![Edit m re s] -- ^ for each line apply every edit that matches in turn to the line
-- | each Edit action specifies how the match should be processed
data Edit m re s
= Template !(SearchReplace re s)
-- ^ replace the match with this template text, substituting ${capture} as appropriate
| Function !re REContext !(LineNo->Match s->RELocation->Capture s->m (Maybe s))
-- ^ use this function to replace the 'REContext' specified captures in each line matched
| LineEdit !re !(LineNo->Matches s->m (LineEdit s))
-- ^ use this function to edit each line matched
-- | a LineEdit is the most general action that can be performed on a line
-- and is the only means of deleting a line
data LineEdit s
= NoEdit -- ^ do not edit this line but leave as is
| ReplaceWith !s -- ^ replace the line with this text (terminating newline should not be included)
| Delete -- ^ delete the this line altogether
deriving (Functor,Show)
\end{code}
\begin{code}
-- | apply an 'Edit' script to a single line
applyEdits :: (IsRegex re s,Monad m,Functor m)
=> LineNo
-> Edits m re s
-> s
-> m s
applyEdits lno ez0 s0 = case ez0 of
Select ez -> select_edit_scripts lno ez s0
Pipe ez -> pipe_edit_scripts lno ez s0
-- | apply a single edit action to a line, the function in the first argument
-- being used to add a new line onto the end of the line where appropriate;
-- the function returns @Nothing@ if no edit is to be performed on the line,
-- @Just mempty@ to delete the line
applyEdit :: (IsRegex re s,Monad m,Functor m)
=> (s->s)
-> LineNo
-> Edit m re s
-> s
-> m (Maybe s)
applyEdit anl lno edit s =
case allMatches acs of
[] -> return Nothing
_ -> fmap Just $ case edit of
Template srch_rpl -> return $ anl $ replaceAll (getTemplate srch_rpl) acs
Function _ ctx f -> anl <$> replaceAllCapturesM replaceMethods ctx (f lno) acs
LineEdit _ g -> fromMaybe (anl s) . applyLineEdit anl <$> g lno acs
where
acs = matchMany rex s
rex = case edit of
Template srch_rpl -> getSearch srch_rpl
Function rex_ _ _ -> rex_
LineEdit rex_ _ -> rex_
-- | apply a 'LineEdit' to a line, using the function in the first
-- argument to append a new line to the result; Nothing should be
-- returned if no edit is to be performed, @Just mempty@ to
-- delete the line
applyLineEdit :: Monoid s => (s->s) -> LineEdit s -> Maybe s
applyLineEdit _ NoEdit = Nothing
applyLineEdit anl (ReplaceWith s) = Just $ anl s
applyLineEdit _ Delete = Just mempty
select_edit_scripts :: (IsRegex re s,Monad m,Functor m)
=> LineNo
-> [Edit m re s]
-> s
-> m s
select_edit_scripts lno ps0 s = select ps0
where
select [] = return $ appendNewlineR s
select (edit:edits) =
applyEdit appendNewlineR lno edit s >>= maybe (select edits) return
pipe_edit_scripts :: (IsRegex re s,Monad m,Functor m)
=> LineNo
-> [Edit m re s]
-> s
-> m s
pipe_edit_scripts lno edits s0 =
appendNewlineR <$> foldr f (return s0) edits
where
f edit act = do
s <- act
fromMaybe s <$> applyEdit id lno edit s
\end{code}
\begin{code}
-- $tutorial
-- The Edit toolkit looks for REs that match a text and runs the
-- associated actions.
--
-- See the Regex Tools tutorial at http://re-tutorial-tools.regex.uk
\end{code}
|