module Nbparts.Util.Text where
import Control.Monad qualified as Monad
import Data.List qualified as List
import Data.Ord qualified as Ord
import Data.Text (Text)
import Data.Text qualified as Text
lineColToIndex :: [Text] -> Int -> Int -> Maybe Int
lineColToIndex :: [Text] -> Int -> Int -> Maybe Int
lineColToIndex [Text]
textLines Int
line Int
col
| Int
line Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0 Bool -> Bool -> Bool
|| Int
col Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0 = Maybe Int
forall a. Maybe a
Nothing
| Bool
otherwise = Int -> Int -> [Text] -> Maybe Int
go Int
1 Int
0 [Text]
textLines
where
go :: Int -> Int -> [Text] -> Maybe Int
go :: Int -> Int -> [Text] -> Maybe Int
go Int
_ Int
_ [] = Maybe Int
forall a. Maybe a
Nothing
go Int
currentLine Int
charsBefore (Text
l : [Text]
ls)
| Int
currentLine Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
line =
if Int
col Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Text -> Int
Text.length Text
l Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1
then Int -> Maybe Int
forall a. a -> Maybe a
Just (Int -> Maybe Int) -> Int -> Maybe Int
forall a b. (a -> b) -> a -> b
$ Int
charsBefore Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
col Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1
else Maybe Int
forall a. Maybe a
Nothing
| Bool
otherwise =
Int -> Int -> [Text] -> Maybe Int
go (Int
currentLine Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (Int
charsBefore Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Text -> Int
Text.length Text
l Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) [Text]
ls
replaceSlices :: Text -> [((Int, Int), Text)] -> Maybe Text
replaceSlices :: Text -> [((Int, Int), Text)] -> Maybe Text
replaceSlices Text
input [((Int, Int), Text)]
replacements = Int -> [((Int, Int), Text)] -> Maybe Text
go Int
0 [((Int, Int), Text)]
sortedReplacements
where
sortedReplacements :: [((Int, Int), Text)]
sortedReplacements = (((Int, Int), Text) -> ((Int, Int), Text) -> Ordering)
-> [((Int, Int), Text)] -> [((Int, Int), Text)]
forall a. (a -> a -> Ordering) -> [a] -> [a]
List.sortBy ((((Int, Int), Text) -> (Int, Int))
-> ((Int, Int), Text) -> ((Int, Int), Text) -> Ordering
forall a b. Ord a => (b -> a) -> b -> b -> Ordering
Ord.comparing ((Int, Int), Text) -> (Int, Int)
forall a b. (a, b) -> a
fst) [((Int, Int), Text)]
replacements
go :: Int -> [((Int, Int), Text)] -> Maybe Text
go :: Int -> [((Int, Int), Text)] -> Maybe Text
go Int
pos [] = Text -> Maybe Text
forall a. a -> Maybe a
Just (Text -> Maybe Text) -> Text -> Maybe Text
forall a b. (a -> b) -> a -> b
$ Int -> Text -> Text
Text.drop Int
pos Text
input
go Int
pos (((Int
start, Int
end), Text
replacement) : [((Int, Int), Text)]
rs) = do
Bool -> Maybe () -> Maybe ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
Monad.unless
( Int
start Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0
Bool -> Bool -> Bool
&& Int
end Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0
Bool -> Bool -> Bool
&& Int
start Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
end
Bool -> Bool -> Bool
&& Int
start Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
pos
)
Maybe ()
forall a. Maybe a
Nothing
let current :: Text
current = Int -> Text -> Text
Text.drop Int
pos Text
input
relStart :: Int
relStart = Int
start Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
pos
relEnd :: Int
relEnd = Int
end Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
pos
let (Text
before, Text
remainder) = Int -> Text -> (Text, Text)
Text.splitAt Int
relStart Text
current
let replaced :: Text
replaced = Int -> Text -> Text
Text.take (Int
relEnd Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
relStart) Text
remainder
Bool -> Maybe () -> Maybe ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
Monad.when (Text -> Int
Text.length Text
replaced Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
relEnd Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
relStart) Maybe ()
forall a. Maybe a
Nothing
Text
after <- Int -> [((Int, Int), Text)] -> Maybe Text
go Int
end [((Int, Int), Text)]
rs
Text -> Maybe Text
forall a. a -> Maybe a
Just (Text -> Maybe Text) -> Text -> Maybe Text
forall a b. (a -> b) -> a -> b
$ Text
before Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
replacement Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
after
splitKeepNewlines :: Text -> [Text]
splitKeepNewlines :: Text -> [Text]
splitKeepNewlines Text
txt
| Text -> Bool
Text.null Text
txt = []
| Bool
otherwise =
let (Text
before, Text
rest) = (Char -> Bool) -> Text -> (Text, Text)
Text.break (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'\n') Text
txt
in case Text -> Maybe (Char, Text)
Text.uncons Text
rest of
Just (Char
'\n', Text
rest') -> (Text
before Text -> Char -> Text
`Text.snoc` Char
'\n') Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
: Text -> [Text]
splitKeepNewlines Text
rest'
Maybe (Char, Text)
_ -> [Text
before]
findSliceBetween :: Int -> Int -> Text -> Text -> Maybe (Int, Int)
findSliceBetween :: Int -> Int -> Text -> Text -> Maybe (Int, Int)
findSliceBetween Int
searchStart Int
searchEnd Text
haystack = Int -> Text -> Text -> Maybe (Int, Int)
findSliceFrom Int
searchStart (Int -> Text -> Text
Text.take Int
searchEnd Text
haystack)
findSliceFrom :: Int -> Text -> Text -> Maybe (Int, Int)
findSliceFrom :: Int -> Text -> Text -> Maybe (Int, Int)
findSliceFrom Int
searchStart Text
haystack Text
needle = do
(Int
startIdx, Int
endIdx) <- Text -> Text -> Maybe (Int, Int)
findSlice (Int -> Text -> Text
Text.drop Int
searchStart Text
haystack) Text
needle
(Int, Int) -> Maybe (Int, Int)
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Int
startIdx Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
searchStart, Int
endIdx Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
searchStart)
findSlice :: Text -> Text -> Maybe (Int, Int)
findSlice :: Text -> Text -> Maybe (Int, Int)
findSlice Text
haystack Text
needle = do
Int
startIdx <- Text -> Text -> Maybe Int
findSliceStart Text
haystack Text
needle
let endIdx :: Int
endIdx = Int
startIdx Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Text -> Int
Text.length Text
needle
(Int, Int) -> Maybe (Int, Int)
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Int
startIdx, Int
endIdx)
findSliceStart :: Text -> Text -> Maybe Int
findSliceStart :: Text -> Text -> Maybe Int
findSliceStart Text
haystack Text
needle =
if Text -> Bool
Text.null Text
remaining
then Maybe Int
forall a. Maybe a
Nothing
else Int -> Maybe Int
forall a. a -> Maybe a
Just (Int -> Maybe Int) -> Int -> Maybe Int
forall a b. (a -> b) -> a -> b
$ Text -> Int
Text.length Text
prefix
where
(Text
prefix, Text
remaining) = HasCallStack => Text -> Text -> (Text, Text)
Text -> Text -> (Text, Text)
Text.breakOn Text
needle Text
haystack