{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE CPP #-} {-# LANGUAGE RankNTypes #-} ----------------------------------------------------------------------------- -- -- 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) #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.String (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, #ifdef MIN_VERSION_gtk3 widgetGetWindow #else widgetGetDrawWindow #endif ) 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 = js1 "setBookmark" setBookmark' = js2 "setBookmark" insertLeft = js "insertLeft" 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 -> String -> IDEM (EditorBuffer CodeMirror) newCMBuffer mbFilename contents = do ideR <- ask liftIO $ do debugM "leksah" "newCMBuffer" scrolledWindow <- scrolledWindowNew Nothing Nothing 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 ( "" ++ "