packages feed

ihp-ide-1.4.0: IHP/IDE/ToolServer/Helper/Controller.hs

{-|
Module: IHP.IDE.ToolServer.Helper.Controller
Description: Provides helpers for controllers of the ToolServer
Copyright: (c) digitally induced GmbH, 2020
-}
module IHP.IDE.ToolServer.Helper.Controller
( theAppPort
, openEditor
, findWebControllers
, findControllers
, findApplications
, theToolServerApplication
, clearDatabaseNeedsMigration
, markDatabaseNeedsMigration
) where

import IHP.Prelude
import IHP.ControllerSupport
import IHP.IDE.ToolServer.Types
import qualified IHP.IDE.PortConfig as PortConfig
import IHP.IDE.Types
import qualified Network.Socket as Socket
import qualified System.Process as Process
import System.Info (os)
import qualified IHP.EnvVar as EnvVar
import IHP.Controller.Context
import System.IO.Unsafe (unsafePerformIO)

import qualified Data.Text as Text
import System.Directory
import qualified Data.Text.IO as IO

-- | Returns the port used by the running app. Usually returns @8000@.
theAppPort :: (?context :: ControllerContext) => IO Socket.PortNumber
theAppPort = do
    toolServerApplication <- fromContext @ToolServerApplication
    pure toolServerApplication.appPort

openEditor :: Text -> Int -> Int -> IO ()
openEditor path line col = do
    (supportsLineAndCol, editor) <- findEditor
    let command =
            editor <> " " <> path <> if supportsLineAndCol then ":" <> tshow line <> ":" <> tshow col else ""
    _ <- Process.system (cs command)
    unless supportsLineAndCol (putStrLn "Pro Tip: Set the env var IHP_EDITOR to your favorite editor. Then all your files will be opened at the right line and column where the error is reported.")
    pure ()

-- | Returns the editor command for the user and also whether the command supports line and col notation
--
-- Line and col notation means that calling @editor myfile.hs:10:5@ works. Tools like @xdg-open@ or on macOS @open@
-- don't support this notation and thus need to be called like @xdg-open myfile.hs@ instead of @xdg-open myfile.hs:10:5@
--
-- Looks for a the env vars IHP_EDITOR or EDITOR. As fallback it uses @open@ or @xdg-open@ (depends on OS).
--
findEditor :: IO (Bool, Text)
findEditor = do
    ihpEditorEnv <- EnvVar.envOrNothing "IHP_EDITOR"
    editorEnv <- EnvVar.envOrNothing "EDITOR"
    pure case catMaybes [ihpEditorEnv, editorEnv] of
        (editor:_) -> (True, editor)
        [] -> case os of
            "linux" -> (False, "xdg-open")
            "darwin" -> (False, "open")


findWebControllers :: IO [Text]
findWebControllers = do
    directoryFiles <-  listDirectory "Web/Controller"
    let controllerFiles :: [Text] =  filter (\x -> not $ "Prelude" `isInfixOf` x || "Context" `isInfixOf` x)  $ map cs directoryFiles
    pure $ map (Text.replace ".hs" "") controllerFiles

findControllers :: Text -> IO [Text]
findControllers application = do
    directoryFiles <-  listDirectory $ cs $ application <> "/Controller"
    let controllerFiles :: [Text] =  filter (\x -> not $ "Prelude" `isInfixOf` x || "Context" `isInfixOf` x)  $ map cs directoryFiles
    pure $ map (Text.replace ".hs" "") controllerFiles

findApplications :: IO ([Text])
findApplications = do
    mainhs <- IO.readFile "Main.hs"
    let imports = filter (\line -> "import " `isPrefixOf` line && ".FrontController" `isSuffixOf` line) (lines mainhs)
    pure (map removeImport imports)
        where
            removeImport line = Text.replace ".FrontController" "" (Text.replace "import " "" line)

theToolServerApplication :: (?context :: ControllerContext) => IO ToolServerApplication
theToolServerApplication = fromContext @ToolServerApplication

clearDatabaseNeedsMigration :: (?context :: ControllerContext) => IO ()
clearDatabaseNeedsMigration = do
    toolServerApp <- theToolServerApplication
    writeIORef toolServerApp.databaseNeedsMigration False

markDatabaseNeedsMigration :: (?context :: ControllerContext) => IO ()
markDatabaseNeedsMigration = do
    toolServerApp <- theToolServerApplication
    writeIORef toolServerApp.databaseNeedsMigration True