phoityne-vscode-0.0.2.0: app/Phoityne/IO/GUI/Control.hs
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE BinaryLiterals #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DeriveDataTypeable #-}
module Phoityne.IO.GUI.Control (
run
, DebugCommandData(..)
) where
import Phoityne.Constant
import Phoityne.Utility
import qualified Phoityne.IO.GUI.VSCode.TH.BreakpointJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.ContinueRequestJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.ContinueResponseJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.DisconnectRequestJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.DisconnectResponseJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.EvaluateArgumentsJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.EvaluateBodyJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.EvaluateRequestJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.EvaluateResponseJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.InitializedEventJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.InitializeRequestJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.InitializeResponseCapabilitesJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.InitializeResponseJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.LaunchRequestArgumentsJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.LaunchRequestJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.LaunchResponseJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.NextRequestJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.NextResponseJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.OutputEventJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.OutputEventBodyJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.PauseRequestJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.PauseResponseJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.RequestJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.ScopesArgumentsJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.ScopesRequestJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.ScopesResponseJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.SetBreakpointsRequestArgumentsJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.SetBreakpointsRequestJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.SetBreakpointsResponseBodyJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.SetBreakpointsResponseJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.SourceBreakpointJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.SourceJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.SourceRequestJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.SourceResponseJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.StackFrameJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.StackTraceBodyJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.StackTraceRequestJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.StackTraceResponseJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.StepInRequestJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.StepInResponseJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.StepOutRequestJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.StepOutResponseJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.StoppedEventJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.TerminatedEventJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.TerminatedEventBodyJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.ThreadsRequestJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.ThreadsResponseJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.VariableJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.VariablesBodyJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.VariablesRequestJSON as J
import qualified Phoityne.IO.GUI.VSCode.TH.VariablesResponseJSON as J
import System.IO
import System.Exit
import System.FilePath
import System.Log.Logger
import qualified Data.Aeson as J
import qualified Data.ByteString.Lazy as BSL
import Text.Parsec
import Data.String.Utils
import qualified Data.List as L
import qualified Control.Exception as E
import qualified Data.Map as MAP
import Control.Concurrent
import Data.List.Split
import Data.Char
import Data.Maybe
import Data.Functor.Identity
import Control.Monad
import qualified System.FSNotify as FSN
-- |
--
--
data DebugContext =
DebugContext {
resSeqDebugContext :: Int
, breakPointDatasDebugContext :: BreakPointDatas
, workspaceDebugContext :: FilePath
, startupDebugContext :: FilePath
, debugStartedDebugContext :: Bool
, debugStoppedPosDebugContext :: Maybe HighlightTextRangeData
, currentFrameIdDebugContext :: Int
, modifiedDebugContext :: Bool
} deriving (Show, Read, Eq, Ord)
-- |
--
--
data DebugCommandData =
DebugCommandData {
startDebugCommandData :: FilePath -> 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 :: FilePath -> IO ()
, cleanStartDebugCommandData :: FilePath -> IO ()
, loadFileDebugCommandData :: FilePath -> IO String
, readWhileDebugCommandData :: (String -> Bool) -> IO String
, infoDebugCommandData :: String -> IO String
}
-- |
--
--
data HighlightTextRangeData = HighlightTextRangeData {
filePathHighlightTextRangeData :: FilePath
, startLineNoHighlightTextRangeData :: Int
, startColNoHighlightTextRangeData :: Int
, endLineNoHighlightTextRangeData :: Int
, endColNoHighlightTextRangeData :: Int
} deriving (Show, Read, Eq, Ord)
-- |
--
--
data BreakPointData =
BreakPointData {
moduleNameBreakPointData :: String
, filePathBreakPointData :: FilePath
, lineNoBreakPointData :: Int
, breakNoBreakPointData :: Maybe Int
, conditionBreakPointData :: Maybe String
} deriving (Show, Read, Eq, Ord)
-- |
--
--
data TraceData = TraceData {
traceIdTraceData :: String
, functionTraceData :: String
, filePathTraceData :: String
} deriving (Show, Read, Eq, Ord)
-- |
--
--
data BindingData = BindingData {
varNameBindingData :: String
, modNameBindingData :: String
, valueBindingData :: String
} deriving (Show, Read, Eq, Ord)
-- |
--
--
type BreakPointDataKey = (FilePath, Int)
type BreakPointDatas = MAP.Map BreakPointDataKey BreakPointData
-- |
--
--
_INITIAL_RESPONSE_SEQUENCE :: Int
_INITIAL_RESPONSE_SEQUENCE = 0
-- |
--
--
_TWO_CRLF :: String
_TWO_CRLF = "\r\n\r\n"
-- |
--
--
_SEP_WIN :: Char
_SEP_WIN = '\\'
_SEP_UNIX :: Char
_SEP_UNIX = '/'
-- |
--
--
defaultDebugContext :: DebugContext
defaultDebugContext = DebugContext _INITIAL_RESPONSE_SEQUENCE (MAP.fromList []) "" "" False Nothing 0 False
-- |
--
--
getKeyOfHighlightTextRangeData :: HighlightTextRangeData -> BreakPointDataKey
getKeyOfHighlightTextRangeData (HighlightTextRangeData file line _ _ _) = (file, line)
-- |
--
--
run :: DebugCommandData -> IO ()
run cmdData = do
hSetBuffering stdin NoBuffering
hSetEncoding stdin utf8
hSetBuffering stdout NoBuffering
hSetEncoding stdout utf8
mvarCtx <- newMVar defaultDebugContext
wait cmdData mvarCtx
-- |
--
--
wait :: DebugCommandData -> MVar DebugContext -> IO ()
wait cmdData ctx = go BSL.empty
where
go :: BSL.ByteString -> IO ()
go buf = do
c <- BSL.hGet stdin 1
let newBuf = BSL.append buf c
case readContentLength (lbs2str newBuf) of
Left _ -> go newBuf
Right len -> do
infoM _LOG_NAME $ "[REQUEST] Content-Length: " ++ show len
cnt <- BSL.hGet stdin len
infoM _LOG_NAME $ "[REQUEST] " ++ lbs2str cnt
handleRequest cmdData ctx cnt
where
readContentLength :: String -> Either ParseError Int
readContentLength = parse parser "readContentLength"
parser = do
string "Content-Length: "
len <- manyTill digit (string _TWO_CRLF)
return . read $ len
-- |
--
--
handleRequest :: DebugCommandData -> MVar DebugContext -> BSL.ByteString -> IO ()
handleRequest cmdData mvarCtx jsonStr = case J.eitherDecode jsonStr :: Either String J.Request of
Left err -> errRes jsonStr $ "requet parse error. [" ++ err ++ "]"
Right jsonDat -> sucRes jsonStr jsonDat
where
errRes :: BSL.ByteString -> String -> IO ()
errRes jsonStr msg = do
errorM _LOG_NAME $ msg ++ " [" ++ lbs2str jsonStr ++ "]"
wait cmdData mvarCtx
sucRes jsonStr (J.Request cmd) = do
handle jsonStr cmd
wait cmdData mvarCtx
handle jsonStr "initialize" = case J.eitherDecode jsonStr :: Either String J.InitializeRequest of
Left err -> errRes jsonStr $ "initialize parse error. [" ++ err ++ "]"
Right req -> initializeHandler mvarCtx req
handle jsonStr "launch" = case J.eitherDecode jsonStr :: Either String J.LaunchRequest of
Left err -> errRes jsonStr $ "launch parse error. [" ++ err ++ "]"
Right req -> launchHandler cmdData mvarCtx req
handle jsonStr "disconnect" = case J.eitherDecode jsonStr :: Either String J.DisconnectRequest of
Left err -> errRes jsonStr $ "disconnect parse error. [" ++ err ++ "]"
Right req -> disconnectHandler cmdData mvarCtx req
handle jsonStr "setBreakpoints" = case J.eitherDecode jsonStr :: Either String J.SetBreakpointsRequest of
Left err -> errRes jsonStr $ "setBreakpoints parse error. [" ++ err ++ "]"
Right req -> setBreakpointsHandler cmdData mvarCtx req
handle jsonStr "continue" = case J.eitherDecode jsonStr :: Either String J.ContinueRequest of
Left err -> errRes jsonStr $ "continue parse error. [" ++ err ++ "]"
Right req -> continueHandler cmdData mvarCtx req
handle jsonStr "next" = case J.eitherDecode jsonStr :: Either String J.NextRequest of
Left err -> errRes jsonStr $ "next parse error. [" ++ err ++ "]"
Right req -> stepOverHandler cmdData mvarCtx req
handle jsonStr "stepIn" = case J.eitherDecode jsonStr :: Either String J.StepInRequest of
Left err -> errRes jsonStr $ "stepIn parse error. [" ++ err ++ "]"
Right req -> stepInHandler cmdData mvarCtx req
handle jsonStr "stepOut" = case J.eitherDecode jsonStr :: Either String J.StepOutRequest of
Left err -> errRes jsonStr $ "stepOut parse error. [" ++ err ++ "]"
Right req -> do
resSeq <- incResSeq mvarCtx
let res = J.defaultStepOutResponse resSeq req
resStr = J.encode $ res{J.successStepOutResponse = False, J.messageStepOutResponse = "unsupported command."}
sendResponse resStr
putStrLnStderr mvarCtx "stepout command is not supported."
handle jsonStr "pause" = case J.eitherDecode jsonStr :: Either String J.PauseRequest of
Left err -> errRes jsonStr $ "pause parse error. [" ++ err ++ "]"
Right req -> do
resSeq <- incResSeq mvarCtx
let res = J.defaultPauseResponse resSeq req
resStr = J.encode $ res{J.successPauseResponse = False, J.messagePauseResponse = "unsupported command."}
sendResponse resStr
putStrLnStderr mvarCtx "pause command is not supported."
handle jsonStr "stackTrace" = case J.eitherDecode jsonStr :: Either String J.StackTraceRequest of
Left err -> errRes jsonStr $ "stackTrace parse error. [" ++ err ++ "]"
Right req -> stackTraceHandler cmdData mvarCtx req
handle jsonStr "scopes" = case J.eitherDecode jsonStr :: Either String J.ScopesRequest of
Left err -> errRes jsonStr $ "scopes parse error. [" ++ err ++ "]"
Right req -> scopesHandler cmdData mvarCtx req
handle jsonStr "variables" = case J.eitherDecode jsonStr :: Either String J.VariablesRequest of
Left err -> errRes jsonStr $ "variables parse error. [" ++ err ++ "]"
Right req -> variablesHandler cmdData mvarCtx req
handle jsonStr "source" = case J.eitherDecode jsonStr :: Either String J.SourceRequest of
Left err -> errRes jsonStr $ "source parse error. [" ++ err ++ "]"
Right req -> do
resSeq <- incResSeq mvarCtx
let res = J.defaultSourceResponse resSeq req
resStr = J.encode $ res{J.successSourceResponse = False, J.messageSourceResponse = "unsupported command."}
sendResponse resStr
putStrLnStderr mvarCtx "source command is not supported."
handle jsonStr "threads" = case J.eitherDecode jsonStr :: Either String J.ThreadsRequest of
Left err -> errRes jsonStr $ "threads parse error. [" ++ err ++ "]"
Right req -> threadsHandler cmdData mvarCtx req
handle jsonStr "evaluate" = case J.eitherDecode jsonStr :: Either String J.EvaluateRequest of
Left err -> errRes jsonStr $ "evaluate parse error. [" ++ err ++ "]"
Right req -> evaluateHandler cmdData mvarCtx req
handle jsonStr cmd = do
putStrLnStderr mvarCtx $ cmd ++ " command is not supported."
putStrLnStderr mvarCtx $ lbs2str jsonStr ++ " is requested."
-- |
--
sendEvent :: BSL.ByteString -> IO ()
sendEvent str = do
infoM _LOG_NAME $ "[EVENT] Content-Length: " ++ show (BSL.length str)
infoM _LOG_NAME $ "[EVENT] " ++ lbs2str str
sendResponseInternal str
-- |
--
sendResponse :: BSL.ByteString -> IO ()
sendResponse str = do
infoM _LOG_NAME $ "[RESPONSE] Content-Length: " ++ show (BSL.length str)
infoM _LOG_NAME $ "[RESPONSE] " ++ lbs2str str
sendResponseInternal str
-- |
--
sendResponseInternal :: BSL.ByteString -> IO ()
sendResponseInternal str = do
BSL.hPut stdout $ BSL.append "Content-Length: " $ str2lbs $ show (BSL.length str)
BSL.hPut stdout $ str2lbs _TWO_CRLF
BSL.hPut stdout str
hFlush stdout
-- |=====================================================================
--
-- Handlers
-- |
--
initializeHandler :: MVar DebugContext -> J.InitializeRequest -> IO ()
initializeHandler mvarCtx (J.InitializeRequest seq _ _ _) = flip E.catches handlers $ do
resSeq <- incResSeq mvarCtx
let capa = J.InitializeResponseCapabilites False False True False []
res = J.InitializeResponse resSeq "response" seq True "initialize" "" capa
sendResponse $ J.encode res
where
handlers = [ E.Handler someExcept ]
someExcept (e :: E.SomeException) = do
let msg = "initializeHandler:" ++ show e
errorM _LOG_NAME msg
-- |
--
launchHandler :: DebugCommandData -> MVar DebugContext -> J.LaunchRequest -> IO ()
launchHandler cmdData mvarCtx req@(J.LaunchRequest _ _ _ args) = flip E.catches handlers $ do
let ws = J.workspaceLaunchRequestArguments args
su = J.startupLaunchRequestArguments args
ctx <- takeMVar mvarCtx
putMVar mvarCtx ctx {
workspaceDebugContext = ws
, startupDebugContext = su
}
runGHCi cmdData mvarCtx ws
loadHsFile cmdData mvarCtx su
resSeq <- incResSeq mvarCtx
sendResponse $ J.encode $ J.defaultLaunchResponse resSeq req
resSeq <- incResSeq mvarCtx
sendEvent $ J.encode $ J.defaultInitializedEvent resSeq
watch cmdData mvarCtx
let infoMsg = [
""
, " Now, ghci initialized."
, " Press F5 to start debugging."
, " Or modify source code. it will be loaded to debugger automatically."
, ""
]
putStrLnConsole mvarCtx $ L.intercalate "\n" infoMsg
putStrStdout mvarCtx _PHOITYNE_GHCI_PROMPT
where
handlers = [ E.Handler someExcept ]
someExcept (e :: E.SomeException) = do
let msg = "launchHandler:" ++ show e
errorM _LOG_NAME msg
-- |
--
--
watch :: DebugCommandData -> MVar DebugContext -> IO ()
watch cmdData mvarCtx = do
_ <- forkIO $ watchFiles cmdData mvarCtx
return ()
watchFiles :: DebugCommandData -> MVar DebugContext -> IO ()
watchFiles cmdData mvarCtx = do
FSN.withManagerConf FSN.defaultConfig{FSN.confDebounce = FSN.Debounce 1} $ \mgr -> do
ctx <- readMVar mvarCtx
let dir = workspaceDebugContext ctx
infoM _LOG_NAME $ "start watch files in [" ++ dir ++ "]"
_ <- FSN.watchTree mgr dir hsFilter action
forever $ threadDelay 1000000
return ()
where
hsFilter event = endswith _HS_FILE_EXT $ FSN.eventPath event
action event = do
ctx <- readMVar mvarCtx
withDebugStarted event $ debugStartedDebugContext ctx
withDebugStarted _ True = do
resSeq <- incResSeq mvarCtx
let terminatedEvt = J.defaultTerminatedEvent resSeq
terminatedEvtStr = J.encode terminatedEvt{J.bodyTerminatedEvent = J.TerminatedEventBody True}
sendEvent terminatedEvtStr
withDebugStarted event False = do
ctx <- takeMVar mvarCtx
putMVar mvarCtx ctx{modifiedDebugContext = True}
loadHsFile cmdData mvarCtx (FSN.eventPath event) >> return ()
-- |
--
disconnectHandler :: DebugCommandData -> MVar DebugContext -> J.DisconnectRequest -> IO ()
disconnectHandler cmdData mvarCtx req = flip E.catches handlers $ do
let exitCmd = stopDebugCommandData cmdData
quitCmd = quitDebugCommandData cmdData
readWhile = readWhileDebugCommandData cmdData
cmdStr <- quitCmd
infoM _LOG_NAME cmdStr
putStrLnStdout mvarCtx cmdStr
str <- readWhile $ const True
infoM _LOG_NAME str
putStrLnStdout mvarCtx str
code <- exitCmd
infoM _LOG_NAME $ show code
putStrLnStdout mvarCtx $ show code
resSeq <- incResSeq mvarCtx
sendResponse $ J.encode $ J.defaultDisconnectResponse resSeq req
where
handlers = [ E.Handler someExcept ]
someExcept (e :: E.SomeException) = do
let msg = "disconnectHandler:" ++ show e
errorM _LOG_NAME msg
putStrLnStderr mvarCtx msg
-- |
--
setBreakpointsHandler :: DebugCommandData -> MVar DebugContext -> J.SetBreakpointsRequest -> IO ()
setBreakpointsHandler cmdData mvarCtx req = flip E.catches handlers $ do
ctx <- readMVar mvarCtx
let cwd = workspaceDebugContext ctx
args = J.argumentsSetBreakpointsRequest req
source = J.sourceSetBreakpointsRequestArguments args
path = J.pathSource source
reqBps = J.breakpointsSetBreakpointsRequestArguments args
bps = map (convBp cwd path) reqBps
delete path
resBody <- insert bps
resSeq <- incResSeq mvarCtx
let res = J.defaultSetBreakpointsResponse resSeq req
resStr = J.encode res{J.bodySetBreakpointsResponse = J.SetBreakpointsResponseBody resBody}
sendResponse resStr
resSeq <- incResSeq mvarCtx
let stopEvt = J.defaultStoppedEvent resSeq
stopEvtStr = J.encode stopEvt
sendEvent stopEvtStr
where
handlers = [ E.Handler someExcept ]
someExcept (e :: E.SomeException) = do
let msg = "setBreakpointsHandler:" ++ show e
errorM _LOG_NAME msg
convBp cwd path (J.SourceBreakpoint lineNo _ cond) =
BreakPointData {
moduleNameBreakPointData = src2mod cwd path
, filePathBreakPointData = path
, lineNoBreakPointData = lineNo
, breakNoBreakPointData = Nothing
, conditionBreakPointData = cond
}
delete path = do
ctx <- takeMVar mvarCtx
let bps = breakPointDatasDebugContext ctx
newBps = MAP.filterWithKey (\(p,_) _-> path /= p) bps
delBps = MAP.elems $ MAP.filterWithKey (\(p,_) _-> path == p) bps
putMVar mvarCtx ctx{breakPointDatasDebugContext = newBps}
debugM _LOG_NAME $ "del bps:" ++ show delBps
mapM_ (deleteBreakPointOnCUI cmdData mvarCtx) delBps
insert reqBps = do
results <- mapM insertInternal reqBps
let addBps = filter (\(_, (J.Breakpoint _ verified _ _ _ _)) -> verified) results
resData = map snd results
debugM _LOG_NAME $ "add bps:" ++ show addBps
debugM _LOG_NAME $ "response bps:" ++ show resData
ctx <- takeMVar mvarCtx
let bps = breakPointDatasDebugContext ctx
newBps = foldr (\v@(BreakPointData _ p l _ _)->MAP.insert (p,l) v) bps $ map fst results
putMVar mvarCtx ctx{breakPointDatasDebugContext = newBps}
return resData
insertInternal reqBp@(BreakPointData modName filePath lineNo _ _) = do
let src = J.Source (Just modName) filePath Nothing Nothing
addBreakPointOnCUI cmdData mvarCtx reqBp >>= \case
Right no -> do
--putStrLnStdout mvarCtx $ "set breakpoint on " ++ filePathBreakPointData reqBp ++ ":L" ++ show (lineNoBreakPointData reqBp)
return (reqBp{breakNoBreakPointData = Just no}, J.Breakpoint (Just no) True "" src lineNo 1)
Left err -> return (reqBp, J.Breakpoint Nothing False err src lineNo 1)
-- |
--
--
threadsHandler :: DebugCommandData -> MVar DebugContext -> J.ThreadsRequest -> IO ()
threadsHandler _ mvarCtx req = flip E.catches handlers $ do
resSeq <- incResSeq mvarCtx
let resStr = J.encode $ J.defaultThreadsResponse resSeq req
sendResponse resStr
where
handlers = [ E.Handler someExcept ]
someExcept (e :: E.SomeException) = do
let msg = "threadsHandler:" ++ show e
errorM _LOG_NAME msg
-- |
--
--
scopesHandler :: DebugCommandData -> MVar DebugContext -> J.ScopesRequest -> IO ()
scopesHandler cmdData mvarCtx req = flip E.catches handlers $ do
let args = J.argumentsScopesRequest req
traceId = J.frameIdScopesArguments args
moveFrame cmdData mvarCtx traceId
resSeq <- incResSeq mvarCtx
let resStr = J.encode $ J.defaultScopesResponse resSeq req
sendResponse resStr
where
handlers = [ E.Handler someExcept ]
someExcept (e :: E.SomeException) = do
let msg = "scopesHandler:" ++ show e
errorM _LOG_NAME msg
-- |
--
--
moveFrame :: DebugCommandData -> MVar DebugContext -> Int -> IO ()
moveFrame cmdData mvarCtx traceId = do
ctx <- readMVar mvarCtx
let curTraceId = currentFrameIdDebugContext ctx
moveCount = curTraceId - traceId
traceCmd = if 0 > moveCount then traceForwardDebugCommandData cmdData
else traceBackDebugCommandData cmdData
getResult = readDebugCommandData cmdData
_ <- foldM (go traceCmd getResult) (""::String) [1..(abs moveCount)]
ctx <- takeMVar mvarCtx
putMVar mvarCtx ctx{currentFrameIdDebugContext = traceId}
where
go traceCmd getResult _ _ = do
cmdStr <- traceCmd
infoM _LOG_NAME cmdStr
putStrLnStdout mvarCtx cmdStr
cmdStr <- getResult
infoM _LOG_NAME cmdStr
putStrStdout mvarCtx cmdStr
return cmdStr
-- |
--
--
variablesHandler :: DebugCommandData -> MVar DebugContext -> J.VariablesRequest -> IO ()
variablesHandler cmdData mvarCtx req = flip E.catches handlers $ do
vals <- currentBindings
resSeq <- incResSeq mvarCtx
let res = J.defaultVariablesResponse resSeq req
resStr = J.encode $ res{J.bodyVariablesResponse = J.VariablesBody vals}
sendResponse resStr
where
handlers = [ E.Handler someExcept ]
someExcept (e :: E.SomeException) = do
let msg = "scopesHandler:" ++ show e
errorM _LOG_NAME msg
currentBindings = do
let bindings = bindingsDebugCommandData cmdData
getResult = readDebugCommandData cmdData
cmdStr <- bindings
infoM _LOG_NAME cmdStr
-- putStrLnStdout mvarCtx cmdStr
bindStr <- getResult
infoM _LOG_NAME bindStr
-- putStrStdout mvarCtx bindStr
case getBindingDataList bindStr of
Left err -> do
errorM _LOG_NAME $ show err
putStrLnStderr mvarCtx $ show err
return []
Right dats -> return $ map convBind2Vals dats
convBind2Vals (BindingData varName _ val) = J.Variable varName val 0
-- |
--
--
stackTraceHandler :: DebugCommandData -> MVar DebugContext -> J.StackTraceRequest -> IO ()
stackTraceHandler cmdData mvarCtx req = flip E.catches handlers $ do
ctx <- readMVar mvarCtx
case debugStoppedPosDebugContext ctx of
Nothing -> do
resSeq <- incResSeq mvarCtx
let body = J.StackTraceBody [] 0
res = J.defaultStackTraceResponse resSeq req
resStr = J.encode $ res{J.bodyStackTraceResponse = body}
sendResponse resStr
Just rangeData -> do
resSeq <- incResSeq mvarCtx
frames <- createStackFrames rangeData
debugM _LOG_NAME $ show frames
let body = J.StackTraceBody (reverse frames) (length frames)
res = J.defaultStackTraceResponse resSeq req
resStr = J.encode $ res{J.bodyStackTraceResponse = body}
sendResponse resStr
where
handlers = [ E.Handler someExcept ]
someExcept (e :: E.SomeException) = do
let msg = "stackTraceHandler:" ++ show e
errorM _LOG_NAME msg
createStackFrames (HighlightTextRangeData file sl sc _ _) = do
ctx <- readMVar mvarCtx
let cwd = workspaceDebugContext ctx
csf = J.StackFrame 0 "0" (J.Source (Just (src2mod cwd file)) file Nothing Nothing) sl sc
let getResult = readDebugCommandData cmdData
history = traceHistDebugCommandData cmdData
cmdStr <- history
infoM _LOG_NAME cmdStr
-- putStrLnStdout mvarCtx cmdStr
traceStr <- getResult
infoM _LOG_NAME traceStr
-- putStrStdout mvarCtx traceStr
case getTraceDataList traceStr of
Left err -> do
errorM _LOG_NAME $ show err
-- putStrLnStderr mvarCtx $ show err
return [csf]
Right dats -> foldM (convTrace2Frame cwd) [csf] dats
convTrace2Frame cwd xs (TraceData traceId _ filePath) = case parse parseHighlightTextRange "getActivatePosFromLine" filePath of
Left err -> do
infoM _LOG_NAME $ show err
return xs
Right (HighlightTextRangeData file sl sc _ _) -> return $
J.StackFrame (read traceId) traceId (J.Source (Just (src2mod cwd file)) file Nothing Nothing) sl sc : xs
-- |
--
--
evaluateHandler :: DebugCommandData -> MVar DebugContext -> J.EvaluateRequest -> IO ()
evaluateHandler cmdData mvarCtx req = flip E.catches handlers $ do
let (J.EvaluateArguments exp frameId ctx) = J.argumentsEvaluateRequest req
moveFrame cmdData mvarCtx $ if isJust frameId then fromJust frameId else 0
eval ctx exp
where
handlers = [ E.Handler someExcept ]
someExcept (e :: E.SomeException) = do
let msg = "continueHandler:" ++ show e
errorM _LOG_NAME msg
eval "watch" exp = do
let forceVar = forceDebugCommandData cmdData
getResult = readDebugCommandData cmdData
cmdStr <- forceVar exp
infoM _LOG_NAME cmdStr
-- putStrLnStdout mvarCtx cmdStr
cmdStr <- getResult
infoM _LOG_NAME cmdStr
-- putStrStdout mvarCtx cmdStr
let result = L.intercalate " " . filter (not . null) . map strip . init . lines $ cmdStr
resSeq <- incResSeq mvarCtx
let body = J.EvaluateBody result 0
res = J.defaultEvaluateResponse resSeq req
resStr = J.encode res{J.bodyEvaluateResponse = body}
sendResponse resStr
eval "hover" exp = do
let info = infoDebugCommandData cmdData
getResult = readDebugCommandData cmdData
cmdStr <- info exp
infoM _LOG_NAME cmdStr
cmdStr <- getResult
infoM _LOG_NAME cmdStr
let result = L.intercalate " " . filter (not . null) . map strip . init . lines $ cmdStr
resSeq <- incResSeq mvarCtx
let body = J.EvaluateBody result 0
res = J.defaultEvaluateResponse resSeq req
resStr = J.encode res{J.bodyEvaluateResponse = body}
sendResponse resStr
eval _ exp = do
let exec = execCommandData cmdData
getResult = readDebugCommandData cmdData
cmdStr <- exec exp
infoM _LOG_NAME cmdStr
cmdStr <- getResult
infoM _LOG_NAME cmdStr
let result = L.intercalate " " . filter (not . null) . map strip . init . lines $ cmdStr
resSeq <- incResSeq mvarCtx
let body = J.EvaluateBody result 0
res = J.defaultEvaluateResponse resSeq req
resStr = J.encode res{J.bodyEvaluateResponse = body}
sendResponse resStr
putStrStdout mvarCtx $ "\n" ++ _PHOITYNE_GHCI_PROMPT
-- |
--
--
continueHandler :: DebugCommandData -> MVar DebugContext -> J.ContinueRequest -> IO ()
continueHandler cmdData mvarCtx req = flip E.catches handlers $ do
resSeq <- incResSeq mvarCtx
let resStr = J.encode $ J.defaultContinueResponse resSeq req
sendResponse resStr
startDebug cmdData mvarCtx
where
handlers = [ E.Handler someExcept ]
someExcept (e :: E.SomeException) = do
let msg = "continueHandler:" ++ show e
errorM _LOG_NAME msg
-- |
--
--
startDebug :: DebugCommandData -> MVar DebugContext -> IO ()
startDebug cmdData mvarCtx = do
ctx <- readMVar mvarCtx
let started = debugStartedDebugContext ctx
startDebugInternal started
where
startDebugInternal True = do
let continue = continueDebugCommandData cmdData
getResult = readDebugCommandData cmdData
cmdStr <- continue True
infoM _LOG_NAME cmdStr
putStrLnStdout mvarCtx cmdStr
cmdStr <- getResult
infoM _LOG_NAME cmdStr
putStrStdout mvarCtx cmdStr
sendEventByDebugStopStatus cmdData mvarCtx cmdStr
startDebugInternal False = do
ctx <- readMVar mvarCtx
withModified $ modifiedDebugContext ctx
withModified True = do
resSeq <- incResSeq mvarCtx
let terminatedEvt = J.defaultTerminatedEvent resSeq
terminatedEvtStr = J.encode terminatedEvt{J.bodyTerminatedEvent = J.TerminatedEventBody True}
sendEvent terminatedEvtStr
withModified False = do
let getResult = readDebugCommandData cmdData
runDebug = runDebugCommandData cmdData
cmdStr <- runDebug True
infoM _LOG_NAME cmdStr
putStrLnStdout mvarCtx cmdStr
cmdStr <- getResult
infoM _LOG_NAME cmdStr
putStrStdout mvarCtx cmdStr
ctx <- takeMVar mvarCtx
putMVar mvarCtx ctx{currentFrameIdDebugContext = 0, debugStartedDebugContext = True}
sendEventByDebugStopStatus cmdData mvarCtx cmdStr
-- |
--
--
sendEventByDebugStopStatus :: DebugCommandData -> MVar DebugContext -> String -> IO ()
sendEventByDebugStopStatus cmdData mvarCtx cmdStr = case getStoppedTextRangeData cmdStr of
Left err -> do
infoM _LOG_NAME $ show err
--putStrLnStdout mvarCtx $ show err
resSeq <- incResSeq mvarCtx
let terminatedEvt = J.defaultTerminatedEvent resSeq
terminatedEvtStr = J.encode terminatedEvt
sendEvent terminatedEvtStr
Right pos -> continueWithHighlightTextRangeData cmdData mvarCtx pos
-- |
--
--
continueWithHighlightTextRangeData :: DebugCommandData -> MVar DebugContext -> HighlightTextRangeData -> IO ()
continueWithHighlightTextRangeData cmdData mvarCtx pos = do
ctx <- readMVar mvarCtx
let bpKey = getKeyOfHighlightTextRangeData pos
bpMap = breakPointDatasDebugContext ctx
case MAP.lookup bpKey bpMap of
Nothing -> do
errorM _LOG_NAME $ "breakpoint not found." ++ show bpKey
sendStopEvent
Just condCmd -> continueWithCondCmd $ conditionBreakPointData condCmd
where
-- |
--
continueWithCondCmd Nothing = do
infoM _LOG_NAME "no condition breakpoint"
sendStopEvent
continueWithCondCmd (Just condStr) = do
let condition = execCommandData cmdData
getResult = readDebugCommandData cmdData
_ <- condition condStr
infoM _LOG_NAME condStr
putStrLnStdout mvarCtx condStr
cmdStr <- getResult
infoM _LOG_NAME cmdStr
putStrStdout mvarCtx cmdStr
condRes <- getConditionResult cmdStr
continueWithCondResult condRes
-- |
--
continueWithCondResult False = startDebug cmdData mvarCtx
continueWithCondResult True = sendStopEvent
-- |
--
getConditionResult res
| L.isPrefixOf "True" res = return True
| L.isPrefixOf "False" res = return False
| otherwise = warningM _LOG_NAME ("invalid condition result. " ++ res) >> return True
-- |
--
sendStopEvent = do
infoM _LOG_NAME $ show pos
ctx <- takeMVar mvarCtx
putMVar mvarCtx ctx{debugStoppedPosDebugContext = Just pos}
resSeq <- incResSeq mvarCtx
let stopEvt = J.defaultStoppedEvent resSeq
stopEvtStr = J.encode stopEvt
sendEvent stopEvtStr
-- |
--
--
stepOverHandler :: DebugCommandData -> MVar DebugContext -> J.NextRequest -> IO ()
stepOverHandler cmdData mvarCtx req = flip E.catches handlers $ do
ctx <- readMVar mvarCtx
case debugStoppedPosDebugContext ctx of
Nothing -> do
resSeq <- incResSeq mvarCtx
let res = J.defaultNextResponse resSeq req
resStr = J.encode res{J.successNextResponse = False, J.messageNextResponse = "debug is initialized but not started yet. press F5(continue)."}
sendResponse resStr
Just _ -> stepOver
where
handlers = [ E.Handler someExcept ]
someExcept (e :: E.SomeException) = do
let msg = "continueHandler:" ++ show e
errorM _LOG_NAME msg
stepOver = do
let step = stepOverDebugCommandData cmdData
getResult = readDebugCommandData cmdData
cmdStr <- step
infoM _LOG_NAME cmdStr
putStrLnStdout mvarCtx cmdStr
cmdStr <- getResult
infoM _LOG_NAME cmdStr
putStrStdout mvarCtx cmdStr
case getStoppedTextRangeData cmdStr of
Left err -> do
infoM _LOG_NAME $ show err
--putStrLnStdout mvarCtx $ show err
resSeq <- incResSeq mvarCtx
let terminatedEvt = J.defaultTerminatedEvent resSeq
terminatedEvtStr = J.encode terminatedEvt
sendEvent terminatedEvtStr
Right pos -> do
ctx <- takeMVar mvarCtx
putMVar mvarCtx ctx{debugStoppedPosDebugContext = Just pos}
resSeq <- incResSeq mvarCtx
let res = J.defaultNextResponse resSeq req
resStr = J.encode res
sendResponse resStr
resSeq <- incResSeq mvarCtx
let stopEvt = J.defaultStoppedEvent resSeq
stopEvtStr = J.encode stopEvt
sendEvent stopEvtStr
-- |
--
--
stepInHandler :: DebugCommandData -> MVar DebugContext -> J.StepInRequest -> IO ()
stepInHandler cmdData mvarCtx req = flip E.catches handlers $ do
ctx <- readMVar mvarCtx
case debugStoppedPosDebugContext ctx of
Nothing -> do
resSeq <- incResSeq mvarCtx
let res = J.defaultStepInResponse resSeq req
resStr = J.encode res{J.successStepInResponse = False, J.messageStepInResponse = "debug is initialized but not started yet. press F5(continue)."}
sendResponse resStr
Just _ -> stepIn
where
handlers = [ E.Handler someExcept ]
someExcept (e :: E.SomeException) = do
let msg = "continueHandler:" ++ show e
errorM _LOG_NAME msg
stepIn = do
let step = stepDebugCommandData cmdData
getResult = readDebugCommandData cmdData
cmdStr <- step
infoM _LOG_NAME cmdStr
putStrLnStdout mvarCtx cmdStr
cmdStr <- getResult
infoM _LOG_NAME cmdStr
putStrStdout mvarCtx cmdStr
case getStoppedTextRangeData cmdStr of
Left err -> do
infoM _LOG_NAME $ show err
--putStrLnStdout mvarCtx $ show err
resSeq <- incResSeq mvarCtx
let terminatedEvt = J.defaultTerminatedEvent resSeq
terminatedEvtStr = J.encode terminatedEvt
sendEvent terminatedEvtStr
Right pos -> do
ctx <- takeMVar mvarCtx
putMVar mvarCtx ctx{debugStoppedPosDebugContext = Just pos}
resSeq <- incResSeq mvarCtx
let res = J.defaultStepInResponse resSeq req
resStr = J.encode res
sendResponse resStr
resSeq <- incResSeq mvarCtx
let stopEvt = J.defaultStoppedEvent resSeq
stopEvtStr = J.encode stopEvt
sendEvent stopEvtStr
-- |=====================================================================
--
-- utility
-- |
--
--
putStrLnConsole :: MVar DebugContext -> String -> IO ()
putStrLnConsole mvarCtx msg = putStrConsole mvarCtx (msg ++ "\n")
-- |
--
--
putStrConsole :: MVar DebugContext -> String -> IO ()
putStrConsole mvarCtx msg = do
resSeq <- incResSeq mvarCtx
let outEvt = J.defaultOutputEvent resSeq
outEvtStr = J.encode outEvt{J.bodyOutputEvent = J.OutputEventBody "console" msg Nothing }
sendEvent outEvtStr
-- |
--
--
putStrLnStdout :: MVar DebugContext -> String -> IO ()
putStrLnStdout mvarCtx msg = putStrStdout mvarCtx (msg ++ "\n")
-- |
--
--
putStrStdout :: MVar DebugContext -> String -> IO ()
putStrStdout mvarCtx msg = do
resSeq <- incResSeq mvarCtx
let outEvt = J.defaultOutputEvent resSeq
outEvtStr = J.encode outEvt{J.bodyOutputEvent = J.OutputEventBody "stdout" msg Nothing }
sendEvent outEvtStr
-- |
--
--
putStrLnStderr :: MVar DebugContext -> String -> IO ()
putStrLnStderr mvarCtx msg = putStrStderr mvarCtx (msg ++ "\n")
-- |
--
--
putStrStderr :: MVar DebugContext -> String -> IO ()
putStrStderr mvarCtx msg = do
resSeq <- incResSeq mvarCtx
let outEvt = J.defaultOutputEvent resSeq
outEvtStr = J.encode outEvt{J.bodyOutputEvent = J.OutputEventBody "stderr" msg Nothing }
sendEvent outEvtStr
-- |
--
--
src2mod :: FilePath -> FilePath -> String
src2mod cwd src
| length cwd >= length src = ""
| otherwise = L.intercalate "."
$ map takeBaseName
$ reverse
$ takeWhile startUpperCase
$ reverse
$ splitOneOf [_SEP_WIN, _SEP_UNIX]
$ drop (length cwd) src
where
startUpperCase modName
| null modName = True
| otherwise = isUpper $ head modName
-- |
--
--
incResSeq :: MVar DebugContext -> IO Int
incResSeq mvarCtx = do
ctx <- takeMVar mvarCtx
let resSec = 1 + resSeqDebugContext ctx
putMVar mvarCtx ctx{resSeqDebugContext = resSec}
return resSec
-- |
--
--
runGHCi :: DebugCommandData -> MVar DebugContext -> FilePath -> IO Bool
runGHCi cmdData mvarCtx cwd = do
let startCmd = startDebugCommandData cmdData
readWhile = readWhileDebugCommandData cmdData
infoM _LOG_NAME "% stack ghci --test --no-load --no-build \n"
putStrLnStdout mvarCtx "% stack ghci --test --no-load --no-build \n"
startCmd cwd
str <- readWhile $ not . endswith _GHCI_PROMPT
infoM _LOG_NAME str
putStrStdout mvarCtx str
withStarted $ endswith _GHCI_PROMPT str
where
withStarted False = return False
withStarted True = do
readAndSetPrompt
return True
readAndSetPrompt = do
let setPrompt = promptDebugCommandData cmdData
getResult = readDebugCommandData cmdData
printEvld = printEvldDebugCommandData cmdData
promptStr <- setPrompt
putStrLnStdout mvarCtx promptStr
infoM _LOG_NAME promptStr
cmdStr <- getResult
putStrStdout mvarCtx cmdStr
infoM _LOG_NAME cmdStr
cmdStr <- printEvld
putStrLnStdout mvarCtx cmdStr
infoM _LOG_NAME cmdStr
cmdStr <- getResult
putStrStdout mvarCtx cmdStr
infoM _LOG_NAME cmdStr
-- |
--
--
loadHsFile :: DebugCommandData -> MVar DebugContext -> FilePath -> IO Bool
loadHsFile cmdData mvarCtx path
| (False == endswith _HS_FILE_EXT path) = return False
| otherwise = do
let loadFile = loadFileDebugCommandData cmdData
readLines = readLinesDebugCommandData cmdData
getResult = readDebugCommandData cmdData
cmdStr <- loadFile path
putStrLnStdout mvarCtx cmdStr
infoM _LOG_NAME cmdStr
cont <- readLines debugStartResultHandler
if | null cont -> return False
| startswith "Ok," (last cont) -> do
cmdStr <- getResult
putStrStdout mvarCtx cmdStr
infoM _LOG_NAME cmdStr
return True
| startswith "Failed," (last cont) -> do
cmdStr <- getResult
putStrStdout mvarCtx cmdStr
infoM _LOG_NAME cmdStr
return False
| otherwise -> do
errorM _LOG_NAME $ "load file fail.["++ path ++"]"
putStrLnStderr mvarCtx $ "load file fail.["++ path ++"]"
return False
where
debugStartResultHandler :: [String] -> IO Bool
debugStartResultHandler acc = putStrLnStdout mvarCtx curStr >> infoM _LOG_NAME curStr >> if
| L.isPrefixOf "Ok," curStr -> return False
| L.isPrefixOf "Failed," curStr -> return False
| otherwise -> return True
where
curStr | null acc = ""
| otherwise = last acc
-- |
-- ブレークポイントをGHCi上でdeleteする
--
deleteBreakPointOnCUI :: DebugCommandData -> MVar DebugContext -> BreakPointData -> IO ()
deleteBreakPointOnCUI cmdData _ (BreakPointData _ _ _ (Just breakNo) _) = do
let deleteBreak = deleteBreakDebugCommandData cmdData
getResult = readDebugCommandData cmdData
cmdStr <- deleteBreak breakNo
-- putStrLnStdout mvarCtx cmdStr
infoM _LOG_NAME cmdStr
cmdStr <- getResult
-- putStrStdout mvarCtx cmdStr
infoM _LOG_NAME cmdStr
deleteBreakPointOnCUI _ mvarCtx bp = do
let err = "invalid delete break point." ++ show bp
putStrLnStderr mvarCtx err
errorM _LOG_NAME err
-- |
-- GHCi上でブレークポイントを追加する
--
addBreakPointOnCUI :: DebugCommandData -> MVar DebugContext -> BreakPointData -> IO (Either String Int)
addBreakPointOnCUI cmdData _ (BreakPointData modName _ lineNo _ _) = do
let setBreak = breakDebugCommandData cmdData
getResult = readDebugCommandData cmdData
cmdStr <- setBreak modName lineNo
-- putStrLnStdout mvarCtx cmdStr
infoM _LOG_NAME cmdStr
cmdStr <- getResult
-- putStrStdout mvarCtx cmdStr
infoM _LOG_NAME cmdStr
case getBreakPointNo cmdStr of
Right no -> return $ Right no
Left err -> if L.isPrefixOf _NO_BREAK_POINT_LOCATION cmdStr
then return $ Left _NO_BREAK_POINT_LOCATION
else do
let msg = "unexpected break set result. " ++ show err ++ cmdStr
errorM _LOG_NAME msg
return $ Left msg
where
-- |
-- 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
-- |=====================================================================
--
-- パーサ
--
-- |
--
--
getStoppedTextRangeData :: String -> Either ParseError HighlightTextRangeData
getStoppedTextRangeData = parse parser "getStoppedTextRangeData"
where
parser = do
_ <- manyTill anyChar (try (string "Stopped at "))
parseHighlightTextRange
-- |
--
drive2lower :: FilePath -> FilePath
drive2lower (x : ':' : xs) = toLower x : ':' : xs
drive2lower xs = xs
-- |
-- 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
-- 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 (drive2lower 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 <- try (manyTill digit (char ':')) <|> try (manyTill digit endOfLine) <|> try (manyTill digit eof)
return ((read ln), (read sn), (read ln), (read sn))
-- |
-- トレース情報のパーサ
--
-- 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)
--
-- -1 : main (D:\haskell\vsc-sample\app\Main.hs:6:8-15)
--
getTraceDataList :: String -> Either ParseError [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 $ TraceData (strip traceId) funcName (init (strip filePath))
-- |
-- バインディング値のパーサ
--
-- parser of
-- args :: Project.Argument.ArgData = _
-- _result :: IO Data.ConfigFile.Types.ConfigParser = _
--
getBindingDataList :: String -> Either ParseError [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 $ BindingData (strip varName) (strip modName) valStr
lineSep = try $ endOfLine >> notFollowedBy space