@@ -6,36 +6,51 @@ module Development.IDE.Plugin.TypeLenses
6
6
)
7
7
where
8
8
9
+ import Control.Applicative ((<|>) )
9
10
import Control.Monad.IO.Class
10
- import Data.Aeson.Types (Value (.. ), toJSON )
11
- import qualified Data.HashMap.Strict as Map
12
- import qualified Data.Text as T
13
- import Development.IDE.Core.RuleTypes (TypeCheck (TypeCheck ))
14
- import Development.IDE.Core.Rules (IdeState , runAction )
15
- import Development.IDE.Core.Service (getDiagnostics )
16
- import Development.IDE.Core.Shake (getHiddenDiagnostics , use )
17
- import Development.IDE.Types.Location (Position (Position , _character , _line ),
18
- Range (Range , _end , _start ),
19
- toNormalizedFilePath' ,
20
- uriToFilePath' )
21
- import Ide.PluginUtils (mkLspCommand )
22
- import Ide.Types (CommandFunction ,
23
- CommandId (CommandId ),
24
- PluginCommand (PluginCommand ),
25
- PluginDescriptor (.. ),
26
- PluginId ,
27
- defaultPluginDescriptor ,
28
- mkPluginHandler )
29
- import qualified Language.LSP.Server as LSP
30
- import Language.LSP.Types (ApplyWorkspaceEditParams (ApplyWorkspaceEditParams ),
31
- CodeLens (CodeLens ),
32
- CodeLensParams (CodeLensParams , _textDocument ),
33
- Diagnostic (.. ), List (.. ),
34
- ResponseError , SMethod (.. ),
35
- TextDocumentIdentifier (TextDocumentIdentifier ),
36
- TextEdit (TextEdit ),
37
- WorkspaceEdit (WorkspaceEdit ))
38
- import Text.Regex.TDFA ((=~) )
11
+ import Data.Aeson.Types (Value (.. ), toJSON )
12
+ import Data.Generics (mkQ , something )
13
+ import qualified Data.HashMap.Strict as Map
14
+ import Data.List (find )
15
+ import qualified Data.Text as T
16
+ import Development.IDE.Core.Compile (TcModuleResult (.. ))
17
+ import Development.IDE.Core.RuleTypes (GetBindings (GetBindings ),
18
+ TypeCheck (TypeCheck ))
19
+ import Development.IDE.Core.Rules (IdeState , runAction )
20
+ import Development.IDE.Core.Service (getDiagnostics )
21
+ import Development.IDE.Core.Shake (getHiddenDiagnostics , use )
22
+ import Development.IDE.GHC.Compat
23
+ import Development.IDE.GHC.Util (printName )
24
+ import Development.IDE.Spans.Common (safeTyThingType )
25
+ import Development.IDE.Spans.LocalBindings (Bindings , getFuzzyScope )
26
+ import Development.IDE.Types.Location (Position (Position , _character , _line ),
27
+ Range (Range , _end , _start ),
28
+ toNormalizedFilePath' ,
29
+ uriToFilePath' )
30
+ import HscTypes (lookupTypeEnv ,
31
+ mkPrintUnqualified )
32
+ import Ide.PluginUtils (mkLspCommand )
33
+ import Ide.Types (CommandFunction ,
34
+ CommandId (CommandId ),
35
+ PluginCommand (PluginCommand ),
36
+ PluginDescriptor (.. ),
37
+ PluginId ,
38
+ defaultPluginDescriptor ,
39
+ mkPluginHandler )
40
+ import qualified Language.LSP.Server as LSP
41
+ import Language.LSP.Types (ApplyWorkspaceEditParams (ApplyWorkspaceEditParams ),
42
+ CodeLens (CodeLens ),
43
+ CodeLensParams (CodeLensParams , _textDocument ),
44
+ Diagnostic (.. ),
45
+ List (.. ), ResponseError ,
46
+ SMethod (.. ),
47
+ TextDocumentIdentifier (TextDocumentIdentifier ),
48
+ TextEdit (TextEdit ),
49
+ WorkspaceEdit (WorkspaceEdit ))
50
+ import Outputable (showSDocForUser )
51
+ import TcRnTypes (TcGblEnv (TcGblEnv , tcg_rdr_env , tcg_rn_decls , tcg_type_env ))
52
+ import TcType (pprSigmaType )
53
+ import Text.Regex.TDFA ((=~) )
39
54
40
55
typeLensCommandId :: T. Text
41
56
typeLensCommandId = " typesignature.add"
@@ -55,14 +70,15 @@ codeLensProvider ::
55
70
codeLensProvider ideState pId CodeLensParams {_textDocument = TextDocumentIdentifier uri} = do
56
71
fmap (Right . List ) $ case uriToFilePath' uri of
57
72
Just (toNormalizedFilePath' -> filePath) -> liftIO $ do
58
- _ <- runAction " codeLens" ideState (use TypeCheck filePath)
73
+ tmr <- runAction " codeLens.TypeCheck" ideState (use TypeCheck filePath)
74
+ bindings <- runAction " bindings.GetBindings" ideState (use GetBindings filePath)
59
75
diag <- getDiagnostics ideState
60
76
hDiag <- getHiddenDiagnostics ideState
61
77
sequence
62
78
[ generateLens pId _range title edit
63
79
| (dFile, _, dDiag@ Diagnostic {_range = _range}) <- diag ++ hDiag,
64
80
dFile == filePath,
65
- (title, tedit) <- suggestSignature False dDiag,
81
+ (title, tedit) <- suggestSignature False tmr bindings dDiag,
66
82
let edit = WorkspaceEdit (Just $ Map. singleton uri $ List tedit) Nothing
67
83
]
68
84
Nothing -> pure []
@@ -77,33 +93,37 @@ commandHandler _ideState wedit = do
77
93
_ <- LSP. sendRequest SWorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing wedit) (\ _ -> pure () )
78
94
return $ Right Null
79
95
80
- suggestSignature :: Bool -> Diagnostic -> [(T. Text , [TextEdit ])]
81
- suggestSignature isQuickFix Diagnostic {_range = _range@ Range {.. }, .. }
96
+ suggestSignature :: Bool -> Maybe TcModuleResult -> Maybe Bindings -> Diagnostic -> [(T. Text , [TextEdit ])]
97
+ suggestSignature isQuickFix mTmr mBindings Diagnostic {_message, _range = _range@ Range {.. }}
82
98
| _message
83
- =~ (" (Top-level binding|Polymorphic local binding|Pattern synonym) with no type signature" :: T. Text ) =
84
- let signature =
85
- removeInitialForAll $
86
- T. takeWhile (\ x -> x /= ' *' && x /= ' •' ) $
87
- T. strip $ unifySpaces $ last $ T. splitOn " type signature: " $ filterNewlines _message
88
- startOfLine = Position (_line _start) startCharacter
89
- beforeLine = Range startOfLine startOfLine
90
- title = if isQuickFix then " add signature: " <> signature else signature
91
- action = TextEdit beforeLine $ signature <> " \n " <> T. replicate startCharacter " "
92
- in [(title, [action])]
99
+ =~ (" (Top-level binding|Polymorphic local binding|Pattern synonym) with no type signature" :: T. Text ),
100
+ Just bindings <- mBindings,
101
+ Just TcModuleResult {tmrTypechecked = TcGblEnv {tcg_type_env, tcg_rn_decls, tcg_rdr_env}} <- mTmr,
102
+ localScope <- getFuzzyScope bindings _start _end,
103
+ Just group <- tcg_rn_decls,
104
+ Just name <- getFirstIdAtLine (succ $ _line _start) group,
105
+ Just ty <- (lookupTypeEnv tcg_type_env name >>= safeTyThingType) <|> (find (\ (x, _) -> x == name) localScope >>= snd ),
106
+ tyMsg <- showSDocForUser unsafeGlobalDynFlags (mkPrintUnqualified unsafeGlobalDynFlags tcg_rdr_env) $ pprSigmaType ty,
107
+ signature <- T. pack $ printName name <> " :: " <> tyMsg,
108
+ startOfLine <- Position (_line _start) startCharacter,
109
+ beforeLine <- Range startOfLine startOfLine,
110
+ title <- if isQuickFix then " add signature: " <> signature else signature,
111
+ action <- TextEdit beforeLine $ signature <> " \n " <> T. replicate startCharacter " " =
112
+ [(title, [action])]
113
+ | otherwise = []
93
114
where
94
- removeInitialForAll :: T. Text -> T. Text
95
- removeInitialForAll (T. breakOnEnd " :: " -> (nm, ty))
96
- | " forall" `T.isPrefixOf` ty = nm <> T. drop 2 (snd (T. breakOn " ." ty))
97
- | otherwise = nm <> ty
98
115
startCharacter
99
116
| " Polymorphic local binding" `T.isPrefixOf` _message =
100
117
_character _start
101
118
| otherwise =
102
119
0
103
- suggestSignature _ _ = []
104
120
105
- unifySpaces :: T. Text -> T. Text
106
- unifySpaces = T. unwords . T. words
107
-
108
- filterNewlines :: T. Text -> T. Text
109
- filterNewlines = T. concat . T. lines
121
+ getFirstIdAtLine :: Int -> HsGroup GhcRn -> Maybe Name
122
+ getFirstIdAtLine line = something (mkQ Nothing f)
123
+ where
124
+ f :: Located Name -> Maybe Name
125
+ f (L l name)
126
+ | RealSrcSpan s <- l,
127
+ srcSpanStartLine s == line =
128
+ Just name
129
+ | otherwise = Nothing
0 commit comments