{-# 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) ' '
else "\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 input :: 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 getUpwards :: BufferM [Int]
getUpwards getPrevious :: YiString -> BufferM [Int]
getPrevious indentBehave :: 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 _ [] = () -> BufferM ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
cycleIndentsB indentBehave :: IndentBehaviour
indentBehave indents :: [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
IncreaseCycle -> Int -> [Int] -> Int
chooseIncreaseCycle
DecreaseCycle -> Int -> [Int] -> Int
chooseDecreaseCycle
IncreaseOnly -> Int -> [Int] -> Int
chooseIncreaseOnly
DecreaseOnly -> Int -> [Int] -> Int
chooseDecreaseOnly
chooseIncreaseCycle :: Int -> [ Int ] -> Int
chooseIncreaseCycle :: Int -> [Int] -> Int
chooseIncreaseCycle currentIndent :: Int
currentIndent hints :: [Int]
hints =
[Int] -> Int
forall a. [a] -> a
head ([Int]
above [Int] -> [Int] -> [Int]
forall a. [a] -> [a] -> [a]
++ [Int]
below)
where
(below :: [Int]
below, above :: [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 currentIndent :: Int
currentIndent hints :: [Int]
hints =
[Int] -> Int
forall a. [a] -> a
last ([Int]
above [Int] -> [Int] -> [Int]
forall a. [a] -> [a] -> [a]
++ [Int]
below)
where
(below :: [Int]
below, above :: [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 currentIndent :: Int
currentIndent hints :: [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 currentIndent :: Int
currentIndent hints :: [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 (-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
== 0 Bool -> Bool -> Bool
|| (Int
indent Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== 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 input :: YiString
input =
case Int -> YiString -> Maybe YiString
getOpen 0 (YiString -> Maybe YiString) -> YiString -> Maybe YiString
forall a b. (a -> b) -> a -> b
$ YiString -> YiString
R.reverse YiString
input of
Nothing -> [Int] -> BufferM [Int]
forall (m :: * -> *) a. Monad m => a -> m a
return []
Just s :: 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 i :: Int
i s :: YiString
s = let rest :: YiString
rest = Int -> YiString -> YiString
R.drop 1 YiString
s in case YiString -> Maybe Char
R.head YiString
s of
Nothing -> Maybe YiString
forall a. Maybe a
Nothing
Just c :: Char
c
| Char -> Bool
isOpening Char
c Bool -> Bool -> Bool
&& Int
i Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== 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
- 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
+ 1) YiString
rest
| Bool
otherwise -> Int -> YiString -> Maybe YiString
getOpen Int
i YiString
rest
isOpening :: Char -> Bool
isOpening :: Char -> Bool
isOpening '(' = Bool
True
isOpening '[' = Bool
True
isOpening '{' = Bool
True
isOpening _ = Bool
False
isClosing :: Char -> Bool
isClosing :: Char -> Bool
isClosing ')' = Bool
True
isClosing ']' = Bool
True
isClosing '}' = Bool
True
isClosing _ = 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 level :: Int
level = do
IndentSettings et :: Bool
et _ sw :: Int
sw <- BufferM IndentSettings
indentSettingsB
let (q :: Int
q, r :: 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 " ")
else YiString -> BufferM YiString
forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> YiString -> YiString
R.replicate Int
q "\t" YiString -> YiString -> YiString
forall a. Semigroup a => a -> a -> a
<> Int -> YiString -> YiString
R.replicate Int
r " ")
spacingOfB :: YiString -> BufferM Int
spacingOfB :: YiString -> BufferM Int
spacingOfB text :: 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 f :: 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 dir :: 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
indentSettings newCount :: Int
newCount input :: YiString
input
| Int
newCount Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= 0 = YiString
rest
| IndentSettings -> Bool
expandTabs IndentSettings
indentSettings = Int -> Char -> YiString
R.replicateChar Int
newCount ' ' 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 (_indents :: YiString
_indents,rest :: 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) '\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) ' '
countIndent :: IndentSettings -> R.YiString -> Int
countIndent :: IndentSettings -> YiString -> Int
countIndent i :: IndentSettings
i t :: YiString
t = (Int -> Char -> Int) -> Int -> YiString -> Int
forall a. (a -> Char -> a) -> a -> YiString -> a
R.foldl' (\i' :: Int
i' c :: Char
c -> Int
i' Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Char -> Int
spacing Char
c) 0 YiString
indents
where
(indents :: YiString
indents, _) = (Char -> Bool) -> YiString -> (YiString, YiString)
R.span Char -> Bool
isSpace YiString
t
spacing :: Char -> Int
spacing '\t' = IndentSettings -> Int
tabSize IndentSettings
i
spacing _ = 1
indentString :: IndentSettings -> Int -> R.YiString -> R.YiString
indentString :: IndentSettings -> Int -> YiString -> YiString
indentString is :: IndentSettings
is numOfShifts :: Int
numOfShifts i :: 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 shiftCount :: Int
shiftCount region :: Region
region = do
IndentSettings
is <- BufferM IndentSettings
indentSettingsB
let indentFn :: R.YiString -> R.YiString
indentFn :: YiString -> YiString
indentFn line :: 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
/= "\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