module Highlight
( highlightError
, highlight
, underline
, bold
, italic
, parenthesize
, strikethrough
, inverse
, getColor
) where
highlightError :: (Int, Int) -> (Int, Int) -> String -> String
highlightError :: (Int, Int) -> (Int, Int) -> String -> String
highlightError (Int
sLine, Int
sCol) (Int
eLine, Int
eCol) String
content =
(Int, Int)
-> (Int, Int) -> String -> (String -> String) -> String -> String
highlight (Int
sLine, Int
sCol) (Int
eLine, Int
eCol) String
"red" String -> String
underline String
content
highlight :: (Int, Int) -> (Int, Int) -> String -> (String -> String) -> String -> String
highlight :: (Int, Int)
-> (Int, Int) -> String -> (String -> String) -> String -> String
highlight sPos :: (Int, Int)
sPos@(Int
sLine, Int
sCol) ePos :: (Int, Int)
ePos@(Int
eLine, Int
eCol) String
color String -> String
effect String
content =
Bool -> String -> String -> String
forall a. Bool -> String -> a -> a
assert ((Int, Int) -> (Int, Int) -> Bool
isInBounds (Int, Int)
sPos (Int, Int)
ePos) String
"Start position must be before or equal to end position" (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$
let ([(Int, String)]
lineIndices, [Int]
lineNumbers) = String -> Int -> Int -> ([(Int, String)], [Int])
targetLines String
content Int
sLine Int
eLine
displayText :: String
displayText = [(Int, String)]
-> [Int]
-> (Int, Int)
-> (Int, Int)
-> String
-> (String -> String)
-> String
buildDisplayText [(Int, String)]
lineIndices [Int]
lineNumbers (Int, Int)
sPos (Int, Int)
ePos String
color String -> String
effect
in String
displayText
targetLines :: String -> Int -> Int -> ([(Int, String)], [Int])
targetLines :: String -> Int -> Int -> ([(Int, String)], [Int])
targetLines String
content Int
sLine Int
eLine =
let
numberedLines :: [(Int, String)]
numberedLines = [Int] -> [String] -> [(Int, String)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Int
1..] ([String] -> [(Int, String)]) -> [String] -> [(Int, String)]
forall a b. (a -> b) -> a -> b
$ String -> [String]
lines String
content
intervalLines :: [(Int, String)]
intervalLines = ((Int, String) -> Bool) -> [(Int, String)] -> [(Int, String)]
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (\(Int
lineNum, String
_) -> Int
lineNum Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
eLine) ([(Int, String)] -> [(Int, String)])
-> [(Int, String)] -> [(Int, String)]
forall a b. (a -> b) -> a -> b
$
((Int, String) -> Bool) -> [(Int, String)] -> [(Int, String)]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (\(Int
lineNum, String
_) -> Int
lineNum Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
sLine) [(Int, String)]
numberedLines
indices :: [Int]
indices = (Int -> (Int, String) -> Int) -> Int -> [(Int, String)] -> [Int]
forall b a. (b -> a -> b) -> b -> [a] -> [b]
scanl (\Int
accIndex (Int
_, String
line) -> Int
accIndex Int -> Int -> Int
forall a. Num a => a -> a -> a
+ String -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
line Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Int
0 [(Int, String)]
intervalLines
numbers :: [Int]
numbers = ((Int, String) -> Int) -> [(Int, String)] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (Int, String) -> Int
forall a b. (a, b) -> a
fst [(Int, String)]
intervalLines
contents :: [String]
contents = ((Int, String) -> String) -> [(Int, String)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Int, String) -> String
forall a b. (a, b) -> b
snd [(Int, String)]
intervalLines
indexedLines :: [(Int, String)]
indexedLines = [Int] -> [String] -> [(Int, String)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Int]
indices [String]
contents
in ([(Int, String)]
indexedLines, [Int]
numbers)
buildDisplayText :: [(Int, String)] -> [Int] -> (Int, Int) -> (Int, Int) -> String -> (String -> String) -> String
buildDisplayText :: [(Int, String)]
-> [Int]
-> (Int, Int)
-> (Int, Int)
-> String
-> (String -> String)
-> String
buildDisplayText [(Int, String)]
indices [Int]
numbers (Int
sLine, Int
sCol) (Int
eLine, Int
eCol) String
colorStr String -> String
effect =
let maxNumLineWidth :: Int
maxNumLineWidth = String -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (String -> Int) -> String -> Int
forall a b. (a -> b) -> a -> b
$ Int -> String
forall a. Show a => a -> String
show (Int -> String) -> Int -> String
forall a b. (a -> b) -> a -> b
$ [Int] -> Int
forall a. Ord a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum [Int]
numbers
formatLineNum :: a -> String
formatLineNum a
n = Int -> String -> String
pad Int
maxNumLineWidth (a -> String
forall a. Show a => a -> String
show a
n) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" | "
color :: String
color = String -> String
getColor String
colorStr
reset :: String
reset = String
"\x1b[0m"
col :: a -> a -> a -> a -> a
col a
num a
line a
col a
fallback = if a
num a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
line then a
col a -> a -> a
forall a. Num a => a -> a -> a
- a
1 else a
fallback
highlightLine :: (a, String) -> Int -> String
highlightLine (a
start, String
line) Int
num =
let (String
before, String
rest) = Int -> String -> (String, String)
forall a. Int -> [a] -> ([a], [a])
splitAt (Int -> Int -> Int -> Int -> Int
forall {a} {a}. (Eq a, Num a) => a -> a -> a -> a -> a
col Int
num Int
sLine Int
sCol Int
0) String
line
(String
target, String
after) = Int -> String -> (String, String)
forall a. Int -> [a] -> ([a], [a])
splitAt (Int -> Int -> Int -> Int -> Int
forall {a} {a}. (Eq a, Num a) => a -> a -> a -> a -> a
col Int
num Int
eLine Int
eCol (String -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
line) Int -> Int -> Int
forall a. Num a => a -> a -> a
- String -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
before) String
rest
highlighted :: String
highlighted = String
color String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
effect String
target String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
reset
in Int -> String
forall a. Show a => a -> String
formatLineNum Int
num String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
before String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
highlighted String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
after
in [String] -> String
unlines ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ ((Int, String) -> Int -> String)
-> [(Int, String)] -> [Int] -> [String]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (Int, String) -> Int -> String
forall {a}. (a, String) -> Int -> String
highlightLine [(Int, String)]
indices [Int]
numbers
pad :: Int
-> String
-> String
pad :: Int -> String -> String
pad Int
len String
txt = Int -> Char -> String
forall a. Int -> a -> [a]
replicate (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max (Int
len Int -> Int -> Int
forall a. Num a => a -> a -> a
- String -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
txt) Int
0) Char
' ' String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
txt
isInBounds :: (Int, Int) -> (Int, Int) -> Bool
isInBounds :: (Int, Int) -> (Int, Int) -> Bool
isInBounds (Int
sLine, Int
sCol) (Int
eLine, Int
eCol) =
Int
sLine Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
eLine Bool -> Bool -> Bool
|| (Int
sLine Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
eLine Bool -> Bool -> Bool
&& Int
sCol Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
eCol)
assert :: Bool
-> String
-> a
-> a
assert :: forall a. Bool -> String -> a -> a
assert Bool
True String
_ a
x = a
x
assert Bool
False String
msg a
_ = String -> a
forall a. HasCallStack => String -> a
error String
msg
getColor :: String
-> String
getColor :: String -> String
getColor String
color = case String
color of
String
"red" -> String
"\x1b[31m"
String
"green" -> String
"\x1b[32m"
String
"yellow" -> String
"\x1b[33m"
String
"blue" -> String
"\x1b[34m"
String
"magenta" -> String
"\x1b[35m"
String
"cyan" -> String
"\x1b[36m"
String
"white" -> String
"\x1b[37m"
String
_ -> String
"\x1b[0m"
underline :: String
-> String
underline :: String -> String
underline String
text = String
"\x1b[4m" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
text String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\x1b[24m"
bold :: String
-> String
bold :: String -> String
bold String
text = String
"\x1b[1m" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
text String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\x1b[22m"
italic :: String
-> String
italic :: String -> String
italic String
text = String
"\x1b[3m" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
text String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\x1b[23m"
parenthesize :: String
-> String
parenthesize :: String -> String
parenthesize String
text = String
"(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
text String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
")"
strikethrough :: String
-> String
strikethrough :: String -> String
strikethrough String
text = String
"\x1b[9m" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
text String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\x1b[29m"
inverse :: String
-> String
inverse :: String -> String
inverse String
text = String
"\x1b[7m" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
text String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\x1b[27m"