{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE TypeFamilies #-} #ifdef LEKSAH_WITH_CODE_MIRROR {-# LANGUAGE RecordWildCards #-} #endif {-# LANGUAGE RankNTypes #-} {-# LANGUAGE NoImplicitPrelude #-} ----------------------------------------------------------------------------- -- -- Module : IDE.TextEditor.CodeMirror -- Copyright : 2007-2013 Juergen Nicklisch-Franken, Hamish Mackenzie -- License : GPL Nothing -- -- Maintainer : maintainer@leksah.org -- Stability : provisional -- Portability : -- -- | -- ----------------------------------------------------------------------------- module IDE.TextEditor.CodeMirror ( CodeMirror(..) #ifdef LEKSAH_WITH_CODE_MIRROR , TextEditor(..) , EditorBuffer(..) , EditorView(..) , EditorIter(..) , EditorMark(..) , EditorTag(..) , EditorTagTable(..) , newCMBuffer #endif ) where import Data.Typeable (Typeable) import Graphics.UI.Gtk (scrolledWindowSetShadowType) import Graphics.UI.Gtk.General.Enums (ShadowType(..)) import Data.Text (Text) import Text.Show (Show) import Data.Tuple (snd, fst) import Data.Function (($), (.)) import Data.Maybe (Maybe, Maybe(..)) import GHC.Base (Functor(..), Monad(..)) import Data.Int (Int) import System.IO (FilePath) import Data.List ((++)) import Data.Bool (Bool(..), not) import GHC.Real (fromIntegral, RealFrac(..)) import GHC.Num (Num(..)) import Data.Eq (Eq(..)) import GHC.Float (Double) import qualified Data.Text as T (pack) #ifdef LEKSAH_WITH_CODE_MIRROR import Control.Monad (unless) import Data.Text (pack, unpack) import IDE.TextEditor.Class (TextEditor(..)) import Graphics.UI.Gtk.WebKit.Types (WebView(..)) import Control.Monad.Reader (ReaderT(..)) import Language.Javascript.JSaddle (valToObject, (#), JSContextRef, JSObjectRef, jsg, (<#), obj, js2, js, JSM, js1, valToText, valToStr, js3, js0, MakeValueRef(..), MakeStringRef(..), JSStringRef, JSValueRef, valToBool, strToText, valToNumber, MakeObjectRef) import Control.Applicative ((<$>)) import Control.Monad.Reader.Class (MonadReader(..)) import Control.Concurrent (putMVar, newEmptyMVar, takeMVar, MVar, tryTakeMVar) import IDE.Core.Types (IDEM) import Control.Monad.IO.Class (MonadIO(..)) import Control.Monad.Trans.Class (MonadTrans(..)) import Control.Lens ((^.), IndexPreservingGetter) import Graphics.UI.Gtk.WebKit.WebView (webViewLoadUri, webViewLoadString, webViewGetMainFrame, loadFinished, webViewNew) import qualified GHCJS.CodeMirror as CM (getDataDir) import System.Glib.Signals (after, on) import Graphics.UI.Gtk.WebKit.JavaScriptCore.WebFrame (webFrameGetGlobalContext) import Text.Blaze.Html.Renderer.Text (renderHtml) import Text.Hamlet (shamlet) import Graphics.UI.Gtk (ScrolledWindow, menuPopup, menuAttachToWidget, menuNew, popupMenuSignal, eventModifier, widgetAddEvents, keyReleaseEvent, leaveNotifyEvent, motionNotifyEvent, keyPressEvent, buttonReleaseEvent, buttonPressEvent, focusInEvent, widgetGrabFocus, widgetGetParent, castToScrolledWindow, containerAdd, scrolledWindowNew, Rectangle(..), EventMask(..), Modifier(..), ContainerClass, mainIteration, castToWidget, widgetGetWindow ) import Data.Maybe (fromJust) import IDE.Core.State (onIDE, reflectIDE, leksahOrPackageDir) import Graphics.UI.Editor.Basics (Connection(..)) import System.Log.Logger (debugM) #endif data CodeMirror = CodeMirror deriving( Typeable, Show ) #ifdef LEKSAH_WITH_CODE_MIRROR data CodeMirrorState = CodeMirrorState { cmContext :: JSContextRef , cmObject :: JSObjectRef } type CM = ReaderT (WebView, CodeMirrorState) JSM webView :: CM WebView webView = fst <$> ask codeMirror :: CM JSObjectRef codeMirror = cmObject . snd <$> ask runCM :: CodeMirrorRef -> CM a -> IDEM a runCM (v, mvar) f = liftIO $ do s <- guiTakeMVar mvar runReaderT (runReaderT f (v, s)) (cmContext s) where guiTakeMVar mvar = do maybeValue <- tryTakeMVar mvar case maybeValue of Just value -> do putMVar mvar value return value Nothing -> do debugM "leksah" "looping" s <- loop mvar debugM "leksah" "done looping" return s loop mvar = do maybeValue <- tryTakeMVar mvar case maybeValue of Just value -> do putMVar mvar value return value Nothing -> do mainIteration loop mvar type CodeMirrorRef = (WebView, MVar CodeMirrorState) body = js "body" value = js "value" setSize = js2 "setSize" mode = js "mode" line = js "line" ch = js "ch" left = js "left" top = js "top" right = js "right" bottom = js "bottom" lastLine = js0 "lastLine" getRange = js2 "getRange" setValue = js1 "setValue" setBookmark' = js2 "setBookmark" find = js0 "find" from = js "from" getCursor :: (MakeValueRef a0, MakeObjectRef o) => a0 -> IndexPreservingGetter o (JSM JSValueRef) getCursor = js1 "getCursor" isClean = js0 "isClean" markText = js3 "markText" className = js "className" clearHistory = js0 "clearHistory" callUndo = js0 "undo" undo' = js "undo" callRedo = js0 "redo" redo' = js "redo" historySize = js0 "historySize" replaceRange = js3 "replaceRange" insertAt = js2 "replaceRange" replaceSelection = js1 "replaceSelection" posFromIndex = js1 "posFromIndex" lineCount = js0 "lineCount" somethingSelected = js0 "somethingSelected" setSelection = js2 "setSelection" placeCursorAt = js1 "setSelection" markClean = js0 "markClean" coordsChar = js2 "coordsChar" charCoords = js2 "charCoords" scrollIntoView = js2 "scrollIntoView" getAllMarks = js0 "getAllMarks" indexFromPos = js1 "indexFromPos" getLineText :: (MakeValueRef a0, MakeObjectRef o) => a0 -> IndexPreservingGetter o (JSM JSValueRef) getLineText = js1 "getLine" jsLength = js "length" cmIter :: CodeMirrorRef -> Int -> Int -> CM (EditorIter CodeMirror) cmIter cm l c = do lift $ do i <- obj i ^. line <# (fromIntegral l :: Double) i ^. ch <# (fromIntegral c :: Double) return $ CMIter cm i newCMBuffer :: Maybe FilePath -> Text -> IDEM (EditorBuffer CodeMirror) newCMBuffer mbFilename contents = do ideR <- ask liftIO $ do debugM "leksah" "newCMBuffer" scrolledWindow <- scrolledWindowNew Nothing Nothing scrolledWindowSetShadowType scrolledWindow ShadowIn cmWebView <- webViewNew containerAdd scrolledWindow cmWebView dataDir <- liftIO $ leksahOrPackageDir "ghcjs-codemirror" CM.getDataDir s <- newEmptyMVar cmWebView `on` loadFinished $ \ _ -> do debugM "leksah" "newCMBuffer loadFinished" cmContext <- webViewGetMainFrame cmWebView >>= webFrameGetGlobalContext let runjs f = f `runReaderT` cmContext runjs $ do document <- jsg "document" codeMirror <- jsg "CodeMirror" code <- obj code ^. value <# contents code ^. mode <# "haskell" cmObject <- codeMirror # (document ^. body, code) >>= valToObject cmObject ^. setSize "100%" "100%" liftIO $ debugM "leksah" "newCMBuffer loaded" liftIO . putMVar s $ CodeMirrorState{..} webViewLoadString cmWebView (T.pack $ "" ++ "