Skip to content

Commit f68d6e3

Browse files
committed
Add lots of comments
1 parent 6e4bc8c commit f68d6e3

File tree

1 file changed

+64
-25
lines changed

1 file changed

+64
-25
lines changed

src/Ide/Plugin/ImportLens.hs

Lines changed: 64 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# LANGUAGE ViewPatterns #-}
12
{-# LANGUAGE DeriveAnyClass #-}
23
{-# LANGUAGE DeriveGeneric #-}
34
{-# LANGUAGE DerivingStrategies #-}
@@ -22,13 +23,11 @@ import qualified Data.Text as T
2223
import Development.IDE.Core.RuleTypes (GhcSessionDeps (GhcSessionDeps),
2324
TcModuleResult (tmrModule),
2425
TypeCheck (TypeCheck))
25-
import Development.IDE.Core.Service (IdeState, runAction)
2626
import Development.IDE.Core.Shake (IdeAction, IdeState (..),
27-
runIdeAction, useWithStaleFast,
28-
use_)
27+
runIdeAction, useWithStaleFast)
2928
import Development.IDE.GHC.Compat
3029
import Development.IDE.GHC.Error (realSpan, realSrcSpanToRange)
31-
import Development.IDE.GHC.Util (hscEnv, prettyPrint)
30+
import Development.IDE.GHC.Util (HscEnvEq, hscEnv, prettyPrint)
3231
import GHC.Generics (Generic)
3332
import Ide.Plugin
3433
import Ide.Types
@@ -42,46 +41,64 @@ import TcRnTypes (TcGblEnv (tcg_used_gres))
4241
importCommandId :: CommandId
4342
importCommandId = "ImportLensCommand"
4443

44+
-- | The "main" function of a plugin
4545
descriptor :: PluginId -> PluginDescriptor
4646
descriptor plId = (defaultPluginDescriptor plId) {
47+
-- This plugin provides code lenses
4748
pluginCodeLensProvider = Just provider,
49+
-- This plugin provides a command handler
4850
pluginCommands = [ importLensCommand ]
4951
}
5052

53+
-- | The command descriptor
5154
importLensCommand :: PluginCommand
5255
importLensCommand =
5356
PluginCommand importCommandId "Explicit import command" runImportCommand
5457

58+
-- | The type of the parameters accepted by our command
5559
data ImportCommandParams = ImportCommandParams WorkspaceEdit
5660
deriving Generic
5761
deriving anyclass (FromJSON, ToJSON)
5862

63+
-- | The actual command handler
5964
runImportCommand :: CommandFunction ImportCommandParams
6065
runImportCommand _lspFuncs _state (ImportCommandParams edit) = do
66+
-- This command simply triggers a workspace edit!
6167
return (Right Null, Just (WorkspaceApplyEdit, ApplyWorkspaceEditParams edit))
6268

63-
-- For every implicit import statement,
64-
-- return a code lens of the corresponding explicit import
65-
-- Example. For the module below:
69+
-- | For every implicit import statement, return a code lens of the corresponding explicit import
70+
-- Example - for the module below:
6671
--
6772
-- > import Data.List
6873
-- >
6974
-- > f = intercalate " " . sortBy length
7075
--
71-
-- the provider should produce one code lens:
76+
-- the provider should produce one code lens associated to the import statement:
7277
--
7378
-- > import Data.List (intercalate, sortBy)
74-
7579
provider :: CodeLensProvider
76-
provider _lspFuncs state pId CodeLensParams{..}
77-
| TextDocumentIdentifier{_uri} <- _textDocument
78-
, Just nfp <- uriToNormalizedFilePath $ toNormalizedUri _uri
80+
provider _lspFuncs -- LSP functions, not used
81+
state -- ghcide state, used to retrieve typechecking artifacts
82+
pId -- plugin Id
83+
CodeLensParams{_textDocument = TextDocumentIdentifier{_uri}}
84+
-- VSCode uses URIs instead of file paths
85+
-- haskell-lsp provides conversion functions
86+
| Just nfp <- uriToNormalizedFilePath $ toNormalizedUri _uri
7987
= do
80-
Just (tmr, _) <- runIde state $ useWithStaleFast TypeCheck nfp
81-
hsc <- hscEnv <$> runAction "importLens" state (use_ GhcSessionDeps nfp)
82-
(imports, mbMinImports) <- extractMinimalImports hsc (tmrModule tmr)
88+
-- Get the typechecking artifacts from the module, even if they are stale.
89+
-- This is for responsiveness - we don't want our code lenses to vanish
90+
-- just because there is a type error unrelated to the moduel imports.
91+
-- However, if the user edits the imports while the module does not typecheck,
92+
-- our code lenses will get out of sync
93+
tmr <- runIde state $ useWithStaleFast TypeCheck nfp
94+
-- We also need a GHC session with all the dependencies
95+
hsc <- runIde state $ useWithStaleFast GhcSessionDeps nfp
96+
-- Use the GHC api to extract the "minimal" imports
97+
(imports, mbMinImports) <- extractMinimalImports hsc tmr
8398

8499
case mbMinImports of
100+
-- Implement the provider logic:
101+
-- for every import, if it's lacking a explicit list, generate a code lens
85102
Just minImports -> do
86103
let minImportsMap =
87104
Map.fromList [ (srcSpanStart l, i) | L l i <- minImports ]
@@ -93,41 +110,63 @@ provider _lspFuncs state pId CodeLensParams{..}
93110
| otherwise
94111
= return $ Right (List [])
95112

96-
extractMinimalImports :: HscEnv -> TypecheckedModule -> IO ([LImportDecl GhcRn], Maybe [LImportDecl GhcRn])
97-
extractMinimalImports hsc TypecheckedModule{..} = do
113+
-- | Use the ghc api to extract a minimal, explicit set of imports for this module
114+
extractMinimalImports
115+
:: Maybe (HscEnvEq, a)
116+
-> Maybe (TcModuleResult, b)
117+
-> IO ([LImportDecl GhcRn], Maybe [LImportDecl GhcRn])
118+
extractMinimalImports (Just (hsc, _)) (Just (tmrModule -> TypecheckedModule{..}, _)) = do
119+
-- extract the original imports and the typechecking environment
98120
let (tcEnv,_) = tm_internals_
99121
Just (_, imports, _, _) = tm_renamed_source
100122
ParsedModule{ pm_parsed_source = L loc _} = tm_parsed_module
123+
span = fromMaybe (error "expected real") $ realSpan loc
101124

125+
-- GHC is secretly full of mutable state
102126
gblElts <- readIORef (tcg_used_gres tcEnv)
127+
128+
-- call findImportUsage does exactly what we need
129+
-- GHC is full of treats like this
103130
let usage = findImportUsage imports gblElts
104-
span = fromMaybe (error "expected real") $ realSpan loc
105-
(_, minimalImports) <- initTcWithGbl hsc tcEnv span $ getMinimalImports usage
131+
(_, minimalImports) <- initTcWithGbl (hscEnv hsc) tcEnv span $ getMinimalImports usage
132+
133+
-- return both the original imports and the computed minimal ones
106134
return (imports, minimalImports)
107135

136+
extractMinimalImports _ _ = return ([], Nothing)
137+
138+
-- | Given an import declaration, generate a code lens unless it has an explicit import list
108139
generateLens :: PluginId -> Uri -> Map SrcLoc (ImportDecl GhcRn) -> LImportDecl GhcRn -> IO (Maybe CodeLens)
109140
generateLens pId uri minImports (L src imp)
141+
-- Explicit import list case
110142
| ImportDecl{ideclHiding = Just (False,_)} <- imp
111143
= return Nothing
144+
-- No explicit import list
112145
| RealSrcSpan l <- src
113146
, Just explicit <- Map.lookup (srcSpanStart src) minImports
114147
, L _ mn <- ideclName imp
148+
-- (almost) no one wants to see an explicit import list for Prelude
115149
, mn /= moduleName pRELUDE
116150
= do
151+
-- The title of the command is just the minimal explicit import decl
117152
let title = T.pack $ prettyPrint explicit
118-
commandArgs = Nothing
119-
c <- mkLspCommand pId importCommandId title commandArgs
120-
let _range :: Range = realSrcSpanToRange l
153+
-- the range of the code lens is the span of the original import decl
154+
_range :: Range = realSrcSpanToRange l
155+
-- the code lens has no extra data
121156
_xdata = Nothing
157+
-- an edit that replaces the whole declaration with the explicit one
122158
edit = WorkspaceEdit (Just editsMap) Nothing
123159
editsMap = HashMap.fromList [(uri, List [importEdit])]
124160
importEdit = TextEdit _range title
125-
args = ImportCommandParams edit
126-
_arguments = Just (List [toJSON args])
127-
_command = Just (c :: Command){_arguments}
161+
-- the command argument is simply the edit
162+
_arguments = Just [toJSON $ ImportCommandParams edit]
163+
-- create the command
164+
_command <- Just <$> mkLspCommand pId importCommandId title _arguments
165+
-- create and return the code lens
128166
return $ Just CodeLens{..}
129167
| otherwise
130168
= return Nothing
131169

170+
-- | A helper to run ide actions
132171
runIde :: IdeState -> IdeAction a -> IO a
133172
runIde state = runIdeAction "importLens" (shakeExtras state)

0 commit comments

Comments
 (0)