1313module Ide.Plugin.Hlint
1414 (
1515 descriptor
16- -- , provider
16+ -- , provider
1717 ) where
1818import Refact.Apply
1919import Control.Arrow ((&&&) )
@@ -50,18 +50,30 @@ import Development.IDE.Core.RuleTypes
5050import Development.IDE.Core.Rules
5151import Development.IDE.Core.Service
5252import Development.IDE.Core.Shake
53+ import Development.IDE.GHC.Util (hscEnv )
5354import Development.IDE.LSP.Server
5455import Development.IDE.Plugin
5556import Development.IDE.Types.Diagnostics as D
5657import Development.IDE.Types.Location
5758import Development.IDE.Types.Logger
5859import Development.Shake
5960-- import Development.Shake hiding ( Diagnostic )
60- import GHC
61+ import GHC hiding ( DynFlags ( .. ))
6162import GHC.Generics
6263import GHC.Generics (Generic )
6364import SrcLoc
6465import HscTypes (ModIface , ModSummary )
66+
67+ #ifndef GHC_LIB
68+ import GHC (DynFlags (.. ))
69+ import HscTypes (hsc_dflags )
70+ #else
71+ import RealGHC (DynFlags (.. ))
72+ import RealGHC.HscTypes (hsc_dflags )
73+ import qualified RealGHC.EnumSet as EnumSet
74+ import Language.Haskell.GhclibParserEx.GHC.Driver.Session as GhclibParserEx (readExtension )
75+ #endif
76+
6577import Ide.Logger
6678import Ide.Types
6779import Ide.Plugin
@@ -106,9 +118,7 @@ rules = do
106118 ideas <- getIdeas file
107119 return $ (diagnostics file ideas, Just () )
108120
109- hlintDataDir <- liftIO getExecutablePath
110-
111- getHlintSettingsRule (HlintEnabled hlintDataDir True )
121+ getHlintSettingsRule (HlintEnabled [] )
112122
113123 action $ do
114124 files <- getFilesOfInterest
@@ -117,9 +127,9 @@ rules = do
117127 where
118128
119129 diagnostics :: NormalizedFilePath -> Either ParseError [Idea ] -> [FileDiagnostic ]
120- diagnostics file (Right ideas) =
130+ diagnostics file (Right ideas) =
121131 [(file, ShowDiag , ideaToDiagnostic i) | i <- ideas, ideaSeverity i /= Ignore ]
122- diagnostics file (Left parseErr) =
132+ diagnostics file (Left parseErr) =
123133 [(file, ShowDiag , parseErrorToDiagnostic parseErr)]
124134
125135 ideaToDiagnostic :: Idea -> Diagnostic
@@ -131,17 +141,19 @@ rules = do
131141 , _source = Just " hlint"
132142 , _message = T. pack $ show idea
133143 , _relatedInformation = Nothing
144+ , _tags = Nothing
134145 }
135-
146+
136147 parseErrorToDiagnostic :: ParseError -> Diagnostic
137148 parseErrorToDiagnostic (Hlint. ParseError l msg contents) =
138- LSP. Diagnostic {
149+ LSP. Diagnostic {
139150 _range = srcSpanToRange l
140151 , _severity = Just LSP. DsInfo
141152 , _code = Just (LSP. StringValue " parser" )
142153 , _source = Just " hlint"
143154 , _message = T. unlines [T. pack msg,T. pack contents]
144155 , _relatedInformation = Nothing
156+ , _tags = Nothing
145157 }
146158 -- This one is defined in Development.IDE.GHC.Error but here
147159 -- the types could come from ghc-lib or ghc
@@ -158,24 +170,35 @@ rules = do
158170
159171getIdeas :: NormalizedFilePath -> Action (Either ParseError [Idea ])
160172getIdeas nfp = do
161- (classify, hint) <- useNoFile_ GetHlintSettings
173+ logm $ " getIdeas:file:" ++ show nfp
174+ (flags, classify, hint) <- useNoFile_ GetHlintSettings
162175 let applyHints' modEx = applyHints classify hint [modEx]
163- fmap (fmap applyHints') moduleEx
164- where moduleEx :: Action (Either ParseError ModuleEx )
165- moduleEx = do
176+ fmap (fmap applyHints') ( moduleEx flags)
177+ where moduleEx :: ParseFlags -> Action (Either ParseError ModuleEx )
178+ moduleEx flags = do
166179#ifndef GHC_LIB
167- pm <- getParsedModule fnp
180+ pm <- getParsedModule nfp
168181 let anns = pm_annotations pm
169182 let modu = pm_parsed_source pm
170183 return $ Right (createModuleEx anns modu)
171184#else
172- liftIO $ parseModuleEx defaultParseFlags (fromNormalizedFilePath nfp) Nothing
185+ flags' <- setExtensions flags
186+ liftIO $ parseModuleEx flags' (fromNormalizedFilePath nfp) Nothing
187+
188+ setExtensions flags = do
189+ hsc <- hscEnv <$> use_ GhcSession nfp
190+ let dflags = hsc_dflags hsc
191+ let hscExts = EnumSet. toList (extensionFlags dflags)
192+ logm $ " getIdeas:setExtensions:hscExtensions:" ++ show hscExts
193+ let hlintExts = mapMaybe (GhclibParserEx. readExtension . show ) hscExts
194+ logm $ " getIdeas:setExtensions:hlintExtensions:" ++ show hlintExts
195+ return $ flags { enabledExtensions = hlintExts }
173196#endif
174197
175198-- ---------------------------------------------------------------------
176199
177200data HlintUsage
178- = HlintEnabled { hlintUseDataDir :: FilePath , hlintAllowOverrides :: Bool }
201+ = HlintEnabled { cmdArgs :: [ String ] }
179202 | HlintDisabled
180203 deriving Show
181204
@@ -185,42 +208,20 @@ instance Hashable GetHlintSettings
185208instance NFData GetHlintSettings
186209instance NFData Hint where rnf = rwhnf
187210instance NFData Classify where rnf = rwhnf
211+ instance NFData ParseFlags where rnf = rwhnf
188212instance Show Hint where show = const " <hint>"
213+ instance Show ParseFlags where show = const " <parseFlags>"
189214instance Binary GetHlintSettings
190215
191- type instance RuleResult GetHlintSettings = ([Classify ], Hint )
216+ type instance RuleResult GetHlintSettings = (ParseFlags , [Classify ], Hint )
192217
193218getHlintSettingsRule :: HlintUsage -> Rules ()
194219getHlintSettingsRule usage =
195220 defineNoFile $ \ GetHlintSettings ->
196221 liftIO $ case usage of
197- HlintEnabled dir enableOverrides -> hlintSettings dir enableOverrides
222+ HlintEnabled cmdArgs -> argsSettings cmdArgs
198223 HlintDisabled -> fail " hlint configuration unspecified"
199224
200- hlintSettings :: FilePath -> Bool -> IO ([Classify ], Hint )
201- hlintSettings hlintDataDir enableOverrides = do
202- curdir <- getCurrentDirectory
203- home <- ((: [] ) <$> getHomeDirectory) `catchIOError` (const $ return [] )
204- hlintYaml <- if enableOverrides
205- then
206- findM Dir. doesFileExist $
207- map (</> " .hlint.yaml" ) (ancestors curdir ++ home)
208- else
209- return Nothing
210- (_, cs, hs) <- foldMapM parseSettings $
211- (hlintDataDir </> " hlint.yaml" ) : maybeToList hlintYaml
212- return (cs, hs)
213- where
214- ancestors = init . map joinPath . reverse . inits . splitPath
215- -- `findSettings` calls `readFilesConfig` which in turn calls
216- -- `readFileConfigYaml` which finally calls `decodeFileEither` from
217- -- the `yaml` library. Annoyingly that function catches async
218- -- exceptions and in particular, it ends up catching
219- -- `ThreadKilled`. So, we have to mask to stop it from doing that.
220- parseSettings f = mask $ \ unmask ->
221- findSettings (unmask . const (return (f, Nothing ))) (Just f)
222- foldMapM f = foldlM (\ acc a -> do w <- f a; return $! mappend acc w) mempty
223-
224225-- ---------------------------------------------------------------------
225226
226227codeActionProvider :: CodeActionProvider
@@ -253,8 +254,8 @@ codeActionProvider _ _ plId docId _ context = (Right . LSP.List . map CACodeActi
253254
254255applyAllCmd :: CommandFunction Uri
255256applyAllCmd _lf ide uri = do
256- let file = maybe (error $ show uri ++ " is not a file" )
257- toNormalizedFilePath'
257+ let file = maybe (error $ show uri ++ " is not a file. " )
258+ toNormalizedFilePath'
258259 (uriToFilePath' uri)
259260 logm $ " applyAllCmd:file=" ++ show file
260261 res <- applyHint ide file Nothing
@@ -283,12 +284,12 @@ data OneHint = OneHint
283284applyOneCmd :: CommandFunction ApplyOneParams
284285applyOneCmd _lf ide (AOP uri pos title) = do
285286 let oneHint = OneHint pos title
286- let file = maybe (error $ show uri ++ " is not a file" ) toNormalizedFilePath'
287+ let file = maybe (error $ show uri ++ " is not a file. " ) toNormalizedFilePath'
287288 (uriToFilePath' uri)
288289 res <- applyHint ide file (Just oneHint)
289290 logm $ " applyOneCmd:file=" ++ show file
290291 logm $ " applyOneCmd:res=" ++ show res
291- return $
292+ return $
292293 case res of
293294 Left err -> (Left (responseError (T. pack $ " applyOne: " ++ show err)), Nothing )
294295 Right fs -> (Right Null , Just (WorkspaceApplyEdit , ApplyWorkspaceEditParams fs))
@@ -297,7 +298,7 @@ applyHint :: IdeState -> NormalizedFilePath -> Maybe OneHint -> IO (Either Strin
297298applyHint ide nfp mhint =
298299 runExceptT $ do
299300 ideas <- bimapExceptT showParseError id $ ExceptT $ liftIO $ runAction " applyHint" ide $ getIdeas nfp
300- let ideas' = maybe ideas (`filterIdeas` ideas) mhint
301+ let ideas' = maybe ideas (`filterIdeas` ideas) mhint
301302 let commands = map (show &&& ideaRefactoring) ideas'
302303 liftIO $ logm $ " applyHint:apply=" ++ show commands
303304 -- set Nothing as "position" for "applyRefactorings" because
@@ -328,8 +329,8 @@ applyHint ide nfp mhint =
328329 liftIO $ logm $ " applyHint:diff=" ++ show wsEdit
329330 ExceptT $ Right <$> (return wsEdit)
330331 Left err ->
331- throwE (show err)
332- where
332+ throwE (show err)
333+ where
333334 -- | If we are only interested in applying a particular hint then
334335 -- let's filter out all the irrelevant ideas
335336 filterIdeas :: OneHint -> [Idea ] -> [Idea ]
@@ -339,7 +340,7 @@ applyHint ide nfp mhint =
339340 in filter (\ i -> ideaHint i == title' && ideaPos i == (l+ 1 , c+ 1 )) ideas
340341
341342 toRealSrcSpan (RealSrcSpan real) = real
342- toRealSrcSpan (UnhelpfulSpan _ ) = error " No real souce span"
343+ toRealSrcSpan (UnhelpfulSpan x ) = error $ " No real source span: " ++ show x
343344
344345 showParseError :: Hlint. ParseError -> String
345346 showParseError (Hlint. ParseError location message content) =
@@ -350,7 +351,7 @@ bimapExceptT :: Functor m => (e -> f) -> (a -> b) -> ExceptT e m a -> ExceptT f
350351bimapExceptT f g (ExceptT m) = ExceptT (fmap h m) where
351352 h (Left e) = Left (f e)
352353 h (Right a) = Right (g a)
353- {-# INLINE bimapExceptT #-}
354+ {-# INLINE bimapExceptT #-}
354355-- ---------------------------------------------------------------------
355356{-
356357{- # LANGUAGE CPP #-}
0 commit comments