{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_HADDOCK show-extensions #-}
module Yi.Buffer.Indent
( autoIndentB
, cycleIndentsB
, indentAsNextB
, indentAsPreviousB
, indentAsTheMostIndentedNeighborLineB
, indentOfB
, indentOfCurrentPosB
, indentSettingsB
, indentToB
, modifyIndentB
, newlineAndIndentB
, shiftIndentOfRegionB
, tabB
) where
import Control.Monad ()
import Data.Char (isSpace)
import Data.List (nub, sort)
import Data.Monoid ((<>))
import Yi.Buffer.Basic (Direction (..))
import Yi.Buffer.HighLevel (firstNonSpaceB, getNextLineB, getNextNonBlankLineB, moveToSol, readLnB)
import Yi.Buffer.Misc
import Yi.Buffer.Region (Region (regionStart), mkRegion, modifyRegionB, readRegionB)
import Yi.Buffer.TextUnit (regionWithTwoMovesB)
import Yi.Rope (YiString)
import qualified Yi.Rope as R
import Yi.String (mapLines)
tabB :: BufferM String
tabB :: BufferM String
tabB = do
IndentSettings
indentSettings <- BufferM IndentSettings
indentSettingsB
String -> BufferM String
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> BufferM String) -> String -> BufferM String
forall a b. (a -> b) -> a -> b
$ if IndentSettings -> Bool
expandTabs IndentSettings
indentSettings
then Int -> Char -> String
forall a. Int -> a -> [a]
replicate (IndentSettings -> Int
tabSize IndentSettings
indentSettings) Char
' '
else String
"\t"
autoIndentB :: IndentBehaviour -> BufferM ()
autoIndentB :: IndentBehaviour -> BufferM ()
autoIndentB = BufferM [Int]
-> (YiString -> BufferM [Int]) -> IndentBehaviour -> BufferM ()
autoIndentHelperB BufferM [Int]
fetchPreviousIndentsB YiString -> BufferM [Int]
indentsOfString
where
indentsOfString :: YiString -> BufferM [Int]
indentsOfString :: YiString -> BufferM [Int]
indentsOfString YiString
input = do
Int
indent <- YiString -> BufferM Int
indentOfB YiString
input
[Int]
bracketHints <- YiString -> BufferM [Int]
lastOpenBracketHint YiString
input
IndentSettings
indentSettings <- BufferM IndentSettings
indentSettingsB
[Int] -> BufferM [Int]
forall (m :: * -> *) a. Monad m => a -> m a
return ([Int] -> BufferM [Int]) -> [Int] -> BufferM [Int]
forall a b. (a -> b) -> a -> b
$ Int
indent Int -> [Int] -> [Int]
forall a. a -> [a] -> [a]
: (Int
indent Int -> Int -> Int
forall a. Num a => a -> a -> a
+ IndentSettings -> Int
shiftWidth IndentSettings
indentSettings) Int -> [Int] -> [Int]
forall a. a -> [a] -> [a]
: [Int]
bracketHints
autoIndentHelperB :: BufferM [ Int ]
-> (YiString -> BufferM [ Int ])
-> IndentBehaviour
-> BufferM ()
autoIndentHelperB :: BufferM [Int]
-> (YiString -> BufferM [Int]) -> IndentBehaviour -> BufferM ()
autoIndentHelperB BufferM [Int]
getUpwards YiString -> BufferM [Int]
getPrevious IndentBehaviour
indentBehave =
do [Int]
upwardHints <- BufferM [Int] -> BufferM [Int]
forall a. BufferM a -> BufferM a
savingExcursionB BufferM [Int]
getUpwards
YiString
previousLine <- Direction -> BufferM YiString
getNextLineB Direction
Backward
[Int]
previousHints <- YiString -> BufferM [Int]
getPrevious YiString
previousLine
let allHints :: [Int]
allHints = [Int]
upwardHints [Int] -> [Int] -> [Int]
forall a. [a] -> [a] -> [a]
++ [Int]
previousHints
IndentBehaviour -> [Int] -> BufferM ()
cycleIndentsB IndentBehaviour
indentBehave [Int]
allHints
cycleIndentsB :: IndentBehaviour -> [Int] -> BufferM ()
cycleIndentsB :: IndentBehaviour -> [Int] -> BufferM ()
cycleIndentsB IndentBehaviour
_ [] = () -> BufferM ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
cycleIndentsB IndentBehaviour
indentBehave [Int]
indents =
do YiString
currentLine <- BufferM YiString
readLnB
Int
currentIndent <- YiString -> BufferM Int
indentOfB YiString
currentLine
Int -> BufferM ()
indentToB (Int -> BufferM ()) -> Int -> BufferM ()
forall a b. (a -> b) -> a -> b
$ Int -> [Int] -> Int
chooseIndent Int
currentIndent ([Int] -> [Int]
forall a. Ord a => [a] -> [a]
sort ([Int] -> [Int]) -> [Int] -> [Int]
forall a b. (a -> b) -> a -> b
$ [Int] -> [Int]
forall a. Eq a => [a] -> [a]
nub [Int]
indents)
where
chooseIndent :: Int -> [ Int ] -> Int
chooseIndent :: Int -> [Int] -> Int
chooseIndent =
case IndentBehaviour
indentBehave of
IndentBehaviour
IncreaseCycle -> Int -> [Int] -> Int
chooseIncreaseCycle
IndentBehaviour
DecreaseCycle -> Int -> [Int] -> Int
chooseDecreaseCycle
IndentBehaviour
IncreaseOnly -> Int -> [Int] -> Int
chooseIncreaseOnly
IndentBehaviour
DecreaseOnly -> Int -> [Int] -> Int
chooseDecreaseOnly
chooseIncreaseCycle :: Int -> [ Int ] -> Int
chooseIncreaseCycle :: Int -> [Int] -> Int
chooseIncreaseCycle Int
currentIndent [Int]
hints =
[Int] -> Int
forall a. [a] -> a
head ([Int]
above [Int] -> [Int] -> [Int]
forall a. [a] -> [a] -> [a]
++ [Int]
below)
where
([Int]
below, [Int]
above) = (Int -> Bool) -> [Int] -> ([Int], [Int])
forall a. (a -> Bool) -> [a] -> ([a], [a])
span (Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
currentIndent) [Int]
hints
chooseDecreaseCycle :: Int -> [ Int ] -> Int
chooseDecreaseCycle :: Int -> [Int] -> Int
chooseDecreaseCycle Int
currentIndent [Int]
hints =
[Int] -> Int
forall a. [a] -> a
last ([Int]
above [Int] -> [Int] -> [Int]
forall a. [a] -> [a] -> [a]
++ [Int]
below)
where
([Int]
below, [Int]
above) = (Int -> Bool) -> [Int] -> ([Int], [Int])
forall a. (a -> Bool) -> [a] -> ([a], [a])
span (Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
currentIndent) [Int]
hints
chooseIncreaseOnly :: Int -> [ Int ] -> Int
chooseIncreaseOnly :: Int -> [Int] -> Int
chooseIncreaseOnly Int
currentIndent [Int]
hints =
[Int] -> Int
forall a. [a] -> a
head ([Int] -> Int) -> [Int] -> Int
forall a b. (a -> b) -> a -> b
$ (Int -> Bool) -> [Int] -> [Int]
forall a. (a -> Bool) -> [a] -> [a]
filter (Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
currentIndent) [Int]
hints [Int] -> [Int] -> [Int]
forall a. [a] -> [a] -> [a]
++ [ Int
currentIndent ]
chooseDecreaseOnly :: Int -> [ Int ] -> Int
chooseDecreaseOnly :: Int -> [Int] -> Int
chooseDecreaseOnly Int
currentIndent [Int]
hints =
[Int] -> Int
forall a. [a] -> a
last ([Int] -> Int) -> [Int] -> Int
forall a b. (a -> b) -> a -> b
$ Int
currentIndent Int -> [Int] -> [Int]
forall a. a -> [a] -> [a]
: (Int -> Bool) -> [Int] -> [Int]
forall a. (a -> Bool) -> [a] -> [a]
filter (Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
currentIndent) [Int]
hints
fetchPreviousIndentsB :: BufferM [Int]
fetchPreviousIndentsB :: BufferM [Int]
fetchPreviousIndentsB = do
Int
moveOffset <- Int -> BufferM Int
lineMoveRel (-Int
1)
YiString
line <- BufferM YiString
readLnB
Int
indent <- YiString -> BufferM Int
indentOfB YiString
line
if Int
moveOffset Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 Bool -> Bool -> Bool
|| (Int
indent Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 Bool -> Bool -> Bool
&& (Char -> Bool) -> YiString -> Bool
R.any (Bool -> Bool
not (Bool -> Bool) -> (Char -> Bool) -> Char -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isSpace) YiString
line)
then [Int] -> BufferM [Int]
forall (m :: * -> *) a. Monad m => a -> m a
return [ Int
indent ]
else (Int
indent Int -> [Int] -> [Int]
forall a. a -> [a] -> [a]
:) ([Int] -> [Int]) -> BufferM [Int] -> BufferM [Int]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> BufferM [Int]
fetchPreviousIndentsB
lastOpenBracketHint :: YiString -> BufferM [ Int ]
lastOpenBracketHint :: YiString -> BufferM [Int]
lastOpenBracketHint YiString
input =
case Int -> YiString -> Maybe YiString
getOpen Int
0 (YiString -> Maybe YiString) -> YiString -> Maybe YiString
forall a b. (a -> b) -> a -> b
$ YiString -> YiString
R.reverse YiString
input of
Maybe YiString
Nothing -> [Int] -> BufferM [Int]
forall (m :: * -> *) a. Monad m => a -> m a
return []
Just YiString
s -> Int -> [Int]
forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> [Int]) -> BufferM Int -> BufferM [Int]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> YiString -> BufferM Int
spacingOfB YiString
s
where
getOpen :: Int -> YiString -> Maybe YiString
getOpen :: Int -> YiString -> Maybe YiString
getOpen Int
i YiString
s = let rest :: YiString
rest = Int -> YiString -> YiString
R.drop Int
1 YiString
s in case YiString -> Maybe Char
R.head YiString
s of
Maybe Char
Nothing -> Maybe YiString
forall a. Maybe a
Nothing
Just Char
c
| Char -> Bool
isOpening Char
c Bool -> Bool -> Bool
&& Int
i Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 -> YiString -> Maybe YiString
forall a. a -> Maybe a
Just YiString
rest
| Char -> Bool
isOpening Char
c -> Int -> YiString -> Maybe YiString
getOpen (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) YiString
rest
| Char -> Bool
isClosing Char
c -> Int -> YiString -> Maybe YiString
getOpen (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) YiString
rest
| Bool
otherwise -> Int -> YiString -> Maybe YiString
getOpen Int
i YiString
rest
isOpening :: Char -> Bool
isOpening :: Char -> Bool
isOpening Char
'(' = Bool
True
isOpening Char
'[' = Bool
True
isOpening Char
'{' = Bool
True
isOpening Char
_ = Bool
False
isClosing :: Char -> Bool
isClosing :: Char -> Bool
isClosing Char
')' = Bool
True
isClosing Char
']' = Bool
True
isClosing Char
'}' = Bool
True
isClosing Char
_ = Bool
False
indentOfB :: YiString -> BufferM Int
indentOfB :: YiString -> BufferM Int
indentOfB = YiString -> BufferM Int
spacingOfB (YiString -> BufferM Int)
-> (YiString -> YiString) -> YiString -> BufferM Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> YiString -> YiString
R.takeWhile Char -> Bool
isSpace
makeIndentString :: Int -> BufferM YiString
makeIndentString :: Int -> BufferM YiString
makeIndentString Int
level = do
IndentSettings Bool
et Int
_ Int
sw <- BufferM IndentSettings
indentSettingsB
let (Int
q, Int
r) = Int
level Int -> Int -> (Int, Int)
forall a. Integral a => a -> a -> (a, a)
`quotRem` Int
sw
if Bool
et
then YiString -> BufferM YiString
forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> YiString -> YiString
R.replicate Int
level YiString
" ")
else YiString -> BufferM YiString
forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> YiString -> YiString
R.replicate Int
q YiString
"\t" YiString -> YiString -> YiString
forall a. Semigroup a => a -> a -> a
<> Int -> YiString -> YiString
R.replicate Int
r YiString
" ")
spacingOfB :: YiString -> BufferM Int
spacingOfB :: YiString -> BufferM Int
spacingOfB YiString
text = do
IndentSettings
indentSettings <- BufferM IndentSettings
indentSettingsB
Int -> BufferM Int
forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> BufferM Int) -> Int -> BufferM Int
forall a b. (a -> b) -> a -> b
$ IndentSettings -> YiString -> Int
countIndent IndentSettings
indentSettings YiString
text
indentToB :: Int -> BufferM ()
indentToB :: Int -> BufferM ()
indentToB = (Int -> Int) -> BufferM ()
modifyIndentB ((Int -> Int) -> BufferM ())
-> (Int -> Int -> Int) -> Int -> BufferM ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Int -> Int
forall a b. a -> b -> a
const
modifyIndentB :: (Int -> Int) -> BufferM ()
modifyIndentB :: (Int -> Int) -> BufferM ()
modifyIndentB Int -> Int
f = do
Region
leadingSpaces <- BufferM () -> BufferM () -> BufferM Region
forall a b. BufferM a -> BufferM b -> BufferM Region
regionWithTwoMovesB BufferM ()
moveToSol BufferM ()
firstNonSpaceB
YiString
newLeadinSpaces <-
Region -> BufferM YiString
readRegionB Region
leadingSpaces BufferM YiString -> (YiString -> BufferM Int) -> BufferM Int
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= YiString -> BufferM Int
indentOfB BufferM Int -> (Int -> BufferM YiString) -> BufferM YiString
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Int -> BufferM YiString
makeIndentString (Int -> BufferM YiString)
-> (Int -> Int) -> Int -> BufferM YiString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Int
f
(YiString -> YiString) -> Region -> BufferM ()
modifyRegionB (YiString -> YiString -> YiString
forall a b. a -> b -> a
const YiString
newLeadinSpaces) Region
leadingSpaces
indentAsPreviousB :: BufferM ()
indentAsPreviousB :: BufferM ()
indentAsPreviousB = Direction -> BufferM ()
indentAsNeighborLineB Direction
Backward
indentAsNextB :: BufferM ()
indentAsNextB :: BufferM ()
indentAsNextB = Direction -> BufferM ()
indentAsNeighborLineB Direction
Forward
indentAsTheMostIndentedNeighborLineB :: BufferM ()
indentAsTheMostIndentedNeighborLineB :: BufferM ()
indentAsTheMostIndentedNeighborLineB = do
YiString
prevLine <- Direction -> BufferM YiString
getNextNonBlankLineB Direction
Backward
YiString
nextLine <- Direction -> BufferM YiString
getNextNonBlankLineB Direction
Forward
Int
prevIndent <- YiString -> BufferM Int
indentOfB YiString
prevLine
Int
nextIndent <- YiString -> BufferM Int
indentOfB YiString
nextLine
Int -> BufferM ()
indentToB (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
prevIndent Int
nextIndent)
indentAsNeighborLineB :: Direction -> BufferM ()
indentAsNeighborLineB :: Direction -> BufferM ()
indentAsNeighborLineB Direction
dir = do
YiString
otherLine <- Direction -> BufferM YiString
getNextNonBlankLineB Direction
dir
Int
otherIndent <- YiString -> BufferM Int
indentOfB YiString
otherLine
Int -> BufferM ()
indentToB Int
otherIndent
newlineAndIndentB :: BufferM ()
newlineAndIndentB :: BufferM ()
newlineAndIndentB = BufferM ()
newlineB BufferM () -> BufferM () -> BufferM ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> BufferM ()
indentAsPreviousB
rePadString :: IndentSettings -> Int -> R.YiString -> R.YiString
rePadString :: IndentSettings -> Int -> YiString -> YiString
rePadString IndentSettings
indentSettings Int
newCount YiString
input
| Int
newCount Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0 = YiString
rest
| IndentSettings -> Bool
expandTabs IndentSettings
indentSettings = Int -> Char -> YiString
R.replicateChar Int
newCount Char
' ' YiString -> YiString -> YiString
forall a. Semigroup a => a -> a -> a
<> YiString
rest
| Bool
otherwise = YiString
tabs YiString -> YiString -> YiString
forall a. Semigroup a => a -> a -> a
<> YiString
spaces YiString -> YiString -> YiString
forall a. Semigroup a => a -> a -> a
<> YiString
rest
where (YiString
_indents,YiString
rest) = (Char -> Bool) -> YiString -> (YiString, YiString)
R.span Char -> Bool
isSpace YiString
input
tabs :: YiString
tabs = Int -> Char -> YiString
R.replicateChar (Int
newCount Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` IndentSettings -> Int
tabSize IndentSettings
indentSettings) Char
'\t'
spaces :: YiString
spaces = Int -> Char -> YiString
R.replicateChar (Int
newCount Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` IndentSettings -> Int
tabSize IndentSettings
indentSettings) Char
' '
countIndent :: IndentSettings -> R.YiString -> Int
countIndent :: IndentSettings -> YiString -> Int
countIndent IndentSettings
i YiString
t = (Int -> Char -> Int) -> Int -> YiString -> Int
forall a. (a -> Char -> a) -> a -> YiString -> a
R.foldl' (\Int
i' Char
c -> Int
i' Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Char -> Int
spacing Char
c) Int
0 YiString
indents
where
(YiString
indents, YiString
_) = (Char -> Bool) -> YiString -> (YiString, YiString)
R.span Char -> Bool
isSpace YiString
t
spacing :: Char -> Int
spacing Char
'\t' = IndentSettings -> Int
tabSize IndentSettings
i
spacing Char
_ = Int
1
indentString :: IndentSettings -> Int -> R.YiString -> R.YiString
indentString :: IndentSettings -> Int -> YiString -> YiString
indentString IndentSettings
is Int
numOfShifts YiString
i = IndentSettings -> Int -> YiString -> YiString
rePadString IndentSettings
is Int
newCount YiString
i
where
newCount :: Int
newCount = IndentSettings -> YiString -> Int
countIndent IndentSettings
is YiString
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ (IndentSettings -> Int
shiftWidth IndentSettings
is Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
numOfShifts)
shiftIndentOfRegionB :: Int -> Region -> BufferM ()
shiftIndentOfRegionB :: Int -> Region -> BufferM ()
shiftIndentOfRegionB Int
shiftCount Region
region = do
IndentSettings
is <- BufferM IndentSettings
indentSettingsB
let indentFn :: R.YiString -> R.YiString
indentFn :: YiString -> YiString
indentFn YiString
line = if Bool -> Bool
not (YiString -> Bool
R.null YiString
line) Bool -> Bool -> Bool
&& YiString
line YiString -> YiString -> Bool
forall a. Eq a => a -> a -> Bool
/= YiString
"\n"
then IndentSettings -> Int -> YiString -> YiString
indentString IndentSettings
is Int
shiftCount YiString
line
else YiString
line
(YiString -> YiString) -> Region -> BufferM ()
modifyRegionB ((YiString -> YiString) -> YiString -> YiString
mapLines YiString -> YiString
indentFn) Region
region
Point -> BufferM ()
moveTo (Point -> BufferM ()) -> Point -> BufferM ()
forall a b. (a -> b) -> a -> b
$ Region -> Point
regionStart Region
region
BufferM ()
firstNonSpaceB
indentOfCurrentPosB :: BufferM Int
indentOfCurrentPosB :: BufferM Int
indentOfCurrentPosB = do
Point
p <- BufferM Point
pointB
BufferM ()
moveToSol
Point
sol <- BufferM Point
pointB
Point -> BufferM ()
moveTo Point
p
let region :: Region
region = Point -> Point -> Region
mkRegion Point
p Point
sol
Region -> BufferM YiString
readRegionB Region
region BufferM YiString -> (YiString -> BufferM Int) -> BufferM Int
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= YiString -> BufferM Int
spacingOfB