Skip to content

Commit 1db00e6

Browse files
ndmitchellaherrmann-da
authored andcommitted
haskell/ghcide#279, support preprocessors (haskell/ghcide#282)
* Support preprocessors * Add a preprocessor for testing * Add a preprocessor test
1 parent bc5c540 commit 1db00e6

File tree

4 files changed

+63
-4
lines changed

4 files changed

+63
-4
lines changed

ghcide/ghcide.cabal

+10-1
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,14 @@ library
131131
Development.IDE.Spans.Type
132132
ghc-options: -Wall -Wno-name-shadowing
133133

134+
executable ghcide-test-preprocessor
135+
default-language: Haskell2010
136+
hs-source-dirs: test/preprocessor
137+
ghc-options: -Wall
138+
main-is: Main.hs
139+
build-depends:
140+
base == 4.*
141+
134142
executable ghcide
135143
if flag(ghc-lib)
136144
buildable: False
@@ -169,7 +177,8 @@ test-suite ghcide-tests
169177
type: exitcode-stdio-1.0
170178
default-language: Haskell2010
171179
build-tool-depends:
172-
ghcide:ghcide
180+
ghcide:ghcide,
181+
ghcide:ghcide-test-preprocessor
173182
build-depends:
174183
base,
175184
bytestring,

ghcide/src/Development/IDE/Core/Preprocessor.hs

+27-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import DynFlags
2020
import qualified HeaderInfo as Hdr
2121
import Development.IDE.Types.Diagnostics
2222
import Development.IDE.GHC.Error
23-
import SysTools (Option (..), runUnlit)
23+
import SysTools (Option (..), runUnlit, runPp)
2424
import Control.Monad.Trans.Except
2525
import qualified GHC.LanguageExtensions as LangExt
2626
import Data.Maybe
@@ -43,10 +43,19 @@ preprocessor filename mbContents = do
4343

4444
-- Perform cpp
4545
dflags <- ExceptT $ parsePragmasIntoDynFlags filename contents
46-
if not $ xopt LangExt.Cpp dflags then
46+
(isOnDisk, contents, dflags) <-
47+
if not $ xopt LangExt.Cpp dflags then
48+
return (isOnDisk, contents, dflags)
49+
else do
50+
contents <- liftIO $ runCpp dflags filename $ if isOnDisk then Nothing else Just contents
51+
dflags <- ExceptT $ parsePragmasIntoDynFlags filename contents
52+
return (False, contents, dflags)
53+
54+
-- Perform preprocessor
55+
if not $ gopt Opt_Pp dflags then
4756
return (contents, dflags)
4857
else do
49-
contents <- liftIO $ runCpp dflags filename $ if isOnDisk then Nothing else Just contents
58+
contents <- liftIO $ runPreprocessor dflags filename $ if isOnDisk then Nothing else Just contents
5059
dflags <- ExceptT $ parsePragmasIntoDynFlags filename contents
5160
return (contents, dflags)
5261

@@ -132,3 +141,18 @@ runCpp dflags filename contents = withTempDir $ \dir -> do
132141
= "# " <> num <> " \"" <> map (\x -> if isPathSeparator x then '/' else x) filename <> "\""
133142
| otherwise = x
134143
stringToStringBuffer . unlines . map tweak . lines <$> readFileUTF8' out
144+
145+
146+
-- | Run a preprocessor on a file
147+
runPreprocessor :: DynFlags -> FilePath -> Maybe SB.StringBuffer -> IO SB.StringBuffer
148+
runPreprocessor dflags filename contents = withTempDir $ \dir -> do
149+
let out = dir </> takeFileName filename <.> "out"
150+
inp <- case contents of
151+
Nothing -> return filename
152+
Just contents -> do
153+
let inp = dir </> takeFileName filename <.> "hs"
154+
withBinaryFile inp WriteMode $ \h ->
155+
hPutStringBuffer h contents
156+
return inp
157+
runPp dflags [SysTools.Option filename, SysTools.Option inp, SysTools.FileOption "" out]
158+
SB.hGetStringBuffer out

ghcide/test/exe/Main.hs

+16
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ main = defaultMain $ testGroup "HIE"
4242
, codeLensesTests
4343
, findDefinitionAndHoverTests
4444
, pluginTests
45+
, preprocessorTests
4546
, thTests
4647
]
4748

@@ -914,6 +915,21 @@ pluginTests = testSessionWait "plugins" $ do
914915
)
915916
]
916917

918+
preprocessorTests :: TestTree
919+
preprocessorTests = testSessionWait "preprocessor" $ do
920+
let content =
921+
T.unlines
922+
[ "{-# OPTIONS_GHC -F -pgmF=ghcide-test-preprocessor #-}"
923+
, "module Testing where"
924+
, "y = x + z" -- plugin replaces x with y, making this have only one diagnostic
925+
]
926+
_ <- openDoc' "Testing.hs" "haskell" content
927+
expectDiagnostics
928+
[ ( "Testing.hs",
929+
[(DsError, (2, 8), "Variable not in scope: z")]
930+
)
931+
]
932+
917933
thTests :: TestTree
918934
thTests =
919935
testGroup

ghcide/test/preprocessor/Main.hs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
module Main(main) where
3+
4+
import System.Environment
5+
6+
main :: IO ()
7+
main = do
8+
_:input:output:_ <- getArgs
9+
let f = map (\x -> if x == 'x' then 'y' else x)
10+
writeFile output . f =<< readFile input

0 commit comments

Comments
 (0)