@@ -12,147 +12,220 @@ Provide CodeLenses to:
12
12
* Fix the module name if incorrect
13
13
-}
14
14
module Ide.Plugin.ModuleName
15
-
16
15
( descriptor
17
16
)
18
17
where
19
18
20
- import Control.Monad (join )
21
- import Control.Monad.IO.Class (MonadIO (liftIO ))
22
- import Control.Monad.Trans.Maybe ()
23
- import Data.Aeson (ToJSON (toJSON ), Value (Null ))
24
- import qualified Data.HashMap.Strict as Map
25
- import Data.List (isPrefixOf )
26
- import Data.List.Extra (replace )
27
- import Data.Maybe (listToMaybe )
28
- import Data.String (IsString )
29
- import Data.Text (Text )
30
- import qualified Data.Text as T
31
- import Development.IDE (hscEnvWithImportPaths , GetParsedModule (GetParsedModule ),
32
- GhcSession (GhcSession ),
33
- HscEnvEq , IdeState ,
34
- List (.. ), NormalizedFilePath ,
35
- Position (Position ), Range (Range ),
36
- evalGhcEnv , realSrcSpanToRange ,
37
- runAction , toNormalizedUri ,
38
- uriToFilePath' , use , use_ )
39
- import Development.IDE.Plugin (getPid )
40
- import GHC (DynFlags (importPaths ),
41
- GenLocated (L ),
42
- HsModule (hsmodName ),
43
- ParsedModule (pm_parsed_source ),
44
- SrcSpan (RealSrcSpan ), unLoc ,getSessionDynFlags )
45
- import Ide.Types (CommandFunction , CommandId (.. ),
46
- PluginCommand (.. ),
47
- PluginDescriptor (.. ),
48
- PluginId (.. ),
49
- defaultPluginDescriptor )
50
- import Language.Haskell.LSP.Core (LspFuncs , getVirtualFileFunc )
51
- import Language.Haskell.LSP.Types (ApplyWorkspaceEditParams (.. ),
52
- CAResult (CACodeAction ),
53
- CodeAction (CodeAction ),
54
- CodeActionKind (CodeActionQuickFix ),
55
- CodeLens (CodeLens ),
56
- CodeLensParams (CodeLensParams ),
57
- Command (Command ),
58
- ServerMethod (.. ),
59
- TextDocumentIdentifier (TextDocumentIdentifier ),
60
- TextEdit (TextEdit ), Uri ,
61
- WorkspaceEdit (.. ),
62
- uriToNormalizedFilePath )
63
- import Language.Haskell.LSP.VFS (virtualFileText )
64
- import System.FilePath (dropExtension )
19
+ import Control.Monad ( join )
20
+ import Control.Monad.IO.Class ( MonadIO (liftIO ) )
21
+ import Control.Monad.Trans.Maybe ( )
22
+ import Data.Aeson ( ToJSON (toJSON )
23
+ , Value (Null )
24
+ )
25
+ import qualified Data.HashMap.Strict as Map
26
+ import Data.List ( isPrefixOf )
27
+ import Data.List.Extra ( replace )
28
+ import Data.Maybe ( listToMaybe )
29
+ import Data.String ( IsString )
30
+ import Data.Text ( Text )
31
+ import qualified Data.Text as T
32
+ import Development.IDE ( hscEnvWithImportPaths
33
+ , GetParsedModule
34
+ ( GetParsedModule
35
+ )
36
+ , GhcSession (GhcSession )
37
+ , HscEnvEq
38
+ , IdeState
39
+ , List (.. )
40
+ , NormalizedFilePath
41
+ , Position (Position )
42
+ , Range (Range )
43
+ , evalGhcEnv
44
+ , realSrcSpanToRange
45
+ , runAction
46
+ , toNormalizedUri
47
+ , uriToFilePath'
48
+ , use
49
+ , use_
50
+ )
51
+ import Development.IDE.Plugin ( getPid )
52
+ import GHC ( DynFlags (importPaths )
53
+ , GenLocated (L )
54
+ , HsModule (hsmodName )
55
+ , ParsedModule (pm_parsed_source )
56
+ , SrcSpan (RealSrcSpan )
57
+ , unLoc
58
+ , getSessionDynFlags
59
+ )
60
+ import Ide.Types ( CommandFunction
61
+ , PluginCommand (.. )
62
+ , PluginDescriptor (.. )
63
+ , PluginId (.. )
64
+ , defaultPluginDescriptor
65
+ )
66
+ import Language.Haskell.LSP.Core ( LspFuncs
67
+ , getVirtualFileFunc
68
+ )
69
+ import Language.Haskell.LSP.Types ( ApplyWorkspaceEditParams (.. )
70
+ , CAResult (CACodeAction )
71
+ , CodeAction (CodeAction )
72
+ , CodeActionKind
73
+ ( CodeActionQuickFix
74
+ )
75
+ , CodeLens (CodeLens )
76
+ , CodeLensParams (CodeLensParams )
77
+ , Command (Command )
78
+ , ServerMethod (.. )
79
+ , TextDocumentIdentifier
80
+ ( TextDocumentIdentifier
81
+ )
82
+ , TextEdit (TextEdit )
83
+ , Uri
84
+ , WorkspaceEdit (.. )
85
+ , uriToNormalizedFilePath
86
+ )
87
+ import Language.Haskell.LSP.VFS ( virtualFileText )
88
+ import System.FilePath ( dropExtension )
89
+ import Ide.Plugin ( mkLspCmdId )
65
90
66
91
-- | Plugin descriptor
67
92
descriptor :: PluginId -> PluginDescriptor
68
- descriptor plId =
69
- (defaultPluginDescriptor plId)
70
- { pluginId = plId,
71
- pluginCodeLensProvider = Just codeLens
72
- ,pluginCommands = [PluginCommand editCommandName editCommandName editCmd]
93
+ descriptor plId = (defaultPluginDescriptor plId)
94
+ { pluginId = plId
95
+ , pluginCodeLensProvider = Just codeLens
96
+ , pluginCommands = [PluginCommand editCommandName editCommandName editCmd]
73
97
-- pluginCodeActionProvider = Just codeAction
74
- }
98
+ }
75
99
76
100
-- | Generate code lenses
77
- codeLens :: LspFuncs c -> IdeState -> PluginId -> CodeLensParams -> IO (Either a2 (List CodeLens ))
78
- codeLens lsp state pluginId (CodeLensParams (TextDocumentIdentifier uri) _) = do
79
- pid <- getPid
80
- actions (asCodeLens (mkLspCmdId pid pluginId editCommandName)) lsp state uri
101
+ codeLens
102
+ :: LspFuncs c
103
+ -> IdeState
104
+ -> PluginId
105
+ -> CodeLensParams
106
+ -> IO (Either a2 (List CodeLens ))
107
+ codeLens lsp state pluginId (CodeLensParams (TextDocumentIdentifier uri) _) =
108
+ do
109
+ pid <- getPid
110
+ actions (asCodeLens (mkLspCmdId pid pluginId editCommandName)) lsp state uri
81
111
82
112
-- | Generate code actions.
83
113
-- NOTE: Not invoked on an empty module (but codeLens is, why?)
84
- codeAction :: LspFuncs c -> IdeState -> p1 -> TextDocumentIdentifier -> p2 -> p3 -> IO (Either a (List CAResult ))
85
- codeAction lsp state _plId (TextDocumentIdentifier uri) _range _ = actions asCodeAction lsp state uri
86
-
87
- -- Copied from "Ide.Plugin"
88
- mkLspCmdId :: T. Text -> PluginId -> CommandId -> T. Text
89
- mkLspCmdId pid (PluginId plid) (CommandId cid)
90
- = pid <> " :" <> plid <> " :" <> cid
114
+ codeAction
115
+ :: LspFuncs c
116
+ -> IdeState
117
+ -> p1
118
+ -> TextDocumentIdentifier
119
+ -> p2
120
+ -> p3
121
+ -> IO (Either a (List CAResult ))
122
+ codeAction lsp state _plId (TextDocumentIdentifier uri) _range _ =
123
+ actions asCodeAction lsp state uri
91
124
92
125
editCommandName :: IsString p => p
93
126
editCommandName = " edit"
94
127
95
128
-- | Generic command to apply a group of edits
96
129
editCmd :: CommandFunction WorkspaceEdit
97
- editCmd _lf _ide workspaceEdits = return (Right Null , Just $ (WorkspaceApplyEdit ,ApplyWorkspaceEditParams workspaceEdits))
130
+ editCmd _lf _ide workspaceEdits = return
131
+ ( Right Null
132
+ , Just $ (WorkspaceApplyEdit , ApplyWorkspaceEditParams workspaceEdits)
133
+ )
98
134
99
135
-- | Required actions (actually, at most one) that can be converted to either CodeLenses or CodeActions
100
- actions :: Show a1 => (Action -> a1 ) -> LspFuncs c -> IdeState -> Uri -> IO (Either a2 (List a1 ))
136
+ actions
137
+ :: Show a1
138
+ => (Action -> a1 )
139
+ -> LspFuncs c
140
+ -> IdeState
141
+ -> Uri
142
+ -> IO (Either a2 (List a1 ))
101
143
actions convert lsp state uri = do
102
- let Just nfp = uriToNormalizedFilePath $ toNormalizedUri uri
103
- let Just fp = uriToFilePath' uri
104
-
105
- contents <- liftIO $ getVirtualFileFunc lsp $ toNormalizedUri uri
106
- let emptyModule = maybe True ((== 0 ) . T. length . T. strip . virtualFileText) contents
107
-
108
- correctNameMaybe <- pathModuleName state nfp fp
109
- statedNameMaybe <- codeModuleName state nfp
110
-
111
- let act = Action uri
112
- let actions = case (correctNameMaybe,statedNameMaybe) of
113
- (Just correctName,Just (nameRange,statedName)) | correctName /= statedName -> [convert $ act nameRange (" Set module name to " <> correctName) correctName]
114
- (Just correctName,_) | emptyModule -> let code = T. unwords [" module" ,correctName," where\n " ] in [convert $ act (Range (Position 0 0 ) (Position 0 0 )) code code]
115
- _ -> []
116
-
117
- out [" actions" ,show actions]
118
- pure . Right . List $ actions
144
+ let Just nfp = uriToNormalizedFilePath $ toNormalizedUri uri
145
+ let Just fp = uriToFilePath' uri
146
+ out [" actions[" , fp]
147
+
148
+ contents <- liftIO $ getVirtualFileFunc lsp $ toNormalizedUri uri
149
+ let emptyModule =
150
+ maybe True ((== 0 ) . T. length . T. strip . virtualFileText) contents
151
+
152
+ correctNameMaybe <- pathModuleName state nfp fp
153
+ statedNameMaybe <- codeModuleName state nfp
154
+ out [" correct" , show correctNameMaybe, " stated" , show statedNameMaybe]
155
+
156
+ let act = Action uri
157
+ let
158
+ actions = case (correctNameMaybe, statedNameMaybe) of
159
+ (Just correctName, Just (nameRange, statedName))
160
+ | correctName /= statedName
161
+ -> [ convert $ act nameRange
162
+ (" Set module name to " <> correctName)
163
+ correctName
164
+ ]
165
+ (Just correctName, _) | emptyModule ->
166
+ let code = T. unwords [" module" , correctName, " where\n " ]
167
+ in [convert $ act (Range (Position 0 0 ) (Position 0 0 )) code code]
168
+ _ -> []
169
+
170
+ out [" actions" , show actions]
171
+ pure . Right . List $ actions
119
172
120
173
-- | The module name, as derived by the position of the module in its source directory
121
174
pathModuleName :: IdeState -> NormalizedFilePath -> String -> IO (Maybe Text )
122
- pathModuleName state nfp fp = do
123
- session :: HscEnvEq <- runAction " ModuleName.ghcSession" state $ use_ GhcSession nfp
124
-
125
- paths <- evalGhcEnv (hscEnvWithImportPaths session) $ importPaths <$> getSessionDynFlags
126
- out [" import paths" ,show paths]
127
-
128
- let maybePrefix = listToMaybe . filter (`isPrefixOf` fp) $ paths
129
- out [" prefix" ,show maybePrefix]
130
- let maybeMdlName = (\ prefix -> replace " /" " ." . drop (length prefix+ 1 ) $ dropExtension fp) <$> maybePrefix
131
- out [" mdlName" ,show maybeMdlName]
132
- return $ T. pack <$> maybeMdlName
175
+ pathModuleName state nfp fp = do
176
+ session :: HscEnvEq <- runAction " ModuleName.ghcSession" state
177
+ $ use_ GhcSession nfp
178
+
179
+ paths <-
180
+ evalGhcEnv (hscEnvWithImportPaths session)
181
+ $ importPaths
182
+ <$> getSessionDynFlags
183
+ out [" import paths" , show paths]
184
+
185
+ let maybePrefix = listToMaybe . filter (`isPrefixOf` fp) $ paths
186
+ out [" prefix" , show maybePrefix]
187
+ let maybeMdlName =
188
+ (\ prefix ->
189
+ replace " /" " ." . drop (length prefix + 1 ) $ dropExtension fp
190
+ )
191
+ <$> maybePrefix
192
+ out [" mdlName" , show maybeMdlName]
193
+ return $ T. pack <$> maybeMdlName
133
194
134
195
-- | The module name, as stated in the module
135
196
codeModuleName :: IdeState -> NormalizedFilePath -> IO (Maybe (Range , Text ))
136
- codeModuleName state nfp = ((\ (L (RealSrcSpan l) m) -> (realSrcSpanToRange l,T. pack . show $ m)) <$> ) . join . (hsmodName . unLoc . pm_parsed_source <$> ) <$> runAction " ModuleName.GetParsedModule" state (use GetParsedModule nfp)
197
+ codeModuleName state nfp =
198
+ ((\ (L (RealSrcSpan l) m) -> (realSrcSpanToRange l, T. pack . show $ m)) <$> )
199
+ . join
200
+ . (hsmodName . unLoc . pm_parsed_source <$> )
201
+ <$> runAction " ModuleName.GetParsedModule" state (use GetParsedModule nfp)
137
202
138
203
-- | A source code change
139
204
data Action = Action { aUri :: Uri ,aRange :: Range ,aTitle :: Text ,aCode :: Text } deriving Show
140
205
141
206
-- | Convert an Action to a CodeLens
142
207
asCodeLens :: Text -> Action -> CodeLens
143
- asCodeLens cid act@ Action {.. } = CodeLens aRange (Just $ Command aTitle cid (Just (List [toJSON $ asEdit act]))) Nothing
208
+ asCodeLens cid act@ Action {.. } = CodeLens
209
+ aRange
210
+ (Just $ Command aTitle cid (Just (List [toJSON $ asEdit act])))
211
+ Nothing
144
212
145
213
-- | Convert an Action to a CodeAction
146
214
asCodeAction :: Action -> CAResult
147
- asCodeAction act@ Action {.. } = CACodeAction $ CodeAction aTitle (Just CodeActionQuickFix ) (Just $ List [] ) (Just $ asEdit act) Nothing
148
- -- -- [TextDocumentEdit (VersionedTextDocumentIdentifier testUri (Just 0)) expectedTextEdits]
215
+ asCodeAction act@ Action {.. } = CACodeAction $ CodeAction
216
+ aTitle
217
+ (Just CodeActionQuickFix )
218
+ (Just $ List [] )
219
+ (Just $ asEdit act)
220
+ Nothing
149
221
150
222
asEdit :: Action -> WorkspaceEdit
151
- asEdit act@ Action {.. } = WorkspaceEdit (Just $ Map. singleton aUri $ List (asTextEdits act) ) Nothing
223
+ asEdit act@ Action {.. } =
224
+ WorkspaceEdit (Just $ Map. singleton aUri $ List (asTextEdits act)) Nothing
152
225
153
226
asTextEdits :: Action -> [TextEdit ]
154
- asTextEdits Action {.. } = [TextEdit aRange aCode]
227
+ asTextEdits Action {.. } = [TextEdit aRange aCode]
155
228
156
229
out :: [String ] -> IO ()
157
- out = print . unwords . (" Plugin ModuleName " : )
158
- -- out _ = return ()
230
+ -- out = print . unwords . ("Plugin ModuleName " :)
231
+ out _ = return ()
0 commit comments