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 Data.Foldable
import Data.Traversable
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, Functor, Foldable, Traversable)
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) (fmap (second f) . r)
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"
}