module Yi.Syntax
( Highlighter ( .. )
, Cache
, Scanner (..)
, ExtHL ( .. )
, noHighlighter, mkHighlighter, skipScanner, emptyFileScan
, Point(..), Size(..), Length, Stroke
, Span(..)
)
where
import qualified Data.Map as M
import Control.Arrow
import Yi.Style
import Yi.Prelude
import Prelude ()
import Data.List (takeWhile)
import Yi.Buffer.Basic
import Yi.Region
type Length = Int
type Stroke = Span StyleName
data Span a = Span {spanBegin :: !Point, spanContents :: !a, spanEnd :: !Point}
deriving Show
instance Traversable Span where
traverse f (Span l a r) = (\a' -> Span l a' r) <$> f a
instance Foldable Span where foldMap = foldMapDefault
instance Functor Span where fmap = fmapDefault
data Highlighter cache syntax =
SynHL { hlStartState :: cache
, hlRun :: Scanner Point Char -> Point -> cache -> cache
, hlGetTree :: cache -> WindowRef -> syntax
, hlFocus :: M.Map WindowRef Region -> cache -> cache
}
data ExtHL syntax = forall cache. ExtHL (Highlighter cache syntax)
data Scanner st a = Scanner {
scanInit :: st,
scanLooked :: st -> Point,
scanEmpty :: a,
scanRun :: st -> [(st,a)]
}
skipScanner :: Int -> Scanner st a -> Scanner st a
skipScanner n (Scanner i l e r) = Scanner i l e (other 0 . r)
where other _ [] = []
other _ [x] = [x]
other 0 (x:xs) = x : other n xs
other m (_:xs) = other (m1) xs
instance Functor (Scanner st) where
fmap f (Scanner i l e r) = Scanner i l (f e) (\st -> fmap (second f) (r st))
data Cache state result = Cache [state] result
emptyFileScan :: Scanner Point Char
emptyFileScan = Scanner { scanInit = 0, scanRun = const [], scanLooked = id, scanEmpty = error "emptyFileScan: no scanEmpty" }
mkHighlighter :: forall state result. Show state =>
(Scanner Point Char -> Scanner state result) ->
Highlighter (Cache state result) result
mkHighlighter scanner =
Yi.Syntax.SynHL
{ hlStartState = Cache [] emptyResult
, hlRun = updateCache
, hlGetTree = \(Cache _ result) _windowRef -> result
, hlFocus = \_ c -> c
}
where startState :: state
startState = scanInit (scanner emptyFileScan)
emptyResult = scanEmpty (scanner emptyFileScan)
updateCache :: Scanner Point Char -> Point -> Cache state result -> Cache state result
updateCache newFileScan dirtyOffset (Cache cachedStates oldResult) = Cache newCachedStates newResult
where newScan = scanner newFileScan
reused :: [state]
reused = takeWhile ((< dirtyOffset) . scanLooked (scanner newFileScan)) cachedStates
resumeState :: state
resumeState = if null reused then startState else last reused
newCachedStates = reused ++ fmap fst recomputed
recomputed = scanRun newScan resumeState
newResult :: result
newResult = if null recomputed then oldResult else snd $ head $ recomputed
noHighlighter :: Highlighter () syntax
noHighlighter = SynHL {hlStartState = (),
hlRun = \_ _ a -> a,
hlFocus = \_ c -> c,
hlGetTree = \ _ -> error "noHighlighter: tried to use syntax"
}