|
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