|
3 | 3 | -- | An HLS plugin to provide code actions to change type signatures
|
4 | 4 | module Ide.Plugin.ChangeTypeSignature (descriptor
|
5 | 5 | -- * For Unit Tests
|
| 6 | + , Log(..) |
6 | 7 | , errorMessageRegexes
|
7 | 8 | ) where
|
8 | 9 |
|
9 | 10 | import Control.Lens
|
10 | 11 | import Control.Monad (guard)
|
11 | 12 | import Control.Monad.IO.Class (MonadIO)
|
12 |
| -import Control.Monad.Trans.Except (ExceptT) |
| 13 | +import Control.Monad.Trans.Class (MonadTrans(lift)) |
| 14 | +import Control.Monad.Trans.Except (ExceptT (..)) |
| 15 | +import Control.Monad.Trans.Maybe (MaybeT(..), hoistMaybe) |
13 | 16 | import Data.Foldable (asum)
|
14 | 17 | import qualified Data.Map as Map
|
15 |
| -import Data.Maybe (mapMaybe) |
| 18 | +import Data.Maybe (mapMaybe, catMaybes) |
16 | 19 | import Data.Text (Text)
|
17 | 20 | import qualified Data.Text as T
|
18 |
| -import Development.IDE (realSrcSpanToRange, IdeState (..), FileDiagnostic, fdLspDiagnosticL, fdStructuredMessageL) |
| 21 | +import Development.IDE (realSrcSpanToRange, |
| 22 | + IdeState (..), |
| 23 | + FileDiagnostic, |
| 24 | + fdLspDiagnosticL, |
| 25 | + fdStructuredMessageL, |
| 26 | + logWith, |
| 27 | + Pretty (..), |
| 28 | + Priority (..), |
| 29 | + Recorder, |
| 30 | + WithPriority) |
19 | 31 | import Development.IDE.Core.PluginUtils
|
20 | 32 | import Development.IDE.Core.RuleTypes (GetParsedModule (GetParsedModule))
|
21 |
| -import Development.IDE.GHC.Compat |
| 33 | +import Development.IDE.GHC.Compat hiding (vcat) |
22 | 34 | import Development.IDE.GHC.Util (printOutputable)
|
| 35 | +import Development.IDE.Types.Diagnostics (_SomeStructuredMessage) |
| 36 | +import Development.IDE.GHC.Compat.Error (msgEnvelopeErrorL, _TcRnMessage) |
23 | 37 | import Generics.SYB (extQ, something)
|
| 38 | +import GHC.Tc.Errors.Types (TcSolverReportMsg(..), |
| 39 | + TcRnMessage (..), |
| 40 | + TcRnMessage (..), |
| 41 | + SolverReportWithCtxt (..), |
| 42 | + TcRnMessageDetailed (..), |
| 43 | + MismatchMsg (..), |
| 44 | + ErrInfo (..)) |
| 45 | +import qualified Ide.Logger as Logger |
24 | 46 | import Ide.Plugin.Error (PluginError,
|
25 | 47 | getNormalizedFilePathE)
|
26 | 48 | import Ide.Types (PluginDescriptor (..),
|
27 | 49 | PluginId (PluginId),
|
28 | 50 | PluginMethodHandler,
|
29 | 51 | defaultPluginDescriptor,
|
30 |
| - mkPluginHandler) |
| 52 | + mkPluginHandler, |
| 53 | + HandlerM, |
| 54 | + Config) |
31 | 55 | import Language.LSP.Protocol.Message
|
32 | 56 | import Language.LSP.Protocol.Types
|
33 | 57 | import Text.Regex.TDFA ((=~))
|
34 |
| -import Development.IDE.Types.Diagnostics (_SomeStructuredMessage) |
35 |
| -import Development.IDE.GHC.Compat.Error (msgEnvelopeErrorL, _TcRnMessage) |
36 |
| -import GHC.Tc.Errors.Types (TcSolverReportMsg(..), TcRnMessage (..), TcRnMessage (..), SolverReportWithCtxt (..), TcRnMessageDetailed (..), MismatchMsg (..), ErrInfo (..)) |
37 | 58 |
|
38 |
| -descriptor :: PluginId -> PluginDescriptor IdeState |
39 |
| -descriptor plId = (defaultPluginDescriptor plId "Provides a code action to change the type signature of a binding if it is wrong") |
40 |
| - { pluginHandlers = mkPluginHandler SMethod_TextDocumentCodeAction (codeActionHandler plId) } |
| 59 | +data Log |
| 60 | + = LogErrInfoCtxt ErrInfo |
| 61 | + | LogFindSigLocFailure DeclName |
41 | 62 |
|
42 |
| -codeActionHandler :: PluginId -> PluginMethodHandler IdeState 'Method_TextDocumentCodeAction |
43 |
| -codeActionHandler plId ideState _ CodeActionParams{_textDocument, _range} = do |
| 63 | +instance Pretty Log where |
| 64 | + pretty = \case |
| 65 | + LogErrInfoCtxt (ErrInfo ctxt suppl) -> |
| 66 | + Logger.vcat [fromSDoc ctxt, fromSDoc suppl] |
| 67 | + LogFindSigLocFailure name -> |
| 68 | + pretty ("Lookup signature location failure: " <> name) |
| 69 | + where |
| 70 | + fromSDoc = pretty . printOutputable |
| 71 | + |
| 72 | +descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState |
| 73 | +descriptor recorder plId = |
| 74 | + (defaultPluginDescriptor plId "Provides a code action to change the type signature of a binding if it is wrong") |
| 75 | + { pluginHandlers = mkPluginHandler SMethod_TextDocumentCodeAction (codeActionHandler recorder plId) |
| 76 | + } |
| 77 | + |
| 78 | +codeActionHandler |
| 79 | + :: Recorder (WithPriority Log) |
| 80 | + -> PluginId |
| 81 | + -> PluginMethodHandler IdeState 'Method_TextDocumentCodeAction |
| 82 | +codeActionHandler recorder plId ideState _ CodeActionParams{_textDocument, _range} = do |
44 | 83 | let TextDocumentIdentifier uri = _textDocument
|
45 | 84 | nfp <- getNormalizedFilePathE uri
|
46 | 85 | decls <- getDecls plId ideState nfp
|
47 | 86 |
|
48 | 87 | activeDiagnosticsInRange (shakeExtras ideState) nfp _range >>= \case
|
49 | 88 | Nothing -> pure (InL [])
|
50 | 89 | Just fileDiags -> do
|
51 |
| - let actions = mapMaybe (generateAction plId uri decls) fileDiags |
52 |
| - pure (InL actions) |
| 90 | + actions <- lift $ mapM (generateAction recorder plId uri decls) fileDiags |
| 91 | + pure (InL (catMaybes actions)) |
53 | 92 |
|
54 | 93 | getDecls :: MonadIO m => PluginId -> IdeState -> NormalizedFilePath -> ExceptT PluginError m [LHsDecl GhcPs]
|
55 | 94 | getDecls (PluginId changeTypeSignatureId) state =
|
@@ -79,28 +118,48 @@ data ChangeSignature = ChangeSignature {
|
79 | 118 | }
|
80 | 119 |
|
81 | 120 | -- | Create a CodeAction from a Diagnostic
|
82 |
| -generateAction :: PluginId -> Uri -> [LHsDecl GhcPs] -> FileDiagnostic -> Maybe (Command |? CodeAction) |
83 |
| -generateAction plId uri decls fileDiag = |
84 |
| - changeSigToCodeAction plId uri <$> diagnosticToChangeSig decls fileDiag |
| 121 | +generateAction |
| 122 | + :: Recorder (WithPriority Log) |
| 123 | + -> PluginId |
| 124 | + -> Uri |
| 125 | + -> [LHsDecl GhcPs] |
| 126 | + -> FileDiagnostic |
| 127 | + -> HandlerM Config (Maybe (Command |? CodeAction)) |
| 128 | +generateAction recorder plId uri decls fileDiag = do |
| 129 | + changeSig <- diagnosticToChangeSig recorder decls fileDiag |
| 130 | + pure $ |
| 131 | + changeSigToCodeAction plId uri <$> changeSig |
| 132 | + |
85 | 133 |
|
86 | 134 | -- | Convert a diagnostic into a ChangeSignature and add the proper SrcSpan
|
87 |
| -diagnosticToChangeSig :: [LHsDecl GhcPs] -> FileDiagnostic -> Maybe ChangeSignature |
88 |
| -diagnosticToChangeSig decls diagnostic = do |
89 |
| - msg <- diagnostic ^. fdStructuredMessageL ^? _SomeStructuredMessage |
90 |
| - tcRnMsg <- msg ^. msgEnvelopeErrorL ^? _TcRnMessage |
91 |
| - (solverReport, errInfo) <- findSolverReport tcRnMsg |
92 |
| - mismatch <- findMismatchMessage solverReport |
93 |
| - (expectedType', actualType') <- findTypeEqMismatch mismatch |
94 |
| - errInfo' <- errInfo |
| 135 | +diagnosticToChangeSig |
| 136 | + :: Recorder (WithPriority Log) |
| 137 | + -> [LHsDecl GhcPs] |
| 138 | + -> FileDiagnostic |
| 139 | + -> HandlerM Config (Maybe ChangeSignature) |
| 140 | +diagnosticToChangeSig recorder decls diagnostic = runMaybeT $ do |
| 141 | + (expectedType, actualType, errInfo) <- hoistMaybe $ do |
| 142 | + msg <- diagnostic ^. fdStructuredMessageL ^? _SomeStructuredMessage |
| 143 | + tcRnMsg <- msg ^. msgEnvelopeErrorL ^? _TcRnMessage |
| 144 | + (solverReport, errInfo) <- findSolverReport tcRnMsg |
| 145 | + mismatch <- findMismatchMessage solverReport |
| 146 | + (expectedType', actualType') <- findTypeEqMismatch mismatch |
| 147 | + errInfo' <- errInfo |
| 148 | + |
| 149 | + pure (showType expectedType', showType actualType', errInfo') |
95 | 150 |
|
96 |
| - let expectedType = showType expectedType' |
97 |
| - actualType = showType actualType' |
| 151 | + logWith recorder Development.IDE.Warning (LogErrInfoCtxt errInfo) |
98 | 152 |
|
99 |
| - declName <- matchingDiagnostic errInfo' |
100 |
| - declSrcSpan <- findSigLocOfStringDecl decls expectedType (T.unpack declName) |
| 153 | + declName <- hoistMaybe (matchingDiagnostic errInfo) |
101 | 154 |
|
| 155 | + declSrcSpan <- |
| 156 | + case findSigLocOfStringDecl decls expectedType (T.unpack declName) of |
| 157 | + Just x -> pure x |
| 158 | + Nothing -> do |
| 159 | + logWith recorder Development.IDE.Warning (LogFindSigLocFailure declName) |
| 160 | + hoistMaybe Nothing |
102 | 161 |
|
103 |
| - Just (ChangeSignature{..}) |
| 162 | + pure ChangeSignature{..} |
104 | 163 | where
|
105 | 164 | showType :: Type -> Text
|
106 | 165 | showType = T.pack . showSDocUnsafe . pprTidiedType
|
@@ -135,8 +194,7 @@ matchingDiagnostic ErrInfo{errInfoContext} =
|
135 | 194 | unwrapMatch (_, _, _, [name]) = Just name
|
136 | 195 | unwrapMatch _ = Nothing
|
137 | 196 |
|
138 |
| - -- TODO: Unsafe? |
139 |
| - errInfoTxt = T.pack $ showSDocUnsafe errInfoContext |
| 197 | + errInfoTxt = printOutputable errInfoContext |
140 | 198 |
|
141 | 199 | -- | List of regexes that match various Error Messages
|
142 | 200 | errorMessageRegexes :: [Text]
|
|
0 commit comments