{-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiWayIf #-} {-# LANGUAGE BinaryLiterals #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE DeriveDataTypeable #-} module Phoityne.IO.GUI.Control ( createMainWindow , DebugCommandData(..) ) where -- モジュール import Phoityne.Constant import Phoityne.Utility import Phoityne.IO.Utility import qualified Phoityne.IO.GUI.GTK.Interface as IF -- システム import System.Exit import System.FilePath import System.Directory import System.Log.Logger import Control.Monad import Control.Concurrent import Data.Maybe import Data.String.Utils import Data.Char import Data.Functor.Identity import Text.Parsec import qualified Data.Map as Map import qualified Data.List as L import qualified Data.Tree as T import qualified Data.Text as TE import qualified Data.Text.Encoding as TE import qualified Text.StringTemplate as TPL -- | -- -- type NoteMap = Map.Map IF.NodeData IF.TextEditorData -- | -- -- data UndoRedoData = DeleteRangeUndoRedoData { filePathDeleteRangeUndoRedoData :: FilePath , startLineNoDeleteRangeUndoRedoData :: Int , startColNoDeleteRangeUndoRedoData :: Int , endLineNoDeleteRangeUndoRedoData :: Int , endColNoDeleteRangeUndoRedoData :: Int , textDeleteRangeUndoRedoData :: String } | InsertTextUndoRedoData { filePathInsertTextUndoRedoData :: FilePath , startLineNoInsertTextUndoRedoData :: Int , startColNoInsertTextUndoRedoData :: Int , textInsertTextUndoRedoData :: String } deriving (Show, Read, Eq, Ord) -- | -- -- data MVarGUIData = MVarGUIData { widgetStoreMVarGUIData :: IF.WidgetStore , breakPointListMVarGUIData :: IF.BreakPointListStore , codeNoteMapMVarGUIData :: NoteMap , folderTreeMVarGUIData :: IF.FolderTreeStore , bindingListStoreMVarGUIData :: IF.BindingListStore , traceListStoreMVarGUIData :: IF.TraceDataListStore , searchResultListStoreMVarGUIData :: IF.SearchResultListStore , traceIdMVarGUIData :: Int , undoBufferMVarGUIData :: [UndoRedoData] , redoBufferMVarGUIData :: [UndoRedoData] , unDoReDoFlagMVarGUIData :: Bool , buildMsgMVarGUIData :: [String] , searchFilesMVarGUIData :: [FilePath] , startupNodeDataMVarGUIData :: Maybe IF.NodeData } -- | -- -- data DebugCommandData = DebugCommandData { startDebugCommandData :: IO () , stopDebugCommandData :: IO ExitCode , readDebugCommandData :: IO String , readLinesDebugCommandData :: ([String] -> IO Bool) -> IO [String] , promptDebugCommandData :: IO String , breakDebugCommandData :: ModuleName -> Int -> IO String , bindingsDebugCommandData :: IO String , runDebugCommandData :: Bool -> IO String , continueDebugCommandData :: Bool -> IO String , stepDebugCommandData :: IO String , stepOverDebugCommandData :: IO String , printEvldDebugCommandData :: IO String , deleteBreakDebugCommandData :: Int -> IO String , traceHistDebugCommandData :: IO String , traceBackDebugCommandData :: IO String , traceForwardDebugCommandData :: IO String , forceDebugCommandData :: String -> IO String , execCommandData :: String -> IO String , quitDebugCommandData :: IO String , buildStartDebugCommandData :: IO () , cleanStartDebugCommandData :: IO () , loadFileDebugCommandData :: FilePath -> IO String , readWhileDebugCommandData :: (String -> Bool) -> IO String } -- | -- -- data HighlightTextRangeData = HighlightTextRangeData { filePathHighlightTextRangeData :: FilePath , startLineNoHighlightTextRangeData :: Int , startColNoHighlightTextRangeData :: Int , endLineNoHighlightTextRangeData :: Int , endColNoHighlightTextRangeData :: Int } deriving (Show, Read, Eq, Ord) -- | -- -- getKeyOfHighlightTextRangeData :: HighlightTextRangeData -> IF.BreakPointDataKey getKeyOfHighlightTextRangeData (HighlightTextRangeData file line _ _ _) = (file, line) -- | -- -- defaultMVarGUIData :: IF.WidgetStore -> IF.BreakPointListStore -> IF.FolderTreeStore -> IF.BindingListStore -> IF.TraceDataListStore -> IF.SearchResultListStore -> MVarGUIData defaultMVarGUIData widgets breaks folder bindings trace search = MVarGUIData { widgetStoreMVarGUIData = widgets , breakPointListMVarGUIData = breaks , codeNoteMapMVarGUIData = Map.fromList [] , folderTreeMVarGUIData = folder , bindingListStoreMVarGUIData = bindings , traceListStoreMVarGUIData = trace , searchResultListStoreMVarGUIData = search , traceIdMVarGUIData = 0 , undoBufferMVarGUIData = [] , redoBufferMVarGUIData = [] , unDoReDoFlagMVarGUIData = False , buildMsgMVarGUIData = [] , searchFilesMVarGUIData = [] , startupNodeDataMVarGUIData = Nothing } -- | -- -- createMainWindow :: DebugCommandData -> [FilePath] -> IO () createMainWindow cmdData paths = do -- Storeの作成 builder <- IF.getBuilder breakStore <- IF.createBreakPointListStore bindingStore <- IF.createBindingListStore traceStore <- IF.createTraceDataListStore searchResultStore <- IF.createSearchResultListStore treeStore <- loadFolderForest _PROJECT_ROOT_MODULE_NAME paths >>= IF.createTreeStore -- GUI共有データの作成 mvarGUI <- newMVar $ defaultMVarGUIData builder breakStore treeStore bindingStore traceStore searchResultStore -- イベントハンドラの登録 IF.setupMainWindow builder (mainWindowCloseEventHanlder) (mainWindowKeyPressEventHandler mvarGUI cmdData) IF.setupToolButton builder (toolBTdebugStartHandler mvarGUI cmdData) (toolBTdebugStopHandler mvarGUI cmdData) (toolBTstepOverHandler mvarGUI cmdData) (toolBTstepInHandler mvarGUI cmdData) (toolBTcontinueHandler mvarGUI cmdData) (toolBTbuildHandler mvarGUI cmdData) (toolBTdeleteHandler mvarGUI cmdData) (toolBTsaveHandler mvarGUI cmdData) (toolBTindentHandler mvarGUI) (toolBTunIndentHandler mvarGUI) (toolBTcommentHandler mvarGUI) (toolBTunCommentHandler mvarGUI) IF.setupFolderTree builder treeStore (folderTreeDoubleClickedHandler mvarGUI cmdData) (folderTreePopupHandler mvarGUI) (folderTreeCreateFolderAction mvarGUI) (folderTreeCreateFileAction mvarGUI cmdData) (folderTreeRenameAction mvarGUI) (folderTreeDeleteAction mvarGUI) (folderTreeSearchAction mvarGUI) (folderTreeReplaceAction mvarGUI cmdData) (folderTreeKeyPressEventHandler mvarGUI cmdData) (folderTreeStartupAction mvarGUI cmdData) IF.setupConsoleView builder (consoleDoubleClickedHandler mvarGUI cmdData) IF.setupBreakPointTable builder breakStore (breakPointTableDoubleClickedHandler mvarGUI cmdData) IF.setupBindingTable builder bindingStore (bindingTableDoubleClickedHandler mvarGUI cmdData) IF.setupTraceTable builder traceStore (traceTableDoubleClickedHandler mvarGUI cmdData) IF.setupSearchResultTable builder searchResultStore (searchResultTableDoubleClickedHandler mvarGUI cmdData) -- 開始 IF.start builder -- |===================================================================== -- MainWindowのイベントハンドラ -- -- | -- -- mainWindowCloseEventHanlder :: IF.MainWindowCloseEventHandler mainWindowCloseEventHanlder = infoM _LOG_NAME "See you again." -- | -- -- mainWindowKeyPressEventHandler :: MVar MVarGUIData -> DebugCommandData -> IF.MainWindowKeyPressEventHandler mainWindowKeyPressEventHandler mvarGUI cmdDat "F5" isShift _ = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData isStart <- IF.isDebugStart builder withStart isStart isShift return True where withStart True True = toolBTdebugStopHandler mvarGUI cmdDat withStart True False = toolBTcontinueHandler mvarGUI cmdDat withStart False False = toolBTdebugStartHandler mvarGUI cmdDat withStart _ _ = return () mainWindowKeyPressEventHandler mvarGUI cmdDat "F7" True _ = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData isStart <- IF.isBuildStart builder when (False == isStart) $ do runClean toolBTbuildHandler mvarGUI cmdDat return True where runClean = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData cleanCmd = cleanStartDebugCommandData cmdDat readLine = readLinesDebugCommandData cmdDat exitCmd = stopDebugCommandData cmdDat IF.clearConsole builder IF.putStrConsole builder $ "start stack clean.\n" cleanCmd readLine $ cleanResultHandler mvarGUI code <- exitCmd IF.putStrLnConsole builder $ show code cleanResultHandler :: MVar MVarGUIData -> [String] -> IO Bool cleanResultHandler _ [] = return False cleanResultHandler mvarGUI strs = do guiData <- takeMVar mvarGUI let builder = widgetStoreMVarGUIData guiData IF.putStrLnConsole builder $ last strs return True mainWindowKeyPressEventHandler mvarGUI cmdDat "F7" _ _ = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData isStart <- IF.isBuildStart builder when (False == isStart) $ do IF.clearConsole builder toolBTbuildHandler mvarGUI cmdDat return True mainWindowKeyPressEventHandler mvarGUI cmdDat "F10" _ _ = do guiData <- takeMVar mvarGUI let builder = widgetStoreMVarGUIData guiData isStart <- IF.isDebugStart builder putMVar mvarGUI guiData if isStart then toolBTstepOverHandler mvarGUI cmdDat else return () return True mainWindowKeyPressEventHandler mvarGUI cmdDat "F11" _ _ = do guiData <- takeMVar mvarGUI let builder = widgetStoreMVarGUIData guiData isStart <- IF.isDebugStart builder putMVar mvarGUI guiData if isStart then toolBTstepInHandler mvarGUI cmdDat else return () return True mainWindowKeyPressEventHandler mvarGUI cmdDat "s" _ True = do saveAll mvarGUI cmdDat -- Ctr+s return True mainWindowKeyPressEventHandler mvarGUI cmdDat "f" _ True = findCurrentTextEditor mvarGUI >>= \case Nothing -> errorM _LOG_NAME "[mainWindowKeyPressEventHandler] invalid text editor." >> return True Just a -> withEditor a where withEditor (nodeData, editor) = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData defaultStr <- IF.getSelectedText editor IF.getSearchKeyBySearchDialog builder defaultStr >>= \case Nothing -> return True Just key -> do clearSearchResultTable mvarGUI activateSearchResultTab mvarGUI setSearchFiles mvarGUI [nodeData] keywordLineSearch [nodeData] key (searchResultHandler mvarGUI) (lineNo, _) <- IF.getCodeTextLineNumber editor activateTextEditorWithSearchResult mvarGUI cmdDat $ Just (IF.getPathFromNodeData nodeData, lineNo+1) return True mainWindowKeyPressEventHandler mvarGUI cmdDat "r" _ True =findCurrentTextEditor mvarGUI >>= \case Nothing -> errorM _LOG_NAME "[mainWindowKeyPressEventHandler] invalid text editor." >> return True Just a -> withEditor a where withEditor (nodeData, editor) = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData nodeDatas = [nodeData] IF.getReplaceByReplaceDialog builder >>= \case Nothing -> return True Just (key, rep) -> do saveAll mvarGUI cmdDat replaceFiles nodeDatas key rep reloadAll mvarGUI -- search clearSearchResultTable mvarGUI activateSearchResultTab mvarGUI setSearchFiles mvarGUI nodeDatas keywordLineSearch nodeDatas rep (searchResultHandler mvarGUI) (lineNo, _) <- IF.getCodeTextLineNumber editor activateTextEditorWithSearchResult mvarGUI cmdDat $ Just (IF.getPathFromNodeData nodeData, lineNo+1) return True mainWindowKeyPressEventHandler mvarGUI cmdDat "F3" False _ = do activateTextEditorWithSearchResult mvarGUI cmdDat Nothing return True mainWindowKeyPressEventHandler _ _ _ _ _ = return False -- |===================================================================== -- ToolButtonのイベントハンドラ -- -- | -- Event Handler -- toolBTdebugStartHandler :: MVar MVarGUIData -> DebugCommandData -> IF.DebugStartBTClickedEventHandler toolBTdebugStartHandler mvarGUI cmdData = do runGHCi >>= \case False -> do warningM _LOG_NAME "run ghci fail." True -> loadHsFile >>= \case False -> do warningM _LOG_NAME "run ghci fail." True -> setupDebug >> startDebug where runGHCi = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData startCmd = startDebugCommandData cmdData readLines = readLinesDebugCommandData cmdData readWhile = readWhileDebugCommandData cmdData IF.clearConsole builder IF.putStrConsole builder $ "start stack ghci.\n" startCmd cont <- readLines (debugStartResultHandler builder) if | null cont -> return False | startswith "Ok," (last cont) -> do res <- readWhile $ not . endswith "> " IF.putStrConsole builder res return True | otherwise -> return False loadHsFile = do guiData <- readMVar mvarGUI loadHsFileMay $ startupNodeDataMVarGUIData guiData loadHsFileMay Nothing = return True loadHsFileMay (Just nodeDat) = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData loadFile = loadFileDebugCommandData cmdData readLines = readLinesDebugCommandData cmdData readWhile = readWhileDebugCommandData cmdData cmdStr <- loadFile $ IF.getPathFromNodeData nodeDat IF.putStrLnConsole builder cmdStr cont <- readLines (debugStartResultHandler builder) if | null cont -> return False | startswith "Ok," (last cont) -> do res <- readWhile $ not . endswith "> " IF.putStrConsole builder res return True | otherwise -> return False setupDebug = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData breakStore = breakPointListMVarGUIData guiData setPrompt = promptDebugCommandData cmdData getResult = readDebugCommandData cmdData printEvld = printEvldDebugCommandData cmdData promptStr <- setPrompt IF.putStrLnConsole builder promptStr cmdStr <- getResult IF.putStrConsole builder cmdStr cmdStr <- printEvld IF.putStrLnConsole builder cmdStr cmdStr <- getResult IF.putStrConsole builder cmdStr IF.setupDebugButtonOn builder breakList <- IF.getBreakPointList breakStore mapM_ (addBreakPointOnCUI mvarGUI cmdData) breakList startDebug = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData getResult = readDebugCommandData cmdData runDebug = runDebugCommandData cmdData cmdStr <- runDebug True IF.putStrLnConsole builder cmdStr cmdStr <- getResult IF.putStrConsole builder cmdStr let breakPos = getStoppedTextRangeData cmdStr continueWithHighlightTextRangeData mvarGUI cmdData breakPos -- | -- -- debugStartResultHandler :: IF.WidgetStore -> [String] -> IO Bool debugStartResultHandler builder acc = IF.putStrLnConsole builder curStr >> if | L.isPrefixOf "Ok," curStr -> return False | L.isPrefixOf "Failed," curStr -> return False | otherwise -> return True where curStr | null acc = "" | otherwise = last acc -- | -- Event Handler -- toolBTdebugStopHandler :: MVar MVarGUIData -> DebugCommandData -> IF.DebugStopBTClickedEventHandler toolBTdebugStopHandler mvarGUI cmdData = do guiData <- takeMVar mvarGUI let builder = widgetStoreMVarGUIData guiData codeNoteMap = codeNoteMapMVarGUIData guiData exitCmd = stopDebugCommandData cmdData quitCmd = quitDebugCommandData cmdData -- readLines = readLinesDebugCommandData cmdData readWhile = readWhileDebugCommandData cmdData putMVar mvarGUI guiData cmdStr <- quitCmd IF.putStrLnConsole builder cmdStr str <- readWhile $ const True IF.putStrConsole builder str IF.putStrLnConsole builder "" code <- exitCmd IF.putStrLnConsole builder $ show code IF.setupDebugButtonOff builder mapM_ IF.offLightBreakPoint $ Map.elems codeNoteMap return () -- | -- Event Handler -- toolBTstepOverHandler :: MVar MVarGUIData -> DebugCommandData -> IF.StepOverBTClickedEventHandler toolBTstepOverHandler mvarGUI cmdData = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData bindStore = bindingListStoreMVarGUIData guiData stepOver = stepOverDebugCommandData cmdData getResult = readDebugCommandData cmdData bindings = bindingsDebugCommandData cmdData cmdStr <- stepOver IF.putStrLnConsole builder cmdStr cmdStr <- getResult IF.putStrConsole builder cmdStr let breakPos = getStoppedTextRangeData cmdStr _ <- bindings bindStr <- getResult case getBindingDataList bindStr of Left err -> warningM _LOG_NAME $ show err Right dats -> IF.updateBindingTable bindStore dats case breakPos of Left err -> warningM _LOG_NAME $ show err Right pos -> activateTextEditor mvarGUI cmdData pos return () -- | -- Event Handler -- toolBTstepInHandler :: MVar MVarGUIData -> DebugCommandData -> IF.StepInBTClickedEventHandler toolBTstepInHandler mvarGUI cmdData = do guiData <- takeMVar mvarGUI let builder = widgetStoreMVarGUIData guiData bindStore = bindingListStoreMVarGUIData guiData step = stepDebugCommandData cmdData getResult = readDebugCommandData cmdData bindings = bindingsDebugCommandData cmdData putMVar mvarGUI guiData cmdStr <- step IF.putStrLnConsole builder cmdStr cmdStr <- getResult IF.putStrConsole builder cmdStr let breakPos = getStoppedTextRangeData cmdStr _ <- bindings bindStr <- getResult case getBindingDataList bindStr of Left err -> warningM _LOG_NAME $ show err Right dats -> IF.updateBindingTable bindStore dats case breakPos of Left err -> warningM _LOG_NAME $ show err Right pos -> activateTextEditor mvarGUI cmdData pos return () -- | -- Event Handler -- toolBTcontinueHandler :: MVar MVarGUIData -> DebugCommandData -> IF.StepInBTClickedEventHandler toolBTcontinueHandler mvarGUI cmdData = do guiData <- takeMVar mvarGUI let builder = widgetStoreMVarGUIData guiData continue = continueDebugCommandData cmdData getResult = readDebugCommandData cmdData putMVar mvarGUI guiData cmdStr <- continue True IF.putStrLnConsole builder cmdStr cmdStr <- getResult IF.putStrConsole builder cmdStr let breakPos = getStoppedTextRangeData cmdStr continueWithHighlightTextRangeData mvarGUI cmdData breakPos -- | -- Event Handler -- toolBTbuildHandler :: MVar MVarGUIData -> DebugCommandData -> IF.BuildBTClickedEventHandler toolBTbuildHandler mvarGUI cmdData = do saveAll mvarGUI cmdData _ <- forkIO $ do hid <- IF.addCallback $ buildCallbackHandler mvarGUI True guiData <- takeMVar mvarGUI let builder = widgetStoreMVarGUIData guiData buildStart = buildStartDebugCommandData cmdData readLine = readLinesDebugCommandData cmdData exitCmd = stopDebugCommandData cmdData putMVar mvarGUI guiData{buildMsgMVarGUIData = []} IF.setupBuildButtonOff builder --IF.clearConsole builder appendBuildMsg mvarGUI "start stack build.\n" buildStart readLine (buildStartResultHandler mvarGUI) IF.delCallback hid code <- exitCmd appendBuildMsg mvarGUI $ "\n" ++ show code ++ "\n" _ <- IF.addCallback $ buildCallbackHandler mvarGUI False IF.setupBuildButtonOn builder return () where -- | -- Event Handler -- buildCallbackHandler :: MVar MVarGUIData -> Bool -> IO Bool buildCallbackHandler mvarGUI doNotDelHandle = do guiData <- takeMVar mvarGUI let builder = widgetStoreMVarGUIData guiData curMsg = buildMsgMVarGUIData guiData putMVar mvarGUI guiData{ buildMsgMVarGUIData = [] } mapM_ (IF.putStrConsole builder) curMsg return doNotDelHandle -- | -- -- buildStartResultHandler :: MVar MVarGUIData -> [String] -> IO Bool buildStartResultHandler _ [] = return False buildStartResultHandler mvarGUI strs = do appendBuildMsg mvarGUI $ last strs return True -- | -- -- appendBuildMsg :: MVar MVarGUIData -> String -> IO () appendBuildMsg mvarGUI msg = do guiData <- takeMVar mvarGUI let curMsg = buildMsgMVarGUIData guiData putMVar mvarGUI guiData{ buildMsgMVarGUIData = curMsg ++ [msg] } -- | -- Event Handler -- toolBTdeleteHandler :: MVar MVarGUIData -> DebugCommandData -> IF.DeleteAllBreakBTClickedEventHandler toolBTdeleteHandler mvarGUI cmdData = do guiData <- takeMVar mvarGUI let bpList = breakPointListMVarGUIData guiData noteMap = codeNoteMapMVarGUIData guiData mapM_ IF.offLightBreakPoint $ Map.elems noteMap putMVar mvarGUI guiData IF.getBreakPointList bpList >>= mapM_ go where go (IF.BreakPointData _ path lineNo _ _) = do let bpKey = (path, lineNo) deleteBreakPointOnCUI mvarGUI cmdData bpKey deleteBreakPointOnBPTable mvarGUI bpKey deleteBreakPointTag mvarGUI bpKey -- | -- Event Handler -- toolBTsaveHandler :: MVar MVarGUIData -> DebugCommandData -> IF.SaveBTClickedEventHandler toolBTsaveHandler = saveAll -- | -- Event Handler -- toolBTindentHandler :: MVar MVarGUIData -> IF.IndentBTClickedEventHandler toolBTindentHandler mvarGUI = findCurrentTextEditor mvarGUI >>= \case Nothing -> return() Just (_, editor) -> IF.blockIndentTextEditor editor -- | -- Event Handler -- toolBTunIndentHandler :: MVar MVarGUIData -> IF.UnIndentBTClickedEventHandler toolBTunIndentHandler mvarGUI = findCurrentTextEditor mvarGUI >>= \case Nothing -> return() Just (_, editor) -> IF.blockUnIndentTextEditor editor -- | -- Event Handler -- toolBTcommentHandler :: MVar MVarGUIData -> IF.CommentBTClickedEventHandler toolBTcommentHandler mvarGUI = findCurrentTextEditor mvarGUI >>= \case Nothing -> return() Just (_, editor) -> IF.blockCommentTextEditor editor -- | -- Event Handler -- toolBTunCommentHandler :: MVar MVarGUIData -> IF.UnCommentBTClickedEventHandler toolBTunCommentHandler mvarGUI = findCurrentTextEditor mvarGUI >>= \case Nothing -> return() Just (_, editor) -> IF.blockUnCommentTextEditor editor -- | -- -- continueWithHighlightTextRangeData :: MVar MVarGUIData -> DebugCommandData -> Either ParseError HighlightTextRangeData -> IO () continueWithHighlightTextRangeData mvarGUI cmdData (Left err) = do warningM _LOG_NAME $ "[continueWithHighlightTextRangeData]" ++ show err updateBindingTable mvarGUI cmdData updateTraceTable mvarGUI cmdData continueWithHighlightTextRangeData mvarGUI cmdData (Right pos) = do guiData <- takeMVar mvarGUI let breakStore = breakPointListMVarGUIData guiData condCmd <- IF.getBreakCondition breakStore $ getKeyOfHighlightTextRangeData pos putMVar mvarGUI guiData continueWithCondCmd mvarGUI cmdData pos condCmd where continueWithCondCmd mvarGUI cmdData pos condCmd | null condCmd = continueWithCondResult mvarGUI cmdData pos True | otherwise = do guiData <- takeMVar mvarGUI let builder = widgetStoreMVarGUIData guiData condition = execCommandData cmdData getResult = readDebugCommandData cmdData putMVar mvarGUI guiData _ <- condition condCmd IF.putStrLnConsole builder condCmd cmdStr <- getResult IF.putStrConsole builder cmdStr condRes <- getConditionResult cmdStr continueWithCondResult mvarGUI cmdData pos condRes continueWithCondResult mvarGUI cmdData _ False = toolBTcontinueHandler mvarGUI cmdData continueWithCondResult mvarGUI cmdData pos True = do updateBindingTable mvarGUI cmdData updateTraceTable mvarGUI cmdData activateTextEditor mvarGUI cmdData pos getConditionResult res | L.isPrefixOf "True" res = return True | L.isPrefixOf "False" res = return False | otherwise = warningM _LOG_NAME ("invalid condition result. " ++ res) >> return True -- | -- continueWithHighlightTextRangeDataで使用している -- updateBindingTable :: MVar MVarGUIData -> DebugCommandData -> IO () updateBindingTable mvarGUI cmdData = do guiData <- readMVar mvarGUI let bindStore = bindingListStoreMVarGUIData guiData bindings = bindingsDebugCommandData cmdData getResult = readDebugCommandData cmdData _ <- bindings bindStr <- getResult case getBindingDataList bindStr of Left err -> errorM _LOG_NAME $ show err Right dats -> IF.updateBindingTable bindStore dats -- | -- continueWithHighlightTextRangeDataで使用している -- updateTraceTable :: MVar MVarGUIData -> DebugCommandData -> IO () updateTraceTable mvarGUI cmdData = do guiData <- takeMVar mvarGUI let traceStore = traceListStoreMVarGUIData guiData getResult = readDebugCommandData cmdData history = traceHistDebugCommandData cmdData _ <- history traceStr <- getResult case getTraceDataList traceStr of Left err -> errorM _LOG_NAME $ show err Right dats -> IF.updateTraceTable traceStore dats putMVar mvarGUI guiData {traceIdMVarGUIData = 0} -- |===================================================================== -- FolderTreeのイベントハンドラ -- -- | -- Event Handler -- フォルダーツリーでファイルがダブルクリックされた場合に -- コードノートを表示する。 -- folderTreeDoubleClickedHandler :: MVar MVarGUIData -> DebugCommandData -> IF.FolderTreeDoubleClickedHandler folderTreeDoubleClickedHandler mvarGUI cmdDat = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData treeStore = folderTreeMVarGUIData guiData IF.getSelectedFolderTreeNodeData builder treeStore >>= withNodeData where withNodeData (Just (IF.FileNodeData _ _ path)) = do editorMay <- findTextEditorByPath mvarGUI path activateWithEditor mvarGUI cmdDat editorMay path 1 withNodeData _ = return () -- | -- Event Handler -- folderTreePopupHandler :: MVar MVarGUIData -> IF.FolderTreePopupHandler folderTreePopupHandler mvarGUI = do guiData <- takeMVar mvarGUI let builder = widgetStoreMVarGUIData guiData treeStore = folderTreeMVarGUIData guiData nodeData <- IF.getSelectedFolderTreeNodeData builder treeStore IF.folderTreeMenuPopup builder nodeData putMVar mvarGUI guiData -- | -- Event Handler -- folderTreeCreateFolderAction :: MVar MVarGUIData -> IF.FolderTreeCreateFolderAction folderTreeCreateFolderAction mvarGUI = do guiData <- takeMVar mvarGUI let builder = widgetStoreMVarGUIData guiData treeStore = folderTreeMVarGUIData guiData nodeData <- IF.getSelectedFolderTreeNodeData builder treeStore nameMay <- IF.getNameByFolderTreeDialog builder "Create Folder" "Input folder name." "" True case getFolderTreeNodeData nodeData nameMay of Nothing -> return () -- canceled. Just (IF.FileNodeData _ _ _) -> return () Just child@(IF.FolderNodeData _ _ path) -> do createDirectory path IF.addNode2TreeStore treeStore (fromJust nodeData) child putMVar mvarGUI guiData{folderTreeMVarGUIData = treeStore} where getFolderTreeNodeData :: Maybe IF.NodeData -> Maybe String -> Maybe IF.NodeData getFolderTreeNodeData (Just (IF.FolderNodeData modName _ path)) (Just name) = Just $ IF.FolderNodeData (getModName modName name) name (path name) getFolderTreeNodeData _ _ = Nothing getModName :: String -> String -> String getModName parent child | null parent = child | otherwise = parent ++ "." ++ child -- | -- Event Handler -- folderTreeCreateFileAction :: MVar MVarGUIData -> DebugCommandData -> IF.FolderTreeCreateFileAction folderTreeCreateFileAction mvarGUI cmdDat = do guiData <- takeMVar mvarGUI let builder = widgetStoreMVarGUIData guiData treeStore = folderTreeMVarGUIData guiData nodeData <- IF.getSelectedFolderTreeNodeData builder treeStore nameMay <- IF.getNameByFolderTreeDialog builder "Create File" "Input file name." "" True case getFileTreeNodeData nodeData nameMay of Nothing -> putMVar mvarGUI guiData Just (IF.FolderNodeData _ _ _) -> return () Just child@(IF.FileNodeData modName _ path) -> do saveFileLBS path $ str2lbs $ code modName IF.addNode2TreeStore treeStore (fromJust nodeData) child putMVar mvarGUI guiData{folderTreeMVarGUIData = treeStore} activateWithEditor mvarGUI cmdDat Nothing (IF.getPathFromNodeData child) 1 where getFileTreeNodeData :: Maybe IF.NodeData -> Maybe String -> Maybe IF.NodeData getFileTreeNodeData (Just (IF.FolderNodeData modName _ path)) (Just name) = Just $ IF.FileNodeData (getModName modName (snd (normName name))) (fst (normName name)) (path (fst (normName name))) getFileTreeNodeData _ _ = Nothing normName name | L.isSuffixOf _HS_FILE_EXT name = (name, takeBaseName name) | otherwise = (name++_HS_FILE_EXT, name) getModName :: String -> String -> String getModName parent child | null parent = child | otherwise = parent ++ "." ++ child tpl = ["{-# LANGUAGE GADTs #-}" ,"{-# LANGUAGE LambdaCase #-}" ,"{-# LANGUAGE MultiWayIf #-}" ,"{-# LANGUAGE BinaryLiterals #-}" ,"{-# LANGUAGE TemplateHaskell #-}" ,"{-# LANGUAGE OverloadedStrings #-}" ,"{-# LANGUAGE ScopedTypeVariables #-}" ,"{-# LANGUAGE DeriveDataTypeable #-}" ,"" ,"module $MODULE$ where" ,"" ] code modName = TPL.toString $ TPL.setManyAttrib [("MODULE", modName)] $ TPL.newSTMP $ unlines tpl -- | -- Event Handler -- folderTreeRenameAction :: MVar MVarGUIData -> IF.FolderTreeRenameAction folderTreeRenameAction mvarGUI = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData treeStore = folderTreeMVarGUIData guiData _ <- IF.getSelectedFolderTreeNodeData builder treeStore nameMay <- IF.getNameByFolderTreeDialog builder "Rename" "Input name." "" True infoM _LOG_NAME $ "[folderTreeRenameAction] not yet implemented. " ++ show nameMay -- | -- Event Handler -- folderTreeDeleteAction :: MVar MVarGUIData -> IF.FolderTreeDeleteAction folderTreeDeleteAction mvarGUI = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData treeStore = folderTreeMVarGUIData guiData IF.getSelectedFolderTreeNodeData builder treeStore >>= \case Nothing -> errorM _LOG_NAME $ "invalid node data." Just nodeDat -> do let name = IF.getNameFromNodeData nodeDat nameMay <- IF.getNameByFolderTreeDialog builder "Delete" ("Delete " ++ name) name False infoM _LOG_NAME $ "[folderTreeDeleteAction] not yet implemented. " ++ show nameMay -- | -- Event Handler -- folderTreeSearchAction :: MVar MVarGUIData -> IF.FolderTreeSearchAction folderTreeSearchAction mvarGUI = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData treeStore = folderTreeMVarGUIData guiData IF.getSelectedFolderTreeAllNodeData builder treeStore >>= withNodeDatas builder where withNodeDatas _ [] = errorM _LOG_NAME "invalid node data." withNodeDatas builder nodeDatas = do keyMay <- IF.getSearchKeyBySearchDialog builder "" when (isJust keyMay) $ do clearSearchResultTable mvarGUI activateSearchResultTab mvarGUI setSearchFiles mvarGUI nodeDatas keywordLineSearch nodeDatas (fromJust keyMay) (searchResultHandler mvarGUI) -- | -- Event Handler -- folderTreeReplaceAction :: MVar MVarGUIData -> DebugCommandData -> IF.FolderTreeReplaceAction folderTreeReplaceAction mvarGUI cmdDat = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData treeStore = folderTreeMVarGUIData guiData IF.getSelectedFolderTreeAllNodeData builder treeStore >>= withNodeDatas builder where withNodeDatas _ [] = errorM _LOG_NAME "invalid node data." withNodeDatas builder nodeDatas = do IF.getReplaceByReplaceDialog builder >>= \case Nothing -> return () Just (key, rep) -> do saveAll mvarGUI cmdDat replaceFiles nodeDatas key rep reloadAll mvarGUI -- search clearSearchResultTable mvarGUI activateSearchResultTab mvarGUI setSearchFiles mvarGUI nodeDatas keywordLineSearch nodeDatas rep (searchResultHandler mvarGUI) -- | -- イベントハンドラ -- folderTreeKeyPressEventHandler :: MVar MVarGUIData -> DebugCommandData -> IF.FolderTreeKeyPressEventHandler folderTreeKeyPressEventHandler mvarGUI _ "Right" _ _ = do guiData <- takeMVar mvarGUI let builder = widgetStoreMVarGUIData guiData store = folderTreeMVarGUIData guiData IF.expandFolderTree builder store putMVar mvarGUI guiData return True folderTreeKeyPressEventHandler mvarGUI _ "Left" _ _ = do guiData <- takeMVar mvarGUI let builder = widgetStoreMVarGUIData guiData store = folderTreeMVarGUIData guiData IF.collapseFolderTree builder store putMVar mvarGUI guiData return True folderTreeKeyPressEventHandler mvarGUI cmdDat "Return" _ _ = do guiData <- takeMVar mvarGUI let builder = widgetStoreMVarGUIData guiData store = folderTreeMVarGUIData guiData nodeMay <- IF.getSelectedFolderTreeNodeData builder store putMVar mvarGUI guiData case nodeMay of Just (IF.FolderNodeData _ _ _) -> do guiData <- takeMVar mvarGUI let builder = widgetStoreMVarGUIData guiData store = folderTreeMVarGUIData guiData IF.expandCollapseFolderTree builder store putMVar mvarGUI guiData Just (IF.FileNodeData _ _ path) -> do editorMay <- findTextEditorByPath mvarGUI path activateWithEditor mvarGUI cmdDat editorMay path 1 Nothing -> errorM _LOG_NAME $ "invalid tree node" return True folderTreeKeyPressEventHandler _ _ _ _ _ = return False -- | -- Event Handler -- folderTreeStartupAction :: MVar MVarGUIData -> DebugCommandData -> IF.FolderTreeStartupAction folderTreeStartupAction mvarGUI _ = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData store = folderTreeMVarGUIData guiData curStartupDat = startupNodeDataMVarGUIData guiData IF.getSelectedFolderTreeNodeData builder store >>= \case Nothing -> errorM _LOG_NAME "[folderTreeStartupAction]invalid node data." Just nodeDat -> do when (isJust curStartupDat) $ IF.updateTreeNode store (fromJust curStartupDat) $ IF.changeNameColorOfNodeData (fromJust curStartupDat) _STARTUP_MODULE_COLOR_BLUE _STARTUP_MODULE_COLOR_BLACK let newDat = IF.changeNameColorOfNodeData nodeDat _STARTUP_MODULE_COLOR_BLACK _STARTUP_MODULE_COLOR_BLUE IF.updateTreeNode store nodeDat newDat guiData <- takeMVar mvarGUI putMVar mvarGUI guiData {startupNodeDataMVarGUIData = Just newDat} -- |===================================================================== -- ConsoleViewのイベントハンドラ -- -- | -- -- consoleDoubleClickedHandler :: MVar MVarGUIData -> DebugCommandData -> IF.ConsoleDoubleClickedHandler consoleDoubleClickedHandler mvarGUI cmdData str = do case getActivatePosFromLine str of Nothing -> infoM _LOG_NAME $ "code highlight rage not found." ++ str Just pos -> activateTextEditor mvarGUI cmdData pos -- |===================================================================== -- BreakPointTableのイベントハンドラ -- -- | -- -- breakPointTableDoubleClickedHandler :: MVar MVarGUIData -> DebugCommandData -> IF.BreakPointTableDoubleClickedHandler breakPointTableDoubleClickedHandler mvarGUI cmdDat (IF.BreakPointData _ path lineNo _ _) = do editorMay <- findTextEditorByPath mvarGUI path activateWithEditor mvarGUI cmdDat editorMay path lineNo -- |===================================================================== -- BindingTableのイベントハンドラ -- -- | -- -- bindingTableDoubleClickedHandler :: MVar MVarGUIData -> DebugCommandData -> IF.BindingTableDoubleClickedHandler bindingTableDoubleClickedHandler mvarGUI cmdData (IF.BindingData argName _ _) = do guiData <- takeMVar mvarGUI let builder = widgetStoreMVarGUIData guiData bindStore = bindingListStoreMVarGUIData guiData forceVar = forceDebugCommandData cmdData bindings = bindingsDebugCommandData cmdData getResult = readDebugCommandData cmdData putMVar mvarGUI guiData cmdStr <- forceVar argName IF.putStrLnConsole builder cmdStr cmdStr <- getResult IF.putStrConsole builder cmdStr _ <- bindings bindStr <- getResult case getBindingDataList bindStr of Left err -> errorM _LOG_NAME $ show err Right dats -> IF.updateBindingTable bindStore dats -- |===================================================================== -- TraceTableのイベントハンドラ -- -- | -- -- traceTableDoubleClickedHandler :: MVar MVarGUIData -> DebugCommandData -> IF.TraceTableDoubleClickedHandler traceTableDoubleClickedHandler mvarGUI cmdData (IF.TraceData traceIdStr _ filePath) = do guiData <- takeMVar mvarGUI let builder = widgetStoreMVarGUIData guiData bindings = bindingsDebugCommandData cmdData curTraceId = traceIdMVarGUIData guiData traceId = (read traceIdStr) :: Int moveCount = curTraceId - traceId getResult = readDebugCommandData cmdData bindStore = bindingListStoreMVarGUIData guiData traceCmd = if 0 > moveCount then traceForwardDebugCommandData cmdData else traceBackDebugCommandData cmdData putMVar mvarGUI guiData {traceIdMVarGUIData = traceId} res <- foldM (go builder traceCmd getResult) (""::String) [1..(abs moveCount)] let path = strip $ replace "Logged breakpoint at " "" $ head $ lines res when (filePath /= path) $ warningM _LOG_NAME $ "move trace failed." ++ res _ <- bindings bindStr <- getResult case getBindingDataList bindStr of Left err -> errorM _LOG_NAME $ show err Right dats -> IF.updateBindingTable bindStore dats case getHighlightTextRangeData filePath of Left err -> errorM _LOG_NAME $ show err Right pos -> activateTextEditor mvarGUI cmdData pos where go builder traceCmd getResult _ _ = do cmdStr <- traceCmd IF.putStrLnConsole builder cmdStr cmdStr <- getResult IF.putStrConsole builder cmdStr return cmdStr -- |===================================================================== -- SearchResultTableのイベントハンドラ -- -- | -- -- searchResultTableDoubleClickedHandler :: MVar MVarGUIData -> DebugCommandData -> IF.SearchResultTableDoubleClickedHandler searchResultTableDoubleClickedHandler mvarGUI cmdData (IF.SearchResultData filePath lineNo startCol endCol _) = do let pos = HighlightTextRangeData { filePathHighlightTextRangeData = filePath , startLineNoHighlightTextRangeData = lineNo , startColNoHighlightTextRangeData = startCol , endLineNoHighlightTextRangeData = lineNo , endColNoHighlightTextRangeData = endCol } activateTextEditor mvarGUI cmdData pos -- |===================================================================== -- activateWithEditorで登録するイベントハンドラ -- -- | -- Event Handler -- codeNoteCloseEventHanlder :: MVar MVarGUIData -> IF.CodeNoteCloseEventHandler codeNoteCloseEventHanlder mvarGUI textEditor = do guiData <- takeMVar mvarGUI let noteMap = codeNoteMapMVarGUIData guiData let delKeys = Map.foldWithKey (\k v acc -> if textEditor == v then k:acc else acc) [] noteMap let newMap = foldr Map.delete noteMap delKeys putMVar mvarGUI $ guiData {codeNoteMapMVarGUIData = newMap} -- | -- Event Handler -- lineTextDoubleClickedHandler :: MVar MVarGUIData -> DebugCommandData -> IF.LineTextDoubleClickedHandler lineTextDoubleClickedHandler mvarGUI cmdData False lineNo = findCurrentTextEditor mvarGUI >>= \case Nothing -> errorM _LOG_NAME "[lineTextDoubleClickedHandler] invalid text editor." Just a -> withEditor a where withEditor (nodeDat, editor) = do IF.updateBreakPointTag editor False lineNo let bpDat = IF.BreakPointData (IF.getModNameFromNodeData nodeDat) (IF.getPathFromNodeData nodeDat) (lineNo+1) Nothing "" addBreakPointOnBPTable mvarGUI bpDat addBreakPointOnCUI mvarGUI cmdData bpDat lineTextDoubleClickedHandler mvarGUI cmdData True lineNo = findCurrentTextEditor mvarGUI >>= \case Nothing -> errorM _LOG_NAME "[lineTextDoubleClickedHandler] invalid text editor." Just a -> withEditor a where withEditor (nodeDat, editor) = do IF.updateBreakPointTag editor True lineNo let bpKey = (IF.getPathFromNodeData nodeDat, lineNo+1) deleteBreakPointOnCUI mvarGUI cmdData bpKey deleteBreakPointOnBPTable mvarGUI bpKey -- | -- -- codeTextKeyPressEventHandler :: MVar MVarGUIData -> DebugCommandData -> IF.CodeTextKeyPressEventHandler codeTextKeyPressEventHandler mvarGUI cmdData "F9" False False True lineNo = findCurrentTextEditor mvarGUI >>= \case Nothing -> errorM _LOG_NAME "[codeTextKeyPressEventHandler] invalid text editor" >> return True Just a -> withEditor a where withEditor (nodeDat, editor) = do IF.updateBreakPointTag editor True lineNo let bpKey = (IF.getPathFromNodeData nodeDat, lineNo+1) deleteBreakPointOnCUI mvarGUI cmdData bpKey deleteBreakPointOnBPTable mvarGUI bpKey return True codeTextKeyPressEventHandler mvarGUI cmdData "F9" False False False lineNo = findCurrentTextEditor mvarGUI >>= \case Nothing -> errorM _LOG_NAME "[codeTextKeyPressEventHandler] invalid text editor" >> return True Just a -> withEditor a where withEditor (nodeDat, editor) = do IF.updateBreakPointTag editor False lineNo let bpDat = IF.BreakPointData (IF.getModNameFromNodeData nodeDat) (IF.getPathFromNodeData nodeDat) (lineNo+1) Nothing "" addBreakPointOnBPTable mvarGUI bpDat addBreakPointOnCUI mvarGUI cmdData bpDat return True codeTextKeyPressEventHandler mvarGUI _ "Right" False True _ _ = do toolBTindentHandler mvarGUI return True codeTextKeyPressEventHandler mvarGUI _ "Left" False True _ _ = do toolBTunIndentHandler mvarGUI return True codeTextKeyPressEventHandler _ _ "g" False True _ _ = do infoM _LOG_NAME "ctrl g called. not yet implemented." return True codeTextKeyPressEventHandler mvarGUI cmdDat "z" False True _ _ = do guiData <- takeMVar mvarGUI let buf = undoBufferMVarGUIData guiData itemMay <- case buf of [] -> putMVar mvarGUI guiData >> return Nothing x:xs -> do putMVar mvarGUI guiData{ undoBufferMVarGUIData = xs , redoBufferMVarGUIData = x:redoBufferMVarGUIData guiData , unDoReDoFlagMVarGUIData = True} return $ Just x unDo mvarGUI cmdDat itemMay guiData <- takeMVar mvarGUI putMVar mvarGUI guiData{unDoReDoFlagMVarGUIData = False} return True where unDo :: MVar MVarGUIData -> DebugCommandData -> Maybe UndoRedoData -> IO () unDo _ _ Nothing = return () unDo mvarGUI cmdDat (Just (DeleteRangeUndoRedoData path startLineNo startColNo _ _ str)) = do editorMay <- findTextEditorByPath mvarGUI path activateWithEditor mvarGUI cmdDat editorMay path startLineNo findTextEditorByPath mvarGUI path >>= \case Nothing -> errorM _LOG_NAME $ "invalie note." Just editor -> do IF.insertText2TextEditor editor startLineNo startColNo str IF.setCursorOnTextEditor editor startLineNo startColNo unDo mvarGUI cmdDat (Just (InsertTextUndoRedoData path startLineNo startColNo str)) = do editorMay <- findTextEditorByPath mvarGUI path activateWithEditor mvarGUI cmdDat editorMay path startLineNo findTextEditorByPath mvarGUI path >>= \case Nothing -> errorM _LOG_NAME $ "invalie note." Just editor -> do (endLineNo, endColNo) <- IF.searchEndIter editor startLineNo startColNo str IF.deleteRangeOnTextEditor editor startLineNo startColNo endLineNo endColNo IF.setCursorOnTextEditor editor startLineNo startColNo codeTextKeyPressEventHandler mvarGUI cmdDat "y" False True _ _ = do guiData <- takeMVar mvarGUI let buf = redoBufferMVarGUIData guiData itemMay <- case buf of [] -> putMVar mvarGUI guiData >> return Nothing x:xs -> do putMVar mvarGUI guiData{ redoBufferMVarGUIData = xs , unDoReDoFlagMVarGUIData = True} return $ Just x reDo mvarGUI cmdDat itemMay guiData <- takeMVar mvarGUI putMVar mvarGUI guiData{unDoReDoFlagMVarGUIData = False} return True where reDo :: MVar MVarGUIData -> DebugCommandData -> Maybe UndoRedoData -> IO () reDo _ _ Nothing = return () reDo mvarGUI cmdDat (Just (DeleteRangeUndoRedoData path startLineNo startColNo _ _ str)) = do editorMay <- findTextEditorByPath mvarGUI path activateWithEditor mvarGUI cmdDat editorMay path startLineNo findTextEditorByPath mvarGUI path >>= \case Nothing -> errorM _LOG_NAME $ "[reDo]invalie note." Just editor -> do (endLineNo, endColNo) <- IF.searchEndIter editor startLineNo startColNo str IF.deleteRangeOnTextEditor editor startLineNo startColNo endLineNo endColNo IF.setCursorOnTextEditor editor startLineNo startColNo return () reDo mvarGUI cmdDat (Just (InsertTextUndoRedoData path startLineNo startColNo str)) = do editorMay <- findTextEditorByPath mvarGUI path activateWithEditor mvarGUI cmdDat editorMay path startLineNo findTextEditorByPath mvarGUI path >>= \case Nothing -> errorM _LOG_NAME $ "[reDo] invalie note." Just editor -> do IF.insertText2TextEditor editor startLineNo startColNo str (endLineNo, endColNo) <- IF.searchEndIter editor startLineNo startColNo str IF.setCursorOnTextEditor editor endLineNo endColNo return () codeTextKeyPressEventHandler _ _ _ _ _ _ _ = return False -- | -- イベントハンドラ -- codeBufferChangedEventHandler :: MVar MVarGUIData -> IO () codeBufferChangedEventHandler _ = return () -- | -- イベントハンドラ -- codeBufferDeleteRangeEventHandler :: MVar MVarGUIData -> (Int, Int) -> (Int, Int) -> String -> IO () codeBufferDeleteRangeEventHandler mvarGUI (siLineNo, siColNo) (eiLineNo, eiColNo) str = findCurrentTextEditor mvarGUI >>= \case Nothing -> errorM _LOG_NAME "[codeBufferDeleteRangeEventHandler] invalid text editor." Just a -> withEditor a where withEditor (nodeDat, _) = do guiData <- takeMVar mvarGUI let unDoBuf = undoBufferMVarGUIData guiData reDoBuf = redoBufferMVarGUIData guiData doing = unDoReDoFlagMVarGUIData guiData let delDat = DeleteRangeUndoRedoData { filePathDeleteRangeUndoRedoData = IF.getPathFromNodeData nodeDat , startLineNoDeleteRangeUndoRedoData = siLineNo , startColNoDeleteRangeUndoRedoData = siColNo , endLineNoDeleteRangeUndoRedoData = eiLineNo , endColNoDeleteRangeUndoRedoData = eiColNo , textDeleteRangeUndoRedoData = str } let newUndoBuf = if doing then unDoBuf else pushWithLimit unDoBuf delDat _UNDO_BUFFER_MAX_SIZE let newRedoBuf = if doing then reDoBuf else [] putMVar mvarGUI guiData{ undoBufferMVarGUIData = newUndoBuf , redoBufferMVarGUIData = newRedoBuf} -- | -- イベントハンドラ -- codeBufferInsertTextEventHandler :: MVar MVarGUIData -> (Int, Int) -> String -> IO () codeBufferInsertTextEventHandler mvarGUI (siLineNo, siColNo) str = findCurrentTextEditor mvarGUI >>= \case Nothing -> errorM _LOG_NAME "[codeBufferInsertTextEventHandler] invalid text editor." Just a -> withEditor a where withEditor (nodeDat, _) = do guiData <- takeMVar mvarGUI let unDoBuf = undoBufferMVarGUIData guiData reDoBuf = redoBufferMVarGUIData guiData doing = unDoReDoFlagMVarGUIData guiData let insertDat = InsertTextUndoRedoData { filePathInsertTextUndoRedoData = IF.getPathFromNodeData nodeDat , startLineNoInsertTextUndoRedoData = siLineNo , startColNoInsertTextUndoRedoData = siColNo , textInsertTextUndoRedoData = str } let newUndoBuf = if doing then unDoBuf else pushWithLimit unDoBuf insertDat _UNDO_BUFFER_MAX_SIZE let newRedoBuf = if doing then reDoBuf else [] putMVar mvarGUI guiData{ undoBufferMVarGUIData = newUndoBuf , redoBufferMVarGUIData = newRedoBuf} -- |===================================================================== -- Utility -- パーサ -- | -- コンソールに表示される文字列において、コードハイライトが可能な位置を -- 抽出するパーサ -- getActivatePosFromLine :: String -> Maybe HighlightTextRangeData getActivatePosFromLine res = go $ split " " res where go [] = Nothing go (x:xs) = case parse parseHighlightTextRange "getActivatePosFromLine" x of Left _ -> go xs Right bp -> Just bp -- | -- -- getHighlightTextRangeData :: String -> Either ParseError HighlightTextRangeData getHighlightTextRangeData = parse parseHighlightTextRange "getHighlightTextRangeData" -- | -- -- getStoppedTextRangeData :: String -> Either ParseError HighlightTextRangeData getStoppedTextRangeData = parse parser "getStoppedTextRangeData" where parser = do _ <- manyTill anyChar (string "Stopped at ") parseHighlightTextRange -- | -- parser of -- A) src\Phoityne\IO\Main.hs:31:11-14 -- B) src\Main.hs:(17,3)-(19,35) -- C) src\Phoityne\IO\Main.hs:31:11 -- parseHighlightTextRange :: forall u. ParsecT String u Identity HighlightTextRangeData parseHighlightTextRange = do path <- manyTill anyChar (string (_HS_FILE_EXT ++ ":")) (sl, sn, el, en) <- try parseA <|> try parseB <|> try parseC return $ HighlightTextRangeData (path ++ _HS_FILE_EXT) sl sn el en where parseA = do ln <- manyTill digit (char ':') sn <- manyTill digit (char '-') en <- try (manyTill digit endOfLine) <|> try (manyTill digit eof) return ((read ln), (read sn), (read ln), (read en)) parseB = do _ <- char '(' sl <- manyTill digit (char ',') sn <- manyTill digit (char ')') _ <- string "-(" el <- manyTill digit (char ',') en <- manyTill digit (char ')') return ((read sl), (read sn), (read el), (read en)) parseC = do ln <- manyTill digit (char ':') sn <- manyTill digit (char ':') return ((read ln), (read sn), (read ln), (read sn)) -- | -- バインディング値のパーサ -- -- parser of -- args :: Project.Argument.ArgData = _ -- _result :: IO Data.ConfigFile.Types.ConfigParser = _ -- getBindingDataList :: String -> Either ParseError [IF.BindingData] getBindingDataList res = parse parser "getBindingDataList" res where parser = manyTill parser1 (string _PHOITYNE_GHCI_PROMPT) parser1 = do varName <- manyTill anyChar (string "::") modName <- manyTill anyChar (try (string "=")) valStr <- manyTill anyChar lineSep <|> manyTill anyChar eof return $ IF.BindingData (strip varName) (strip modName) valStr lineSep = try $ endOfLine >> notFollowedBy space -- | -- トレース情報のパーサ -- -- parser of -- Phoityne>>= :history -- -1 : config:confB (src\Project\Argument.hs:85:17-28) -- -2 : config:confB (src\Project\Argument.hs:87:17-36) -- src\Project\IO\Main.hs:(70,9)-(71,65) -- getTraceDataList :: String -> Either ParseError [IF.TraceData] getTraceDataList res = go [] $ reverse $ filter (L.isPrefixOf "-") $ lines res where go acc [] = Right acc go acc (x:xs) = case parse parser "getTraceDataList" x of Left err -> Left err Right dat -> go (dat:acc) xs parser = do traceId <- manyTill anyChar (many1 space >> char ':' >> space) funcName <- manyTill anyChar (space >> char '(') filePath <- manyTill anyChar eof return $ IF.TraceData (strip traceId) funcName (init (strip filePath)) -- |===================================================================== -- Utility -- 検索 -- -- | -- 検索対象となったファイル群を共有データに保存する。 -- setSearchFiles :: MVar MVarGUIData -> [IF.NodeData] -> IO () setSearchFiles mvarGUI datas = do let paths = foldr (\d acc->IF.getPathFromNodeData d : acc) [] datas guiData <- takeMVar mvarGUI putMVar mvarGUI guiData { searchFilesMVarGUIData = paths } -- | -- 検索結果の削除 -- clearSearchResultTable :: MVar MVarGUIData -> IO () clearSearchResultTable mvarGUI = do guiData <- takeMVar mvarGUI let store = searchResultListStoreMVarGUIData guiData IF.clearSearchResultTable store putMVar mvarGUI guiData{searchResultListStoreMVarGUIData = store} -- | -- 検索結果テーブルにフォーカスをあてる。 -- activateSearchResultTab :: MVar MVarGUIData -> IO () activateSearchResultTab mvarGUI = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData IF.activateSearchResultTab builder -- | -- 複数ファイルにおいて、行単位のキーワード検索を行う -- keywordLineSearch :: [IF.NodeData] -> String -> (FilePath -> Int -> Int -> Int -> String -> IO ()) -> IO () keywordLineSearch [] _ _ = return () keywordLineSearch ((IF.FolderNodeData _ _ _):xs) key hdl = keywordLineSearch xs key hdl keywordLineSearch ((IF.FileNodeData _ _ path):xs) key hdl = searchFile path key hdl >> keywordLineSearch xs key hdl where -- | -- -- searchFile :: FilePath -> String -> (FilePath -> Int -> Int -> Int -> String -> IO ()) -> IO () searchFile path key hdl = do bs <- loadFile path searchLine (TE.pack key) (hdl path) 1 $ TE.lines $ TE.decodeUtf8 bs -- | -- -- searchLine :: TE.Text -> (Int -> Int -> Int -> String -> IO ()) -> Int -> [TE.Text] -> IO () searchLine _ _ _ [] = return () searchLine key hdl lineNo (line:lines) = do mapM_ go $ TE.breakOnAll key line searchLine key hdl (lineNo+1) lines where go (prior, _) = hdl lineNo (colIdx prior) (colIdx prior + TE.length key - 1) (TE.unpack line) colIdx txt = (TE.length txt) + 1 -- | -- 検索中にヒットした情報をストアに保存するハンドラ -- searchResultHandler :: MVar MVarGUIData -> FilePath -> Int -> Int -> Int -> String -> IO () searchResultHandler mvarGUI filePath lineNo startColNo endColNo line = do guiData <- takeMVar mvarGUI let store = searchResultListStoreMVarGUIData guiData IF.addSearchReslutTable store $ IF.SearchResultData filePath lineNo startColNo endColNo (strip line) putMVar mvarGUI guiData{searchResultListStoreMVarGUIData = store} -- | -- 行単位の置換を行う -- replaceFiles :: [IF.NodeData] -> String -> String -> IO () replaceFiles [] _ _ = return () replaceFiles ((IF.FolderNodeData _ _ _):xs) key rep = replaceFiles xs key rep replaceFiles ((IF.FileNodeData _ _ path):xs) key rep = replaceFile path key rep >> replaceFiles xs key rep where replaceFile :: FilePath -> String -> String -> IO () replaceFile path key rep = do bs <- loadFile path let res = TE.replace (TE.pack key) (TE.pack rep) $ TE.decodeUtf8 bs saveFile path $ TE.encodeUtf8 res -- |===================================================================== -- Utility -- デバッグ -- -- | -- ブレークポイントテーブルからブレークポイントを削除する -- deleteBreakPointOnBPTable :: MVar MVarGUIData -> IF.BreakPointDataKey -> IO () deleteBreakPointOnBPTable mvarGUI bpKey = do guiData <- readMVar mvarGUI let bpList = breakPointListMVarGUIData guiData IF.deleteFromBreakPointListStore bpList bpKey -- | -- ブレークポイントをGHCi上でdeleteする -- deleteBreakPointOnCUI :: MVar MVarGUIData -> DebugCommandData -> IF.BreakPointDataKey -> IO () deleteBreakPointOnCUI mvarGUI cmdData bpKey@(path, lineNo) = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData isDebug <- IF.isDebugStart builder when isDebug deleteBreakPointOnCUIInternal where deleteBreakPointOnCUIInternal = do guiData <- readMVar mvarGUI let bpList = breakPointListMVarGUIData guiData IF.findBreakPointData bpList bpKey >>= deleteBreakPointByBPData deleteBreakPointByBPData (Just (IF.BreakPointData _ _ _ (Just breakNo) _)) = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData deleteBreak = deleteBreakDebugCommandData cmdData getResult = readDebugCommandData cmdData cmdStr <- deleteBreak breakNo IF.putStrLnConsole builder cmdStr cmdStr <- getResult IF.putStrConsole builder cmdStr deleteBreakPointByBPData _ = errorM _LOG_NAME $ "invalid delete break point." ++ path ++ ":" ++ show lineNo -- | -- ブレークポイントテーブルにブレークポイントを追加する -- addBreakPointOnBPTable :: MVar MVarGUIData -> IF.BreakPointData -> IO () addBreakPointOnBPTable mvarGUI bpDat = do guiData <- readMVar mvarGUI let bpList = breakPointListMVarGUIData guiData IF.addBreakPoint2Table bpList bpDat -- | -- GHCi上でブレークポイントを追加する -- addBreakPointOnCUI :: MVar MVarGUIData -> DebugCommandData -> IF.BreakPointData -> IO () addBreakPointOnCUI mvarGUI cmdData breakData@(IF.BreakPointData modName path lineNo _ _) = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData IF.isDebugStart builder >>= \case False -> return () True -> addBreakPointInternl where addBreakPointInternl = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData setBreak = breakDebugCommandData cmdData getResult = readDebugCommandData cmdData cmdStr <- setBreak modName lineNo IF.putStrLnConsole builder cmdStr cmdStr <- getResult IF.putStrConsole builder cmdStr case getBreakPointNo cmdStr of Left err -> if L.isPrefixOf _NO_BREAK_POINT_LOCATION cmdStr then deleteBreakPoint else errorM _LOG_NAME $ "unexpected break set result. " ++ show err ++ cmdStr Right no -> updateBreakPointNo no deleteBreakPoint = do guiData <- readMVar mvarGUI let breakStore = breakPointListMVarGUIData guiData codeNoteMap = codeNoteMapMVarGUIData guiData treeStore = folderTreeMVarGUIData guiData nodeMay <- IF.findTreeNode treeStore (\node -> L.isSuffixOf path (IF.getPathFromNodeData node)) case nodeMay of Nothing -> errorM _LOG_NAME $ "node data not found." ++ show breakData Just node -> do IF.deleteFromBreakPointListStore breakStore (path, lineNo) case Map.lookup node codeNoteMap of Nothing -> return () Just editor -> IF.deleteBreakPointTag editor (IF.lineNoBreakPointData breakData) updateBreakPointNo no = do guiData <- readMVar mvarGUI let bpList = breakPointListMVarGUIData guiData key = (path, lineNo) IF.updateBreakPointTable bpList key breakData{ IF.breakNoBreakPointData = Just no } -- | -- parser of -- Breakpoint 0 activated at src\Main.hs:(21,3)-(23,35) -- getBreakPointNo :: String -> Either ParseError Int getBreakPointNo res = parse parser "getBreakPointNo" res where parser = do _ <- manyTill anyChar (string "Breakpoint ") no <- manyTill digit (string " activated at") return $ read no -- | -- -- deleteBreakPointTag :: MVar MVarGUIData -> IF.BreakPointDataKey -> IO () deleteBreakPointTag mvarGUI (path, lineNo) = findTextEditorByPath mvarGUI path >>= withEditor where withEditor Nothing = return () withEditor (Just (IF.TextEditorData lineView _ _ _)) = IF.deleteBreakPointTagAtLine lineView (lineNo - 1) -- |===================================================================== -- Utility -- テキストエディッタ -- -- | -- -- activateTextEditor :: MVar MVarGUIData -> DebugCommandData -> HighlightTextRangeData -> IO () activateTextEditor mvarGUI cmdDat pos = do let path = filePathHighlightTextRangeData pos lineNo = startLineNoHighlightTextRangeData pos editorMay <- findTextEditorByPath mvarGUI path activateWithEditor mvarGUI cmdDat editorMay path lineNo editorMay <- findTextEditorByPath mvarGUI path highLightBreakPoint mvarGUI pos editorMay guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData breakStore = breakPointListMVarGUIData guiData IF.highLightBreakPointTableRow builder breakStore ((filePathHighlightTextRangeData pos), (startLineNoHighlightTextRangeData pos)) where highLightBreakPoint _ _ Nothing = errorM _LOG_NAME $ "[highLightBreakPoint]invalid node map." highLightBreakPoint mvarGUI pos (Just textEditor) = do guiData <- readMVar mvarGUI let codeNoteMap = codeNoteMapMVarGUIData guiData mapM_ IF.offLightBreakPoint $ Map.elems codeNoteMap IF.highLightBreakPoint textEditor (startLineNoHighlightTextRangeData pos) (startColNoHighlightTextRangeData pos) (endLineNoHighlightTextRangeData pos) (endColNoHighlightTextRangeData pos) -- | -- -- activateWithEditor :: MVar MVarGUIData -> DebugCommandData -> Maybe IF.TextEditorData -> FilePath -> Int -> IO () activateWithEditor mvarGUI _ (Just editor) _ lineNo = do guiData <- takeMVar mvarGUI let builder = widgetStoreMVarGUIData guiData putMVar mvarGUI guiData IF.activateCodeNote builder editor lineNo activateWithEditor mvarGUI cmdDat Nothing filePath lineNo = do createCodeNode mvarGUI filePath lineNo setupBreakPointTags mvarGUI filePath where setupBreakPointTags mvarGUI path = findTextEditorByPath mvarGUI path >>= \case Nothing -> errorM _LOG_NAME $ "[setupBreakPointTags]invalid node map." Just (IF.TextEditorData lineView _ _ _) -> do guiData <- takeMVar mvarGUI let listStore = breakPointListMVarGUIData guiData breaks <- IF.getBreakPointList listStore mapM_ (addBreakPointTag lineView) $ filter (\(IF.BreakPointData _ filePath _ _ _)->L.isSuffixOf path filePath) breaks putMVar mvarGUI guiData addBreakPointTag lineView (IF.BreakPointData _ _ lineNo _ _) = IF.addBreakPointTagAtLine lineView (lineNo-1) createCodeNode mvarGUI path lineNo = do guiData <- takeMVar mvarGUI let treeStore = folderTreeMVarGUIData guiData nodeMay <- IF.findTreeNode treeStore (\node -> L.isSuffixOf path (IF.getPathFromNodeData node)) putMVar mvarGUI guiData createCodeNodeWithNodeMay mvarGUI lineNo nodeMay createCodeNodeWithNodeMay _ _ Nothing = errorM _LOG_NAME $ "[createCodeNodeWithNodeMay]unexpected error." createCodeNodeWithNodeMay mvarGUI lineNo (Just node) = createCodeNodeWithNode mvarGUI lineNo node createCodeNodeWithNode _ _ (IF.FolderNodeData _ _ _) = errorM _LOG_NAME $ "[createCodeNodeWithNode]unexpected error." createCodeNodeWithNode mvarGUI lineNo node@(IF.FileNodeData _ _ path) = do guiData <- takeMVar mvarGUI let builder = widgetStoreMVarGUIData guiData codeNoteMap = codeNoteMapMVarGUIData guiData code <- loadFile path wid <- IF.setupCodeNote builder (takeFileName path) path code (codeNoteCloseEventHanlder mvarGUI) (lineTextDoubleClickedHandler mvarGUI cmdDat) (Just lineNo) (codeTextKeyPressEventHandler mvarGUI cmdDat) (codeBufferChangedEventHandler mvarGUI) (codeBufferDeleteRangeEventHandler mvarGUI) (codeBufferInsertTextEventHandler mvarGUI) let newMap = Map.insert node wid codeNoteMap putMVar mvarGUI $ guiData { codeNoteMapMVarGUIData = newMap} IF.activateCodeNote builder wid lineNo -- | -- -- activateTextEditorWithSearchResult :: MVar MVarGUIData -> DebugCommandData -> Maybe IF.SearchResultOffset -> IO () activateTextEditorWithSearchResult mvarGUI cmdDat offset = findCurrentTextEditor mvarGUI >>= \case Nothing -> errorM _LOG_NAME "[activateTextEditorWithSearchResult] invalid text editor" Just a -> withEditor a where withEditor curEditor@(nodeDat, _) = do guiData <- readMVar mvarGUI let files = searchFilesMVarGUIData guiData hasSearched curEditor $ L.elem (IF.getPathFromNodeData nodeDat) files hasSearched _ True = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData store = searchResultListStoreMVarGUIData guiData IF.nextCurrentSearchResult builder store offset activateTextEditorWithCurrentSearchResult mvarGUI cmdDat hasSearched curEditor False = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData key <- IF.getSearchKeyFromSearchDialog builder isSearchKey curEditor key isSearchKey (nodeData, editor) key | null key = mainWindowKeyPressEventHandler mvarGUI cmdDat "f" False True >> return () | otherwise = do clearSearchResultTable mvarGUI activateSearchResultTab mvarGUI setSearchFiles mvarGUI [nodeData] keywordLineSearch [nodeData] key (searchResultHandler mvarGUI) (lineNo, _) <- IF.getCodeTextLineNumber editor activateTextEditorWithSearchResult mvarGUI cmdDat $ Just (IF.getPathFromNodeData nodeData, lineNo+1) activateTextEditorWithCurrentSearchResult :: MVar MVarGUIData -> DebugCommandData -> IO () activateTextEditorWithCurrentSearchResult mvarGUI cmdData = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData store = searchResultListStoreMVarGUIData guiData IF.getCurrentSearchResult builder store >>= \case Nothing -> return () Just (IF.SearchResultData filePath lineNo startCol endCol _) -> do activateTextEditor mvarGUI cmdData $ HighlightTextRangeData filePath lineNo startCol lineNo endCol -- | -- -- findTextEditorByPath :: MVar MVarGUIData -> FilePath -> IO (Maybe IF.TextEditorData) findTextEditorByPath mvarGUI path = do guiData <- readMVar mvarGUI let codeNoteMap = codeNoteMapMVarGUIData guiData nodeMay = L.find (\node->L.isSuffixOf path (IF.getPathFromNodeData node)) $ Map.keys codeNoteMap case nodeMay of Just node -> return . Just $ codeNoteMap Map.! node Nothing -> return Nothing -- | -- -- findCurrentTextEditor :: MVar MVarGUIData -> IO (Maybe (IF.NodeData, IF.TextEditorData)) findCurrentTextEditor mvarGUI = do guiData <- readMVar mvarGUI let builder = widgetStoreMVarGUIData guiData noteMap = codeNoteMapMVarGUIData guiData findCurrent builder $ Map.toList noteMap where findCurrent _ [] = return Nothing findCurrent builder ((n, e):xs) = do IF.isCurrentTextEditor builder e >>= \case True -> return $ Just (n, e) False -> findCurrent builder xs -- |===================================================================== -- Utility -- -- -- | -- すべての変更ファイルを保存する -- saveAll :: MVar MVarGUIData -> DebugCommandData -> IO () saveAll mvarGUI _ = do guiData <- readMVar mvarGUI let noteMap = codeNoteMapMVarGUIData guiData mapM_ go $ Map.toList noteMap where go (nodeDat, editor) = do isModified <- IF.isTextEditorModified editor when isModified $ do content <- IF.getCodeViewContent editor saveFile (IF.getPathFromNodeData nodeDat) content IF.setTextEditorModified editor False -- | -- すべての開いているテキストエディッタのコンテンツを再読み込みする。 -- reloadAll :: MVar MVarGUIData -> IO () reloadAll mvarGUI = do guiData <- readMVar mvarGUI let noteMap = codeNoteMapMVarGUIData guiData mapM_ go $ Map.toList noteMap where go (nodeDat, editor) = do let path = IF.getPathFromNodeData nodeDat bs <- loadFile path (lineNo, colNo) <- IF.getCodeTextLineNumber editor guiData <- takeMVar mvarGUI putMVar mvarGUI guiData{unDoReDoFlagMVarGUIData = True} IF.setContent2TextEditor editor bs guiData <- takeMVar mvarGUI putMVar mvarGUI guiData{unDoReDoFlagMVarGUIData = False} IF.setCursorOnTextEditor editor lineNo colNo -- | -- フォルダツリー全体の読み込み -- loadFolderForest :: String -> [FilePath] -> IO (T.Tree IF.NodeData) loadFolderForest _ paths = do topNodes <- foldM go [] $ reverse paths return $ head topNodes -- return $ T.Node (IF.FolderNodeData forestName forestName "") topNodes where go acc path = do tree <- loadFolderTree "" path return $ tree:acc -- | -- フォルダツリーの読み込み -- loadFolderTree :: ModuleName -> FilePath -> IO (T.Tree IF.NodeData) loadFolderTree modName path = doesDirectoryExist path >>= withDir where withDir False = do errorM _LOG_NAME $ "invalid dirctory:" ++ path return $ T.Node (IF.FolderNodeData modName ("invalid directory") path) [] withDir True = do let dirName = takeFileName path node = T.Node (IF.FolderNodeData modName (""++dirName++"") path) [] items <- getDirectoryContents path foldM setFolderItem node $ normalizeList items normalizeList :: [FilePath] -> [FilePath] normalizeList fs = files ++ dirs where items = filter (\s->'.' /= head s) fs dirs = filter (\s-> all ((/=) '.') s) items files = filter (\s-> any ((==) '.') s) items setFolderItem :: T.Tree IF.NodeData -> FilePath -> IO (T.Tree IF.NodeData) setFolderItem node item = do let fullPath = path item let baseName = takeBaseName item let nzModName = getNzModName modName baseName -- if True == null modName then baseName else modName <.> baseName isDir <- doesDirectoryExist fullPath setFolderItem_ node nzModName fullPath isDir getNzModName modName baseName | null modName = getNzModNameWithNullModName baseName | otherwise = modName <.> baseName getNzModNameWithNullModName baseName | null baseName = "" | isUpper (head baseName) = baseName | otherwise = "" setFolderItem_ :: T.Tree IF.NodeData -> ModuleName -> FilePath -> Bool -> IO (T.Tree IF.NodeData) setFolderItem_ node nzModName fullPath isDir | True == isDir = do child <- loadFolderTree nzModName fullPath return $ addChildTree node child | otherwise = do let fileExt = takeExtension fullPath setHsItem node nzModName fullPath fileExt setHsItem :: T.Tree IF.NodeData -> ModuleName -> FilePath -> String -> IO (T.Tree IF.NodeData) setHsItem node nzModName fullPath fileExt | elem fileExt _AVAILABLE_FILE_EXT = do let fileName = takeFileName fullPath let child = T.Node (IF.FileNodeData nzModName (""++fileName++"") fullPath) [] return $ addChildTree node child | otherwise = return node