cornelis-0.2.0.0: src/Cornelis/Subscripts.hs
module Cornelis.Subscripts where
import Cornelis.Offsets
import Cornelis.Vim (getWindowCursor, getBufferLine, replaceInterval, setWindowCursor, reportError)
import Data.Foldable (asum, foldl', for_)
import Data.Map (Map)
import qualified Data.Map as M
import Data.Maybe (fromMaybe)
import Data.Proxy
import qualified Data.Text as T
import Data.Void (Void)
import Neovim (Neovim)
import Neovim.API.Text (vim_get_current_window, window_get_buffer)
import Text.Megaparsec
type Parser = Parsec Void T.Text
data Flavor a
= Digits a
| Subscript a
| Superscript a
deriving (Eq, Ord, Show, Functor)
extract :: Flavor a -> a
extract (Digits a) = a
extract (Subscript a) = a
extract (Superscript a) = a
parseNum :: Num a => Flavor (Char, String) -> Parser (Flavor a)
parseNum f = do
r <- option id (negate <$ satisfy (== fst (extract f)) ) <*> parseDigits (fmap snd f)
pure $ r <$ f
parseDigits :: Num a => Flavor String -> Parser a
parseDigits fv = mkNum <$> takeWhile1P (Just "digit") (flip elem $ extract fv)
where
mkNum = foldl' step 0 . chunkToTokens (Proxy :: Proxy T.Text)
step a c = a * 10 + fromIntegral (digitToInt fv c)
digitToInt :: Flavor String -> Char -> Int
digitToInt fv
= fromMaybe (error "digitToInt: not a digit")
. flip lookup (zip (extract fv) [0..])
subscripts :: Flavor (Char, String)
subscripts = Subscript ('₋', "₀₁₂₃₄₅₆₇₈₉")
superscripts :: Flavor (Char, String)
superscripts = Superscript ('⁻', "⁰¹²³⁴⁵⁶⁷⁸⁹")
digits :: Flavor (Char, String)
digits = Digits ('-', "0123456789")
mkReplacement :: (Char, String) -> (Char, String) -> Map Char Char
mkReplacement (m1, s1) (m2, s2) = M.fromList $ zip (m1 : s1) (m2 : s2)
replace :: Map Char Char -> String -> String
replace m = fmap (\c -> fromMaybe c $ M.lookup c m)
parseFlavor :: Parser (Flavor Int)
parseFlavor = asum
[ parseNum digits
, parseNum superscripts
, parseNum subscripts
]
parseLine :: Parser (String, Flavor Int)
parseLine = manyTill_ anySingle $ try parseFlavor
unparse :: Flavor Int -> String
unparse (Digits n) = unparseWith (extract digits) n
unparse (Subscript n) = unparseWith (extract subscripts) n
unparse (Superscript n) = unparseWith (extract superscripts) n
unparseWith :: (Char, String) -> Int -> String
unparseWith to n =
let from = extract digits
rep = mkReplacement from to
in replace rep $ show n
applyOver :: (Int -> Int) -> Parser (T.Text, (Int, Int))
applyOver f = do
(start_str, fv) <- parseLine
let n = unparse $ fmap f fv
start = T.pack start_str
pure
( T.pack n
, (T.length start, length $ show $ extract fv)
)
incNextDigitSeq :: Neovim env ()
incNextDigitSeq = overNextDigitSeq (+1)
decNextDigitSeq :: Neovim env ()
decNextDigitSeq = overNextDigitSeq (subtract 1)
overNextDigitSeq :: (Int -> Int) -> Neovim env ()
overNextDigitSeq f = do
w <- vim_get_current_window
b <- window_get_buffer w
Pos line col <- getWindowCursor w
txt <- getBufferLine b (zeroIndex line)
let later = T.drop (fromZeroIndexed (zeroIndex col)) txt
reportError $ T.pack $ show later
for_ (parse (applyOver f) "" later) $ \(result, (before, target)) -> do
reportError result
let start_col = col .+ Offset before
end_col = start_col .+ Offset target
start_pos = Pos line start_col
end_pos = Pos line end_col
replaceInterval b (Interval start_pos end_pos) result
setWindowCursor w start_pos