1
+ {-# LANGUAGE ScopedTypeVariables #-}
1
2
{-# LANGUAGE CPP #-}
2
3
{-# LANGUAGE DeriveAnyClass #-}
3
4
{-# LANGUAGE DeriveGeneric #-}
@@ -33,18 +34,25 @@ import qualified Data.Text as T
33
34
import qualified Data.Text.IO as T
34
35
import Data.Typeable
35
36
import Development.IDE
36
- import Development.IDE.Core.Rules (defineNoFile )
37
+ import Development.IDE.Core.Rules (getParsedModuleWithComments , defineNoFile )
37
38
import Development.IDE.Core.Shake (getDiagnostics )
38
39
39
- #ifdef GHC_LIB
40
+ #ifdef HLINT_ON_GHC_LIB
40
41
import Data.List (nub )
41
- import "ghc-lib" GHC hiding (DynFlags (.. ))
42
+ import "ghc-lib" GHC hiding (DynFlags (.. ), ms_hspp_opts )
43
+ import "ghc-lib-parser" GHC.LanguageExtensions (Extension )
42
44
import "ghc" GHC as RealGHC (DynFlags (.. ))
43
- import "ghc" HscTypes as RealGHC.HscTypes (hsc_dflags )
45
+ import "ghc" HscTypes as RealGHC.HscTypes (hsc_dflags , ms_hspp_opts )
44
46
import qualified "ghc" EnumSet as EnumSet
45
47
import Language.Haskell.GhclibParserEx.GHC.Driver.Session as GhclibParserEx (readExtension )
48
+ import System.FilePath (takeFileName )
49
+ import System.IO (hPutStr , noNewlineTranslation , hSetNewlineMode , utf8 , hSetEncoding , IOMode (WriteMode ), withFile , hClose )
50
+ import System.IO.Temp
46
51
#else
47
52
import Development.IDE.GHC.Compat hiding (DynFlags (.. ))
53
+ import Language.Haskell.GHC.ExactPrint.Parsers (postParseTransform )
54
+ import Language.Haskell.GHC.ExactPrint.Delta (deltaOptions )
55
+ import Language.Haskell.GHC.ExactPrint.Types (Rigidity (.. ))
48
56
#endif
49
57
50
58
import Ide.Logger
@@ -53,12 +61,12 @@ import Ide.Plugin.Config
53
61
import Ide.PluginUtils
54
62
import Language.Haskell.HLint as Hlint
55
63
import Language.Haskell.LSP.Core
64
+ ( LspFuncs (withIndefiniteProgress ),
65
+ ProgressCancellable (Cancellable ) )
56
66
import Language.Haskell.LSP.Types
57
67
import qualified Language.Haskell.LSP.Types as LSP
58
68
import qualified Language.Haskell.LSP.Types.Lens as LSP
59
- import System.FilePath (takeFileName )
60
- import System.IO (hPutStr , noNewlineTranslation , hSetNewlineMode , utf8 , hSetEncoding , IOMode (WriteMode ), withFile , hClose )
61
- import System.IO.Temp
69
+
62
70
import Text.Regex.TDFA.Text ()
63
71
import GHC.Generics (Generic )
64
72
@@ -176,7 +184,14 @@ getIdeas nfp = do
176
184
fmap applyHints' (moduleEx flags)
177
185
178
186
where moduleEx :: ParseFlags -> Action (Maybe (Either ParseError ModuleEx ))
179
- #ifdef GHC_LIB
187
+ #ifndef HLINT_ON_GHC_LIB
188
+ moduleEx _flags = do
189
+ mbpm <- getParsedModule nfp
190
+ return $ createModule <$> mbpm
191
+ where createModule pm = Right (createModuleEx anns modu)
192
+ where anns = pm_annotations pm
193
+ modu = pm_parsed_source pm
194
+ #else
180
195
moduleEx flags = do
181
196
mbpm <- getParsedModule nfp
182
197
-- If ghc was not able to parse the module, we disable hlint diagnostics
@@ -190,20 +205,21 @@ getIdeas nfp = do
190
205
Just <$> (liftIO $ parseModuleEx flags' fp contents')
191
206
192
207
setExtensions flags = do
193
- hsc <- hscEnv <$> use_ GhcSession nfp
194
- let dflags = hsc_dflags hsc
195
- let hscExts = EnumSet. toList (extensionFlags dflags)
196
- let hscExts' = mapMaybe (GhclibParserEx. readExtension . show ) hscExts
197
- let hlintExts = nub $ enabledExtensions flags ++ hscExts'
208
+ hlintExts <- getExtensions flags nfp
198
209
logm $ " hlint:getIdeas:setExtensions:" ++ show hlintExts
199
210
return $ flags { enabledExtensions = hlintExts }
200
- #else
201
- moduleEx _flags = do
202
- mbpm <- getParsedModule nfp
203
- return $ createModule <$> mbpm
204
- where createModule pm = Right (createModuleEx anns modu)
205
- where anns = pm_annotations pm
206
- modu = pm_parsed_source pm
211
+
212
+ getExtensions :: ParseFlags -> NormalizedFilePath -> Action [Extension ]
213
+ getExtensions pflags nfp = do
214
+ dflags <- getFlags
215
+ let hscExts = EnumSet. toList (extensionFlags dflags)
216
+ let hscExts' = mapMaybe (GhclibParserEx. readExtension . show ) hscExts
217
+ let hlintExts = nub $ enabledExtensions pflags ++ hscExts'
218
+ return hlintExts
219
+ where getFlags :: Action DynFlags
220
+ getFlags = do
221
+ (modsum, _) <- use_ GetModSummary nfp
222
+ return $ ms_hspp_opts modsum
207
223
#endif
208
224
209
225
-- ---------------------------------------------------------------------
@@ -334,10 +350,18 @@ applyOneCmd lf ide (AOP uri pos title) = do
334
350
applyHint :: IdeState -> NormalizedFilePath -> Maybe OneHint -> IO (Either String WorkspaceEdit )
335
351
applyHint ide nfp mhint =
336
352
runExceptT $ do
337
- ideas <- bimapExceptT showParseError id $ ExceptT $ liftIO $ runAction " applyHint" ide $ getIdeas nfp
353
+ let runAction' :: Action a -> IO a
354
+ runAction' = runAction " applyHint" ide
355
+ let errorHandlers = [ Handler $ \ e -> return (Left (show (e :: IOException )))
356
+ , Handler $ \ e -> return (Left (show (e :: ErrorCall )))
357
+ ]
358
+ ideas <- bimapExceptT showParseError id $ ExceptT $ runAction' $ getIdeas nfp
338
359
let ideas' = maybe ideas (`filterIdeas` ideas) mhint
339
- let commands = map ( show &&& ideaRefactoring) ideas'
360
+ let commands = map ideaRefactoring ideas'
340
361
liftIO $ logm $ " applyHint:apply=" ++ show commands
362
+ let fp = fromNormalizedFilePath nfp
363
+ (_, mbOldContent) <- liftIO $ runAction' $ getFileContents nfp
364
+ oldContent <- maybe (liftIO $ T. readFile fp) return mbOldContent
341
365
-- set Nothing as "position" for "applyRefactorings" because
342
366
-- applyRefactorings expects the provided position to be _within_ the scope
343
367
-- of each refactoring it will apply.
@@ -353,27 +377,48 @@ applyHint ide nfp mhint =
353
377
-- If we provide "applyRefactorings" with "Just (1,13)" then
354
378
-- the "Redundant bracket" hint will never be executed
355
379
-- because SrcSpan (1,20,??,??) doesn't contain position (1,13).
356
- let fp = fromNormalizedFilePath nfp
357
- (_, mbOldContent) <- liftIO $ runAction " hlint" ide $ getFileContents nfp
358
- oldContent <- maybe (liftIO $ T. readFile fp) return mbOldContent
359
- -- We need to save a file with last edited contents cause `apply-refact`
360
- -- doesn't expose a function taking directly contents instead a file path.
361
- -- Ideally we should try to expose that function upstream and remove this.
362
- res <- liftIO $ withSystemTempFile (takeFileName fp) $ \ temp h -> do
380
+ #ifdef HLINT_ON_GHC_LIB
381
+ let writeFileUTF8NoNewLineTranslation file txt =
382
+ withFile file WriteMode $ \ h -> do
383
+ hSetEncoding h utf8
384
+ hSetNewlineMode h noNewlineTranslation
385
+ hPutStr h (T. unpack txt)
386
+ res <-
387
+ liftIO $ withSystemTempFile (takeFileName fp) $ \ temp h -> do
363
388
hClose h
364
389
writeFileUTF8NoNewLineTranslation temp oldContent
365
- (Right <$> applyRefactorings Nothing commands temp) `catches`
366
- [ Handler $ \ e -> return (Left (show (e :: IOException )))
367
- , Handler $ \ e -> return (Left (show (e :: ErrorCall )))
368
- ]
390
+ (pflags, _, _) <- runAction' $ useNoFile_ GetHlintSettings
391
+ exts <- runAction' $ getExtensions pflags nfp
392
+ -- We have to reparse extensions to remove the invalid ones
393
+ let (enabled, disabled, _invalid) = parseExtensions $ map show exts
394
+ let refactExts = map show $ enabled ++ disabled
395
+ (Right <$> applyRefactorings Nothing commands temp refactExts)
396
+ `catches` errorHandlers
397
+ #else
398
+ mbParsedModule <- liftIO $ runAction' $ getParsedModuleWithComments nfp
399
+ res <-
400
+ case mbParsedModule of
401
+ Nothing -> throwE " Apply hint: error parsing the module"
402
+ Just pm -> do
403
+ let anns = pm_annotations pm
404
+ let modu = pm_parsed_source pm
405
+ (modsum, _) <- liftIO $ runAction' $ use_ GetModSummary nfp
406
+ let dflags = ms_hspp_opts modsum
407
+ -- apply-refact uses RigidLayout
408
+ let rigidLayout = deltaOptions RigidLayout
409
+ (anns', modu') <-
410
+ ExceptT $ return $ postParseTransform (Right (anns, [] , dflags, modu)) rigidLayout
411
+ liftIO $ (Right <$> applyRefactorings' Nothing commands anns' modu')
412
+ `catches` errorHandlers
413
+ #endif
369
414
case res of
370
415
Right appliedFile -> do
371
416
let uri = fromNormalizedUri (filePathToUri' nfp)
372
417
let wsEdit = diffText' True (uri, oldContent) (T. pack appliedFile) IncludeDeletions
373
418
liftIO $ logm $ " hlint:applyHint:diff=" ++ show wsEdit
374
419
ExceptT $ return (Right wsEdit)
375
420
Left err ->
376
- throwE ( show err)
421
+ throwE err
377
422
where
378
423
-- | If we are only interested in applying a particular hint then
379
424
-- let's filter out all the irrelevant ideas
@@ -396,10 +441,3 @@ bimapExceptT f g (ExceptT m) = ExceptT (fmap h m) where
396
441
h (Left e) = Left (f e)
397
442
h (Right a) = Right (g a)
398
443
{-# INLINE bimapExceptT #-}
399
-
400
- writeFileUTF8NoNewLineTranslation :: FilePath -> T. Text -> IO ()
401
- writeFileUTF8NoNewLineTranslation file txt =
402
- withFile file WriteMode $ \ h -> do
403
- hSetEncoding h utf8
404
- hSetNewlineMode h noNewlineTranslation
405
- hPutStr h (T. unpack txt)
0 commit comments