{-# LANGUAGE ScopedTypeVariables, ExistentialQuantification, GeneralizedNewtypeDeriving, MultiParamTypeClasses #-} -- -- Copyright (C) 2007 Don Stewart - http://www.cse.unsw.edu.au/~dons -- -- -- | This module defines a common interface for syntax-awareness. 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 -- size in #codepoints 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 -- | The main type of syntax highlighters. This record type combines all -- the required functions, and is parametrized on the type of the internal -- state. -- FIXME: this is actually completetly abstrcted from sytnax HL, so the names are silly. data Highlighter cache syntax = SynHL { hlStartState :: cache -- ^ The start state for the highlighter. , hlRun :: Scanner Point Char -> Point -> cache -> cache , hlGetTree :: cache -> WindowRef -> syntax , hlFocus :: M.Map WindowRef Region -> cache -> cache -- ^ focus at a given point, and return the coresponding node. (hint -- the root can always be returned, at the cost of performance.) } data ExtHL syntax = forall cache. ExtHL (Highlighter cache syntax) data Scanner st a = Scanner { scanInit :: st, -- ^ Initial state scanLooked :: st -> Point, -- ^ How far did the scanner look to produce this intermediate state? -- The state can be reused as long as nothing changes before that point. scanEmpty :: a, -- hack :/ scanRun :: st -> [(st,a)] -- ^ Running function returns a list of results and intermediate states. -- Note: the state is the state /before/ producing the result in the second component. } 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] -- we must return the final result (because if the list is empty mkHighlighter thinks it can reuse the previous result) other 0 (x:xs) = x : other n xs other m (_:xs) = other (m-1) 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" } -- | This takes as input a scanner that returns the "full" result at -- each element in the list; perhaps in a different form for the -- purpose of incremental-lazy eval. 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" }