2024-03-04 16:32:22 +00:00
{- # LANGUAGE CPP # -}
{- # LANGUAGE DataKinds # -}
{- # LANGUAGE FlexibleContexts # -}
{- # LANGUAGE OverloadedStrings # -}
{- # LANGUAGE TypeApplications # -}
{- # LANGUAGE RankNTypes # -}
{- # LANGUAGE ViewPatterns # -}
{- # OPTIONS_GHC - Wno - unused - record - wildcards # -}
{- # OPTIONS_GHC - Wno - unused - matches # -}
{- # LANGUAGE FlexibleInstances # -}
{- # LANGUAGE MultiParamTypeClasses # -}
{- # LANGUAGE TemplateHaskell # -}
{- # LANGUAGE BangPatterns # -}
{- # LANGUAGE InstanceSigs # -}
{- # OPTIONS_GHC - Wno - incomplete - patterns # -}
2024-03-13 16:42:30 +00:00
module GHCup.Brick.Widgets.Menus.CompileHLS (
CompileHLSOptions ,
CompileHLSMenu ,
create ,
handler ,
draw ,
jobs ,
setCompile ,
updateCabal ,
overwriteVer ,
isolateDir ,
cabalProject ,
cabalProjectLocal ,
patches ,
targetGHCs ,
cabalArgs ,
)
where
2024-03-04 16:32:22 +00:00
import GHCup.Brick.Widgets.Menu ( Menu )
import qualified GHCup.Brick.Widgets.Menu as Menu
import GHCup.Brick.Common ( Name ( .. ) )
import Brick
( BrickEvent ( .. ) ,
EventM ,
Widget ( .. ) )
import Prelude hiding ( appendFile )
import Optics.TH ( makeLenses )
import qualified GHCup.Brick.Common as Common
import GHCup.Types ( KeyCombination , VersionPattern , ToolVersion )
import URI.ByteString ( URI )
import qualified Data.Text as T
import qualified Data.ByteString.UTF8 as UTF8
import GHCup.Utils ( parseURI )
import Data.Bifunctor ( Bifunctor ( .. ) )
import Data.Function ( ( & ) )
import Optics ( ( .~ ) )
import Data.Char ( isSpace )
import System.FilePath ( isValid , isAbsolute , normalise )
import Control.Applicative ( Alternative ( ( <|> ) ) )
import Text.Read ( readEither )
import GHCup.Prelude ( stripNewlineEnd )
import qualified GHCup.OptParse.Common as OptParse
data CompileHLSOptions = CompileHLSOptions
{ _jobs :: Maybe Int
, _setCompile :: Bool
, _updateCabal :: Bool
, _overwriteVer :: Maybe [ VersionPattern ]
, _isolateDir :: Maybe FilePath
, _cabalProject :: Maybe ( Either FilePath URI )
, _cabalProjectLocal :: Maybe URI
, _patches :: Maybe ( Either FilePath [ URI ] )
, _targetGHCs :: [ ToolVersion ]
, _cabalArgs :: [ T . Text ]
} deriving ( Eq , Show )
makeLenses ''CompileHLSOptions
type CompileHLSMenu = Menu CompileHLSOptions Name
create :: KeyCombination -> CompileHLSMenu
create k = Menu . createMenu CompileGHCBox initialState k buttons fields
where
initialState =
CompileHLSOptions
Nothing
False
False
Nothing
Nothing
Nothing
Nothing
Nothing
[]
[]
-- Brick's internal editor representation is [mempty].
emptyEditor i = T . null i || ( i == " \ n " )
whenEmpty :: a -> ( T . Text -> Either Menu . ErrorMessage a ) -> T . Text -> Either Menu . ErrorMessage a
whenEmpty emptyval f i = if not ( emptyEditor i ) then f i else Right emptyval
cabalProjectV :: T . Text -> Either Menu . ErrorMessage ( Maybe ( Either FilePath URI ) )
cabalProjectV i =
case not $ emptyEditor i of
True ->
let readPath = Right . Left . stripNewlineEnd . T . unpack $ i
in bimap T . pack Just $ second Right ( readUri i ) <|> readPath
False -> Right Nothing
{- There is an unwanted dependency to ghcup - opt... Alternatives are
- copy - paste a bunch of code
- define a new common library
- }
ghcVersionTagEither :: T . Text -> Either Menu . ErrorMessage [ ToolVersion ]
ghcVersionTagEither = first T . pack . traverse ( OptParse . ghcVersionTagEither . T . unpack ) . T . split isSpace
overWriteVersionParser :: T . Text -> Either Menu . ErrorMessage ( Maybe [ VersionPattern ] )
overWriteVersionParser = bimap T . pack Just . OptParse . overWriteVersionParser . T . unpack
jobsV :: T . Text -> Either Menu . ErrorMessage ( Maybe Int )
jobsV =
let parseInt = bimap ( const " Invalid value. Must be an integer " ) Just . readEither @ Int . T . unpack
in whenEmpty Nothing parseInt
readUri :: T . Text -> Either String URI
readUri = first show . parseURI . UTF8 . fromString . T . unpack
patchesV :: T . Text -> Either Menu . ErrorMessage ( Maybe ( Either FilePath [ URI ] ) )
patchesV = whenEmpty Nothing readPatches
where
readPatches j =
let
x = ( bimap T . unpack ( fmap Left ) $ filepathV j )
y = second ( Just . Right ) $ traverse readUri ( T . split isSpace j )
in first T . pack $ x <|> y
filepathV :: T . Text -> Either Menu . ErrorMessage ( Maybe FilePath )
filepathV i =
case not $ emptyEditor i of
True -> absolutePathParser ( T . unpack i )
False -> Right Nothing
absolutePathParser :: FilePath -> Either Menu . ErrorMessage ( Maybe FilePath )
absolutePathParser f = case isValid f && isAbsolute f of
True -> Right . Just . stripNewlineEnd . normalise $ f
False -> Left " Please enter a valid absolute filepath. "
additionalValidator :: T . Text -> Either Menu . ErrorMessage [ T . Text ]
additionalValidator = Right . T . split isSpace
fields =
[ Menu . createEditableField ( Common . MenuElement Common . CabalProjectEditBox ) cabalProjectV cabalProject
& Menu . fieldLabelL .~ " cabal project "
& Menu . fieldHelpMsgL .~ " If relative filepath, specifies the path to cabal.project inside the unpacked HLS tarball/checkout. Otherwise expects a full URI with https/http/file scheme. "
, Menu . createEditableField ( Common . MenuElement Common . CabalProjectLocalEditBox ) ( bimap T . pack Just . readUri ) cabalProjectLocal
& Menu . fieldLabelL .~ " cabal project local "
& Menu . fieldHelpMsgL .~ " URI (https/http/file) to a cabal.project.local to be used for the build. Will be copied over. "
, Menu . createCheckBoxField ( Common . MenuElement Common . UpdateCabalCheckBox ) updateCabal
& Menu . fieldLabelL .~ " cabal update "
& Menu . fieldHelpMsgL .~ " Run 'cabal update' before the build "
, Menu . createEditableField ( Common . MenuElement Common . JobsEditBox ) jobsV jobs
& Menu . fieldLabelL .~ " jobs "
& Menu . fieldHelpMsgL .~ " How many jobs to use for make "
, Menu . createEditableField ( Common . MenuElement Common . TargetGhcEditBox ) ghcVersionTagEither targetGHCs
& Menu . fieldLabelL .~ " target GHC "
& Menu . fieldHelpMsgL .~ " For which GHC version to compile for (can be specified multiple times) "
, Menu . createEditableField ( Common . MenuElement Common . PatchesEditBox ) patchesV patches
& Menu . fieldLabelL .~ " patches "
& Menu . fieldHelpMsgL .~ " Either a URI to a patch (https/http/file) or Absolute path to patch directory "
, Menu . createCheckBoxField ( Common . MenuElement Common . SetCheckBox ) setCompile
& Menu . fieldLabelL .~ " set "
& Menu . fieldHelpMsgL .~ " Set as active version after install "
, Menu . createEditableField ( Common . MenuElement Common . AdditionalEditBox ) additionalValidator cabalArgs
& Menu . fieldLabelL .~ " CONFIGURE_ARGS "
& Menu . fieldHelpMsgL .~ " Additional arguments to cabal install, prefix with '-- ' (longopts) "
, Menu . createEditableField ( Common . MenuElement Common . IsolateEditBox ) filepathV isolateDir
& Menu . fieldLabelL .~ " isolated "
& Menu . fieldHelpMsgL .~ " install in an isolated absolute directory instead of the default one "
, Menu . createEditableField ( Common . MenuElement Common . OvewrwiteVerEditBox ) overWriteVersionParser overwriteVer
& Menu . fieldLabelL .~ " overwrite version "
& Menu . fieldHelpMsgL .~ " Allows to overwrite the finally installed VERSION with a different one "
]
buttons = [
Menu . createButtonField ( Common . MenuElement Common . OkButton )
& Menu . fieldLabelL .~ " Compile "
& Menu . fieldHelpMsgL .~ " Compile HLS from source with options below "
]
handler :: BrickEvent Name e -> EventM Name CompileHLSMenu ()
handler = Menu . handlerMenu
draw :: CompileHLSMenu -> Widget Name
draw = Common . frontwardLayer " Compile HLS " . Menu . drawMenu