leksah-0.16.2.2: src/IDE/Pane/Variables.hs
{-# LANGUAGE FlexibleInstances, TypeSynonymInstances,
MultiParamTypeClasses, DeriveDataTypeable, OverloadedStrings #-}
-----------------------------------------------------------------------------
--
-- Module : IDE.Pane.Variables
-- Copyright : 2007-2011 Juergen Nicklisch-Franken, Hamish Mackenzie
-- License : GPL
--
-- Maintainer : maintainer@leksah.org
-- Stability : provisional
-- Portability :
--
-- |
--
-----------------------------------------------------------------------------
module IDE.Pane.Variables (
IDEVariables
, VariablesState
, showVariables
, fillVariablesList
, fillVariablesListQuiet
) where
import Data.Typeable (Typeable(..))
import IDE.Core.State
import IDE.Package (tryDebug, tryDebugQuiet)
import IDE.Debug (debugCommand')
import IDE.Utils.Tool (ToolOutput(..))
import Text.ParserCombinators.Parsec
(anyChar,
lookAhead,
eof,
(<|>),
try,
(<?>),
char,
noneOf,
many,
CharParser(..),
parse)
import qualified Text.ParserCombinators.Parsec.Token as P
(whiteSpace, symbol, makeTokenParser)
import Text.ParserCombinators.Parsec.Language (emptyDef)
import IDE.Workspaces (packageTry, packageTryQuiet)
import qualified Data.Conduit.List as CL (consume)
import Control.Monad.Trans.Class (MonadTrans(..))
import Control.Monad.IO.Class (MonadIO(..))
import IDE.Utils.GUIUtils (treeViewContextMenu, __)
import Data.Text (Text)
import Data.Monoid ((<>))
import qualified Data.Text as T (pack, unpack)
import Control.Applicative ((<$>))
import GI.Gtk.Objects.ScrolledWindow
(scrolledWindowSetPolicy, scrolledWindowSetShadowType,
scrolledWindowNew, ScrolledWindow(..))
import GI.Gtk.Objects.TreeView
(onTreeViewRowActivated, treeViewGetSelection,
treeViewSetHeadersVisible, treeViewAppendColumn, treeViewSetModel,
treeViewNew, TreeView(..))
import Data.GI.Gtk.ModelView.ForestStore
(forestStoreGetValue, ForestStore(..), forestStoreSetValue,
forestStoreInsert, forestStoreClear, forestStoreNew)
import GI.Gtk.Objects.Widget (afterWidgetFocusInEvent, toWidget)
import GI.Gtk.Objects.Notebook (Notebook(..))
import GI.Gtk.Objects.Window (Window(..))
import GI.Gtk.Objects.CellRendererText
(setCellRendererTextText, cellRendererTextNew)
import GI.Gtk.Objects.TreeViewColumn
(TreeViewColumn(..), treeViewColumnSetReorderable,
treeViewColumnSetResizable, treeViewColumnSetSizing,
treeViewColumnSetTitle, treeViewColumnNew)
import GI.Gtk.Enums
(PolicyType(..), ShadowType(..), SelectionMode(..),
TreeViewColumnSizing(..))
import GI.Gtk.Interfaces.CellLayout (cellLayoutPackStart)
import Data.GI.Gtk.ModelView.CellLayout
(cellLayoutSetDataFunction)
import GI.Gtk.Objects.TreeSelection
(treeSelectionSetMode)
import GI.Gtk.Objects.Adjustment (noAdjustment)
import GI.Gtk.Objects.Container (containerAdd)
import Control.Monad.Reader (MonadReader(..))
import GI.Gtk.Structs.TreePath
(TreePath(..))
import GI.Gtk.Objects.Menu (Menu(..))
import GI.Gtk.Objects.MenuItem
(toMenuItem, onMenuItemActivate, menuItemNewWithLabel)
import GI.Gtk.Objects.SeparatorMenuItem (separatorMenuItemNew)
import GI.Gtk.Objects.MenuShell (menuShellAppend)
import Data.GI.Gtk.ModelView.Types
(treeSelectionGetSelectedRows', treePathNewFromIndices')
-- | Represents the Variables pane
data IDEVariables = IDEVariables {
scrolledView :: ScrolledWindow
, treeView :: TreeView
, variables :: ForestStore VarDescription
} deriving Typeable
-- | The data for a single entry in the pane
data VarDescription = VarDescription {
varName :: Text
, varType :: Text
, varValue :: Text}
-- | The additional state used when recovering the pane
data VariablesState = VariablesState
deriving(Eq,Ord,Read,Show,Typeable)
instance Pane IDEVariables IDEM
where
primPaneName _ = __ "Variables"
getAddedIndex _ = 0
getTopWidget = liftIO . toWidget . scrolledView
paneId b = "*Variables"
instance RecoverablePane IDEVariables VariablesState IDEM where
saveState p = return (Just VariablesState)
recoverState pp VariablesState = do
nb <- getNotebook pp
buildPane pp nb builder
builder = builder'
-- | Get the Variables pane
getVariables :: IDEM IDEVariables
getVariables = forceGetPane (Right "*Variables")
-- | Display the Variables pane
showVariables :: IDEAction
showVariables = do
pane <- getVariables
displayPane pane False
-- | Builds the Variables pane, together with a list
-- of connections of events
builder' :: PanePath ->
Notebook ->
Window ->
IDEM (Maybe IDEVariables, Connections)
builder' pp nb windows = do
ideR <- ask
variables <- forestStoreNew []
treeView <- treeViewNew
treeViewSetModel treeView (Just variables)
renderer1 <- cellRendererTextNew
col1 <- treeViewColumnNew
treeViewColumnSetTitle col1 (__ "Name")
treeViewColumnSetSizing col1 TreeViewColumnSizingAutosize
treeViewColumnSetResizable col1 True
treeViewColumnSetReorderable col1 True
treeViewAppendColumn treeView col1
cellLayoutPackStart col1 renderer1 False
cellLayoutSetDataFunction col1 renderer1 variables
$ setCellRendererTextText renderer1 . varName
renderer2 <- cellRendererTextNew
col2 <- treeViewColumnNew
treeViewColumnSetTitle col2 (__ "Type")
treeViewColumnSetSizing col2 TreeViewColumnSizingAutosize
treeViewColumnSetResizable col2 True
treeViewColumnSetReorderable col2 True
treeViewAppendColumn treeView col2
cellLayoutPackStart col2 renderer2 False
cellLayoutSetDataFunction col2 renderer2 variables
$ setCellRendererTextText renderer2 . varType
renderer3 <- cellRendererTextNew
col3 <- treeViewColumnNew
treeViewColumnSetTitle col3 (__ "Value")
treeViewColumnSetSizing col3 TreeViewColumnSizingAutosize
treeViewColumnSetResizable col3 True
treeViewColumnSetReorderable col3 True
treeViewAppendColumn treeView col3
cellLayoutPackStart col3 renderer3 False
cellLayoutSetDataFunction col3 renderer3 variables
$ setCellRendererTextText renderer3 . varValue
treeViewSetHeadersVisible treeView True
sel <- treeViewGetSelection treeView
treeSelectionSetMode sel SelectionModeSingle
scrolledView <- scrolledWindowNew noAdjustment noAdjustment
scrolledWindowSetShadowType scrolledView ShadowTypeIn
containerAdd scrolledView treeView
scrolledWindowSetPolicy scrolledView PolicyTypeAutomatic PolicyTypeAutomatic
let pane = IDEVariables scrolledView treeView variables
cid1 <- onIDE afterWidgetFocusInEvent treeView (do
liftIDE $ makeActive pane
return True)
cids2 <- treeViewContextMenu treeView $ variablesContextMenu ideR variables treeView
cid4 <- ConnectC treeView <$> onTreeViewRowActivated treeView (variablesSelect ideR variables)
return (Just pane, [cid1, cid4] ++ cids2)
-- | Quietly list the variable bindings of the current debug session
fillVariablesListQuiet :: IDEAction
fillVariablesListQuiet = packageTryQuiet $ do
mbVariables <- liftIDE getPane
case mbVariables of
Nothing -> return ()
Just var -> tryDebugQuiet $ debugCommand' ":show bindings" $ do
to <- CL.consume
lift $ postAsyncIDE $
case parse variablesParser "" . T.unpack $ selectString to of
Left e -> liftIO $ sysMessage Normal (T.pack $ show e)
Right triples -> do
forestStoreClear (variables var)
mapM_ (insertAtRoot (variables var))
(zip triples [0..length triples])
insertAtRoot :: MonadIO m => ForestStore a -> (a, Int) -> m ()
insertAtRoot forestStore (v,index) = do
emptyPath <- treePathNewFromIndices' []
forestStoreInsert forestStore emptyPath index v
-- | List the variable bindings of the current debug session
fillVariablesList :: IDEAction
fillVariablesList = packageTry $ do
mbVariables <- liftIDE getPane
case mbVariables of
Nothing -> return ()
Just var -> tryDebug $ debugCommand' ":show bindings" $ do
to <- CL.consume
lift $ postAsyncIDE $
case parse variablesParser "" . T.unpack $ selectString to of
Left e -> liftIO $ sysMessage Normal (T.pack $ show e)
Right triples -> do
forestStoreClear (variables var)
mapM_ (insertAtRoot (variables var))
(zip triples [0..length triples])
-- | Concatenates the tool output lines with newlines
selectString :: [ToolOutput] -> Text
selectString (ToolOutput str:r) = "\n" <> str <> selectString r
selectString (_:r) = selectString r
selectString [] = ""
-- | Tries to get the selected variable in the Pane,
-- together with the 'TreePath'
getSelectedVariable :: TreeView
-> ForestStore VarDescription
-> IO (Maybe (VarDescription,TreePath))
getSelectedVariable treeView forestStore = do
treeSelection <- treeViewGetSelection treeView
paths <- treeSelectionGetSelectedRows' treeSelection
case paths of
a:r -> do
val <- forestStoreGetValue forestStore a
return (Just (val,a))
_ -> return Nothing
-- | Parses multiple 'VarDescription's
variablesParser :: CharParser () [VarDescription]
variablesParser = do
whiteSpace
r <- many variableParser
eof
return r
-- | Parses a 'VarDescription' from a line of ghci output
variableParser :: CharParser () VarDescription
variableParser = do
whiteSpace
varName <- T.pack <$> many (noneOf ":")
symbol "::"
typeStr <- T.pack <$> many (noneOf "=")
char '='
value <- T.pack <$> many (
noneOf "\n"
<|> try (do
r <- char '\n'
lookAhead (char ' ')
return r))
return (VarDescription varName typeStr value)
<?> "variableParser"
-- | Parses only the value of a variable binding from a line of
-- ghci output
valueParser :: CharParser () Text
valueParser = do
whiteSpace
many (noneOf "=")
char '='
T.pack <$> many anyChar
<?> "valueParser"
-- | Parses only the type of a variable binding from a line of
-- ghci output
typeParser :: CharParser () Text
typeParser = do
whiteSpace
many (noneOf ":")
symbol "::"
T.pack <$> many anyChar
<?> "typeParser"
lexer = P.makeTokenParser emptyDef
symbol = P.symbol lexer
whiteSpace = P.whiteSpace lexer
-- | Constructs the context menu of the Variables pane
variablesContextMenu :: IDERef
-> ForestStore VarDescription
-> TreeView
-> Menu
-> IO ()
variablesContextMenu ideR store treeView theMenu = do
item1 <- menuItemNewWithLabel (__ "Force")
onMenuItemActivate item1 $ do
mbSel <- getSelectedVariable treeView store
case mbSel of
Just (varDescr,path) -> reflectIDE (forceVariable varDescr path store) ideR
otherwise -> return ()
sep1 <- separatorMenuItemNew >>= liftIO . toMenuItem
item2 <- menuItemNewWithLabel (__ "Print")
onMenuItemActivate item2 $ do
mbSel <- getSelectedVariable treeView store
case mbSel of
Just (varDescr,path) -> reflectIDE (printVariable varDescr path store) ideR
otherwise -> return ()
item3 <- menuItemNewWithLabel (__ "Update")
onMenuItemActivate item3 $ reflectIDE (postAsyncIDE fillVariablesList) ideR
mapM_ (menuShellAppend theMenu) [item1, item2, sep1, item3]
-- | Called when a variable is selected in the Variables pane.
-- Forces the variable and updates the entry in the pane,
variablesSelect :: IDERef
-> ForestStore VarDescription
-> TreePath
-> TreeViewColumn
-> IO ()
variablesSelect ideR store path _ = do
varDescr <- forestStoreGetValue store path
reflectIDE (forceVariable varDescr path store) ideR
-- | Force the value of the given 'VarDescription', retrieve its type
-- and update the appropriate entry in the Variables pane
forceVariable :: VarDescription -> TreePath -> ForestStore VarDescription -> IDEAction
forceVariable varDescr path forestStore = packageTry $ tryDebug $ do
debugCommand' (":force " <> varName varDescr) $ do
to <- CL.consume
lift $ postAsyncIDE $
case parse valueParser "" . T.unpack $ selectString to of
Left e -> liftIO $ sysMessage Normal (T.pack $ show e)
Right value -> do
var <- forestStoreGetValue forestStore path
forestStoreSetValue forestStore path var{varValue = value}
debugCommand' (":type " <> varName varDescr) $ do
to <- CL.consume
lift $ postAsyncIDE $
case parse typeParser "" . T.unpack $ selectString to of
Left e -> liftIO $ sysMessage Normal (T.pack $ show e)
Right typ -> do
var <- forestStoreGetValue forestStore path
forestStoreSetValue forestStore path var{varType = typ}
-- | Print the value of the given 'VarDescription', retrieve its type
-- and update the appropriate entry in the Variables pane
printVariable :: VarDescription -> TreePath -> ForestStore VarDescription -> IDEAction
printVariable varDescr path forestStore = packageTry $ tryDebug $ do
debugCommand' (":print " <> varName varDescr) $ do
to <- CL.consume
lift $ postAsyncIDE $
case parse valueParser "" . T.unpack $ selectString to of
Left e -> liftIO $ sysMessage Normal (T.pack $ show e)
Right value -> do
var <- forestStoreGetValue forestStore path
forestStoreSetValue forestStore path var{varValue = value}
debugCommand' (":type " <> varName varDescr) $ do
to <- CL.consume
lift $ postAsyncIDE $
case parse typeParser "" . T.unpack $ selectString to of
Left e -> liftIO $ sysMessage Normal (T.pack $ show e)
Right typ -> do
var <- forestStoreGetValue forestStore path
forestStoreSetValue forestStore path var{varType = typ}