Skip to content

Commit 34f5e41

Browse files
committed
Implement hlint applyOneCmd
1 parent 76eee09 commit 34f5e41

File tree

1 file changed

+65
-30
lines changed

1 file changed

+65
-30
lines changed

src/Ide/Plugin/Hlint.hs

Lines changed: 65 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@ module Ide.Plugin.Hlint
1616
--, provider
1717
) where
1818
import Refact.Apply
19+
import Control.Arrow ((&&&))
1920
import Control.DeepSeq
2021
import Control.Exception
2122
import Control.Lens ((^.))
2223
import Control.Monad
2324
import Control.Monad.Extra
2425
import Control.Monad.Trans.Maybe
26+
import Control.Monad.IO.Class
27+
import Control.Monad.Trans.Except
2528
import qualified Data.Aeson as Aeson
2629
import Data.Aeson.Types (ToJSON(..), FromJSON(..), Value(..), Result(..))
2730
import Data.Binary
@@ -39,6 +42,7 @@ import Data.Maybe
3942
import qualified Data.Set as Set
4043
import Data.Set (Set)
4144
import qualified Data.Text as T
45+
import qualified Data.Text.IO as T
4246
import Data.Typeable
4347
import Data.Typeable (Typeable)
4448
import Development.IDE.Core.OfInterest
@@ -58,8 +62,10 @@ import GHC.Generics
5862
import GHC.Generics (Generic)
5963
import SrcLoc
6064
import HscTypes (ModIface, ModSummary)
65+
import Ide.Logger
6166
import Ide.Types
6267
import Ide.Plugin
68+
import Ide.PluginUtils
6369
import Language.Haskell.HLint
6470
import Language.Haskell.HLint as Hlint
6571
import qualified Language.Haskell.LSP.Core as LSP
@@ -97,10 +103,8 @@ type instance RuleResult GetHlintDiagnostics = ()
97103
rules :: Rules ()
98104
rules = do
99105
define $ \GetHlintDiagnostics file -> do
100-
(classify, hint) <- useNoFile_ GetHlintSettings
101-
eModuleEx <- getModuleEx file
102-
let getIdeas moduleEx = applyHints classify hint [moduleEx]
103-
return $ (diagnostics file (fmap getIdeas eModuleEx), Just ())
106+
ideas <- getIdeas file
107+
return $ (diagnostics file ideas, Just ())
104108

105109
hlintDataDir <- liftIO getExecutablePath
106110

@@ -112,17 +116,6 @@ rules = do
112116

113117
where
114118

115-
getModuleEx :: NormalizedFilePath -> Action (Either ParseError ModuleEx)
116-
getModuleEx fp = do
117-
#ifndef GHC_LIB
118-
pm <- getParsedModule fp
119-
let anns = pm_annotations pm
120-
let modu = pm_parsed_source pm
121-
return $ Right (createModuleEx anns modu)
122-
#else
123-
liftIO $ parseModuleEx defaultParseFlags (fromNormalizedFilePath fp) Nothing
124-
#endif
125-
126119
diagnostics :: NormalizedFilePath -> Either ParseError [Idea] -> [FileDiagnostic]
127120
diagnostics file (Right ideas) =
128121
[(file, ShowDiag, ideaToDiagnostic i) | i <- ideas, ideaSeverity i /= Ignore]
@@ -163,6 +156,22 @@ rules = do
163156
}
164157
srcSpanToRange (UnhelpfulSpan _) = noRange
165158

159+
getIdeas :: NormalizedFilePath -> Action (Either ParseError [Idea])
160+
getIdeas nfp = do
161+
(classify, hint) <- useNoFile_ GetHlintSettings
162+
let applyHints' modEx = applyHints classify hint [modEx]
163+
fmap (fmap applyHints') moduleEx
164+
where moduleEx :: Action (Either ParseError ModuleEx)
165+
moduleEx = do
166+
#ifndef GHC_LIB
167+
pm <- getParsedModule fnp
168+
let anns = pm_annotations pm
169+
let modu = pm_parsed_source pm
170+
return $ Right (createModuleEx anns modu)
171+
#else
172+
liftIO $ parseModuleEx defaultParseFlags (fromNormalizedFilePath nfp) Nothing
173+
#endif
174+
166175
-- ---------------------------------------------------------------------
167176

168177
data HlintUsage
@@ -257,22 +266,24 @@ data OneHint = OneHint
257266
} deriving (Eq, Show)
258267

259268
applyOneCmd :: CommandFunction ApplyOneParams
260-
applyOneCmd _lf ide (AOP uri pos title) = do
269+
applyOneCmd _lf ide (AOP uri pos title) = do
261270
let oneHint = OneHint pos title
262-
let file = uriToFilePath' uri
263-
applyHint file (Just oneHint)
271+
let file = maybe (error $ show uri ++ " is not a file") toNormalizedFilePath'
272+
(uriToFilePath' uri)
273+
res <- applyHint ide file (Just oneHint)
264274
logm $ "applyOneCmd:file=" ++ show file
265275
logm $ "applyOneCmd:res=" ++ show res
266-
case res of
267-
Left err -> return $ IdeResultFail
268-
(IdeError PluginError (T.pack $ "applyOne: " ++ show err) Null)
269-
Right fs -> return (IdeResultOk fs)
276+
return $
277+
case res of
278+
Left err -> (Left (responseError (T.pack $ "applyOne: " ++ show err)), Nothing)
279+
Right fs -> (Right Null, Just (WorkspaceApplyEdit, ApplyWorkspaceEditParams fs))
270280

271-
applyHint :: FilePath -> Maybe OneHint -> IO (Either String WorkspaceEdit)
272-
applyHint fp mhint fileMap = do
281+
applyHint :: IdeState -> NormalizedFilePath -> Maybe OneHint -> IO (Either String WorkspaceEdit)
282+
applyHint ide nfp mhint =
273283
runExceptT $ do
274-
ideas <- getIdeas fp mhint
275-
let commands = map (show &&& ideaRefactoring) ideas
284+
ideas <- bimapExceptT showParseError id $ ExceptT $ liftIO $ runAction "applyHint" ide $ getIdeas nfp
285+
let ideas' = maybe ideas (`filterIdeas` ideas) mhint
286+
let commands = map (show &&& ideaRefactoring) ideas'
276287
liftIO $ logm $ "applyHint:apply=" ++ show commands
277288
-- set Nothing as "position" for "applyRefactorings" because
278289
-- applyRefactorings expects the provided position to be _within_ the scope
@@ -289,18 +300,42 @@ applyHint fp mhint fileMap = do
289300
-- If we provide "applyRefactorings" with "Just (1,13)" then
290301
-- the "Redundant bracket" hint will never be executed
291302
-- because SrcSpan (1,20,??,??) doesn't contain position (1,13).
303+
let fp = fromNormalizedFilePath nfp
292304
res <- liftIO $ (Right <$> applyRefactorings Nothing commands fp) `catches`
293305
[ Handler $ \e -> return (Left (show (e :: IOException)))
294306
, Handler $ \e -> return (Left (show (e :: ErrorCall)))
295307
]
296308
case res of
297309
Right appliedFile -> do
298-
diff <- ExceptT $ Right <$> makeDiffResult fp (T.pack appliedFile) fileMap
299-
liftIO $ logm $ "applyHint:diff=" ++ show diff
300-
return diff
310+
let uri = fromNormalizedUri (filePathToUri' nfp)
311+
oldContent <- liftIO $ T.readFile fp
312+
let wsEdit = diffText' True (uri, oldContent) (T.pack appliedFile) IncludeDeletions
313+
liftIO $ logm $ "applyHint:diff=" ++ show wsEdit
314+
ExceptT $ Right <$> (return wsEdit)
301315
Left err ->
302-
throwE (show err)
316+
throwE (show err)
317+
where
318+
-- | If we are only interested in applying a particular hint then
319+
-- let's filter out all the irrelevant ideas
320+
filterIdeas :: OneHint -> [Idea] -> [Idea]
321+
filterIdeas (OneHint (Position l c) title) ideas =
322+
let title' = T.unpack title
323+
ideaPos = (srcSpanStartLine &&& srcSpanStartCol) . toRealSrcSpan . ideaSpan
324+
in filter (\i -> ideaHint i == title' && ideaPos i == (l+1, c+1)) ideas
325+
326+
toRealSrcSpan (RealSrcSpan real) = real
327+
toRealSrcSpan (UnhelpfulSpan _) = error "No real souce span"
328+
329+
showParseError :: Hlint.ParseError -> String
330+
showParseError (Hlint.ParseError location message content) =
331+
unlines [show location, message, content]
303332

333+
-- | Map over both failure and success.
334+
bimapExceptT :: Functor m => (e -> f) -> (a -> b) -> ExceptT e m a -> ExceptT f m b
335+
bimapExceptT f g (ExceptT m) = ExceptT (fmap h m) where
336+
h (Left e) = Left (f e)
337+
h (Right a) = Right (g a)
338+
{-# INLINE bimapExceptT #-}
304339
-- ---------------------------------------------------------------------
305340
{-
306341
{-# LANGUAGE CPP #-}

0 commit comments

Comments
 (0)