@@ -58,6 +58,7 @@ import Development.IDE.Spans.Type
58import System.FilePath
58import System.FilePath
59import System.Directory
59import System.Directory
60import System.IO.Extra
60import System.IO.Extra
63-- | Contains the typechecked module and the OrigNameCache entry for
64-- | Contains the typechecked module and the OrigNameCache entry for
@@ -91,7 +92,7 @@ parseModule
91 :: IdeOptions
92 :: IdeOptions
93 -> FilePath
94 -> FilePath
94− -> SB.StringBuffer
95+ -> Maybe SB.StringBuffer
95 -> IO ([FileDiagnostic], Maybe ParsedModule)
96 -> IO ([FileDiagnostic], Maybe ParsedModule)
96parseModule IdeOptions{..} env file =
97parseModule IdeOptions{..} env file =
97 fmap (either (, Nothing) (second Just)) .
98 fmap (either (, Nothing) (second Just)) .
@@ -310,15 +311,55 @@ getModSummaryFromBuffer fp contents dflags parsed = do
310 , ms_parsed_mod = Nothing
311 , ms_parsed_mod = Nothing
314+-- | Run CPP on a file
315+runCpp :: DynFlags -> FilePath -> Maybe SB.StringBuffer -> IO SB.StringBuffer
316+runCpp dflags filename contents = withTempDir $ \dir -> do
317+ let out = dir </> takeFileName filename <.> "out"
320+ -- Happy case, file is not modified, so run CPP on it in-place
321+ -- which also makes things like relative #include files work
322+ -- and means location information is correct
323+ doCpp dflags True filename out
324+ liftIO $ SB.hGetStringBuffer out
327+ -- Sad path, we have to create a version of the path in a temp dir
328+ -- __FILE__ macro is wrong, ignoring that for now (likely not a real issue)
330+ -- Relative includes aren't going to work, so we fix that by adding to the include path.
331+ let addSelf (IncludeSpecs quote global) = IncludeSpecs (takeDirectory filename : quote) global
332+ dflags <- return dflags{includePaths = addSelf $ includePaths dflags}
334+ -- Location information is wrong, so we fix that by patching it afterwards.
335+ let inp = dir </> "___HIE_CORE_MAGIC___"
336+ withBinaryFile inp WriteMode $ \h ->
337+ hPutStringBuffer h contents
338+ doCpp dflags True inp out
340+ -- Fix up the filename in lines like:
341+ -- # 1 "C:/Temp/extra-dir-914611385186/___HIE_CORE_MAGIC___"
343+ | Just x <- stripPrefix "# " x
344+ , "___HIE_CORE_MAGIC___" `isInfixOf` x
345+ , let num = takeWhile (not . isSpace) x
346+ -- important to use /, and never \ for paths, even on Windows, since then C escapes them
347+ -- and GHC gets all confused
348+ = "# " <> num <> " \"" <> map (\x -> if isPathSeparator x then '/' else x) filename <> "\""
350+ stringToStringBuffer . unlines . map tweak . lines <$> readFileUTF8' out
313-- | Given a buffer, flags, file path and module summary, produce a
353-- | Given a buffer, flags, file path and module summary, produce a
314-- parsed module (or errors) and any parse warnings.
354-- parsed module (or errors) and any parse warnings.
315parseFileContents
355parseFileContents
316 :: GhcMonad m
356 :: GhcMonad m
317 => (GHC.ParsedSource -> ([(GHC.SrcSpan, String)], GHC.ParsedSource))
357 => (GHC.ParsedSource -> ([(GHC.SrcSpan, String)], GHC.ParsedSource))
318 -> FilePath -- ^ the filename (for source locations)
358 -> FilePath -- ^ the filename (for source locations)
319− -> SB.StringBuffer -- ^ Haskell module source text (full Unicode is supported)
359+ -> Maybe SB.StringBuffer -- ^ Haskell module source text (full Unicode is supported)
320 -> ExceptT [FileDiagnostic] m ([FileDiagnostic], ParsedModule)
360 -> ExceptT [FileDiagnostic] m ([FileDiagnostic], ParsedModule)
321−parseFileContents preprocessor filename contents = do
361+parseFileContents preprocessor filename mbContents = do
362+ contents <- liftIO $ maybe (hGetStringBuffer filename) return mbContents
322 let loc = mkRealSrcLoc (mkFastString filename) 1 1
363 let loc = mkRealSrcLoc (mkFastString filename) 1 1
323 dflags <- ExceptT $ parsePragmasIntoDynFlags filename contents
364 dflags <- ExceptT $ parsePragmasIntoDynFlags filename contents
@@ -326,13 +367,7 @@ parseFileContents preprocessor filename contents = do
326 if not $ xopt LangExt.Cpp dflags then
367 if not $ xopt LangExt.Cpp dflags then
327 return (contents, dflags)
368 return (contents, dflags)
329− contents <- liftIO $ withTempDir $ \dir -> do
370+ contents <- liftIO $ runCpp dflags filename mbContents
330− let inp = dir </> takeFileName filename
331− let out = dir </> takeFileName filename <.> "out"
332− let f x = if SB.atEnd x then Nothing else Just $ SB.nextChar x
333− liftIO $ writeFileUTF8 inp (unfoldr f contents)
334− doCpp dflags True inp out
335− liftIO $ SB.hGetStringBuffer out
336 dflags <- ExceptT $ parsePragmasIntoDynFlags filename contents
371 dflags <- ExceptT $ parsePragmasIntoDynFlags filename contents
337 return (contents, dflags)
372 return (contents, dflags)