13
13
module Ide.Plugin.Hlint
14
14
(
15
15
descriptor
16
- -- , provider
16
+ -- , provider
17
17
) where
18
18
import Refact.Apply
19
19
import Control.Arrow ((&&&) )
@@ -50,18 +50,30 @@ import Development.IDE.Core.RuleTypes
50
50
import Development.IDE.Core.Rules
51
51
import Development.IDE.Core.Service
52
52
import Development.IDE.Core.Shake
53
+ import Development.IDE.GHC.Util (hscEnv )
53
54
import Development.IDE.LSP.Server
54
55
import Development.IDE.Plugin
55
56
import Development.IDE.Types.Diagnostics as D
56
57
import Development.IDE.Types.Location
57
58
import Development.IDE.Types.Logger
58
59
import Development.Shake
59
60
-- import Development.Shake hiding ( Diagnostic )
60
- import GHC
61
+ import GHC hiding ( DynFlags ( .. ))
61
62
import GHC.Generics
62
63
import GHC.Generics (Generic )
63
64
import SrcLoc
64
65
import 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
+
65
77
import Ide.Logger
66
78
import Ide.Types
67
79
import Ide.Plugin
@@ -106,9 +118,7 @@ rules = do
106
118
ideas <- getIdeas file
107
119
return $ (diagnostics file ideas, Just () )
108
120
109
- hlintDataDir <- liftIO getExecutablePath
110
-
111
- getHlintSettingsRule (HlintEnabled hlintDataDir True )
121
+ getHlintSettingsRule (HlintEnabled [] )
112
122
113
123
action $ do
114
124
files <- getFilesOfInterest
@@ -117,9 +127,9 @@ rules = do
117
127
where
118
128
119
129
diagnostics :: NormalizedFilePath -> Either ParseError [Idea ] -> [FileDiagnostic ]
120
- diagnostics file (Right ideas) =
130
+ diagnostics file (Right ideas) =
121
131
[(file, ShowDiag , ideaToDiagnostic i) | i <- ideas, ideaSeverity i /= Ignore ]
122
- diagnostics file (Left parseErr) =
132
+ diagnostics file (Left parseErr) =
123
133
[(file, ShowDiag , parseErrorToDiagnostic parseErr)]
124
134
125
135
ideaToDiagnostic :: Idea -> Diagnostic
@@ -131,17 +141,19 @@ rules = do
131
141
, _source = Just " hlint"
132
142
, _message = T. pack $ show idea
133
143
, _relatedInformation = Nothing
144
+ , _tags = Nothing
134
145
}
135
-
146
+
136
147
parseErrorToDiagnostic :: ParseError -> Diagnostic
137
148
parseErrorToDiagnostic (Hlint. ParseError l msg contents) =
138
- LSP. Diagnostic {
149
+ LSP. Diagnostic {
139
150
_range = srcSpanToRange l
140
151
, _severity = Just LSP. DsInfo
141
152
, _code = Just (LSP. StringValue " parser" )
142
153
, _source = Just " hlint"
143
154
, _message = T. unlines [T. pack msg,T. pack contents]
144
155
, _relatedInformation = Nothing
156
+ , _tags = Nothing
145
157
}
146
158
-- This one is defined in Development.IDE.GHC.Error but here
147
159
-- the types could come from ghc-lib or ghc
@@ -158,24 +170,35 @@ rules = do
158
170
159
171
getIdeas :: NormalizedFilePath -> Action (Either ParseError [Idea ])
160
172
getIdeas nfp = do
161
- (classify, hint) <- useNoFile_ GetHlintSettings
173
+ logm $ " getIdeas:file:" ++ show nfp
174
+ (flags, classify, hint) <- useNoFile_ GetHlintSettings
162
175
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
166
179
#ifndef GHC_LIB
167
- pm <- getParsedModule fnp
180
+ pm <- getParsedModule nfp
168
181
let anns = pm_annotations pm
169
182
let modu = pm_parsed_source pm
170
183
return $ Right (createModuleEx anns modu)
171
184
#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 }
173
196
#endif
174
197
175
198
-- ---------------------------------------------------------------------
176
199
177
200
data HlintUsage
178
- = HlintEnabled { hlintUseDataDir :: FilePath , hlintAllowOverrides :: Bool }
201
+ = HlintEnabled { cmdArgs :: [ String ] }
179
202
| HlintDisabled
180
203
deriving Show
181
204
@@ -185,42 +208,20 @@ instance Hashable GetHlintSettings
185
208
instance NFData GetHlintSettings
186
209
instance NFData Hint where rnf = rwhnf
187
210
instance NFData Classify where rnf = rwhnf
211
+ instance NFData ParseFlags where rnf = rwhnf
188
212
instance Show Hint where show = const " <hint>"
213
+ instance Show ParseFlags where show = const " <parseFlags>"
189
214
instance Binary GetHlintSettings
190
215
191
- type instance RuleResult GetHlintSettings = ([Classify ], Hint )
216
+ type instance RuleResult GetHlintSettings = (ParseFlags , [Classify ], Hint )
192
217
193
218
getHlintSettingsRule :: HlintUsage -> Rules ()
194
219
getHlintSettingsRule usage =
195
220
defineNoFile $ \ GetHlintSettings ->
196
221
liftIO $ case usage of
197
- HlintEnabled dir enableOverrides -> hlintSettings dir enableOverrides
222
+ HlintEnabled cmdArgs -> argsSettings cmdArgs
198
223
HlintDisabled -> fail " hlint configuration unspecified"
199
224
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
-
224
225
-- ---------------------------------------------------------------------
225
226
226
227
codeActionProvider :: CodeActionProvider
@@ -253,8 +254,8 @@ codeActionProvider _ _ plId docId _ context = (Right . LSP.List . map CACodeActi
253
254
254
255
applyAllCmd :: CommandFunction Uri
255
256
applyAllCmd _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'
258
259
(uriToFilePath' uri)
259
260
logm $ " applyAllCmd:file=" ++ show file
260
261
res <- applyHint ide file Nothing
@@ -283,12 +284,12 @@ data OneHint = OneHint
283
284
applyOneCmd :: CommandFunction ApplyOneParams
284
285
applyOneCmd _lf ide (AOP uri pos title) = do
285
286
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'
287
288
(uriToFilePath' uri)
288
289
res <- applyHint ide file (Just oneHint)
289
290
logm $ " applyOneCmd:file=" ++ show file
290
291
logm $ " applyOneCmd:res=" ++ show res
291
- return $
292
+ return $
292
293
case res of
293
294
Left err -> (Left (responseError (T. pack $ " applyOne: " ++ show err)), Nothing )
294
295
Right fs -> (Right Null , Just (WorkspaceApplyEdit , ApplyWorkspaceEditParams fs))
@@ -297,7 +298,7 @@ applyHint :: IdeState -> NormalizedFilePath -> Maybe OneHint -> IO (Either Strin
297
298
applyHint ide nfp mhint =
298
299
runExceptT $ do
299
300
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
301
302
let commands = map (show &&& ideaRefactoring) ideas'
302
303
liftIO $ logm $ " applyHint:apply=" ++ show commands
303
304
-- set Nothing as "position" for "applyRefactorings" because
@@ -328,8 +329,8 @@ applyHint ide nfp mhint =
328
329
liftIO $ logm $ " applyHint:diff=" ++ show wsEdit
329
330
ExceptT $ Right <$> (return wsEdit)
330
331
Left err ->
331
- throwE (show err)
332
- where
332
+ throwE (show err)
333
+ where
333
334
-- | If we are only interested in applying a particular hint then
334
335
-- let's filter out all the irrelevant ideas
335
336
filterIdeas :: OneHint -> [Idea ] -> [Idea ]
@@ -339,7 +340,7 @@ applyHint ide nfp mhint =
339
340
in filter (\ i -> ideaHint i == title' && ideaPos i == (l+ 1 , c+ 1 )) ideas
340
341
341
342
toRealSrcSpan (RealSrcSpan real) = real
342
- toRealSrcSpan (UnhelpfulSpan _ ) = error " No real souce span"
343
+ toRealSrcSpan (UnhelpfulSpan x ) = error $ " No real source span: " ++ show x
343
344
344
345
showParseError :: Hlint. ParseError -> String
345
346
showParseError (Hlint. ParseError location message content) =
@@ -350,7 +351,7 @@ bimapExceptT :: Functor m => (e -> f) -> (a -> b) -> ExceptT e m a -> ExceptT f
350
351
bimapExceptT f g (ExceptT m) = ExceptT (fmap h m) where
351
352
h (Left e) = Left (f e)
352
353
h (Right a) = Right (g a)
353
- {-# INLINE bimapExceptT #-}
354
+ {-# INLINE bimapExceptT #-}
354
355
-- ---------------------------------------------------------------------
355
356
{-
356
357
{- # LANGUAGE CPP #-}
0 commit comments