8
8
-- | Go to the definition of a variable.
9
9
module Development.IDE.Plugin.CodeAction (plugin ) where
10
10
11
- import Avail (AvailInfo (Avail ), AvailInfo (AvailTC ), availNames )
12
11
import Language.Haskell.LSP.Types
13
12
import Control.Monad (join )
14
13
import Development.IDE.Plugin
@@ -21,8 +20,11 @@ import Development.IDE.GHC.Error
21
20
import Development.IDE.GHC.Util
22
21
import Development.IDE.LSP.Server
23
22
import Development.IDE.Plugin.CodeAction.PositionIndexed
23
+ import Development.IDE.Plugin.CodeAction.RuleTypes
24
+ import Development.IDE.Plugin.CodeAction.Rules
24
25
import Development.IDE.Types.Location
25
26
import Development.IDE.Types.Options
27
+ import Development.Shake (Rules )
26
28
import qualified Data.HashMap.Strict as Map
27
29
import qualified Language.Haskell.LSP.Core as LSP
28
30
import Language.Haskell.LSP.VFS
@@ -36,20 +38,18 @@ import Data.List.Extra
36
38
import qualified Data.Text as T
37
39
import Data.Tuple.Extra ((&&&) )
38
40
import HscTypes
39
- import OccName
40
41
import Parser
41
- import RdrName
42
42
import Text.Regex.TDFA ((=~) , (=~~) )
43
43
import Text.Regex.TDFA.Text ()
44
44
import Outputable (ppr , showSDocUnsafe )
45
45
import DynFlags (xFlags , FlagSpec (.. ))
46
46
import GHC.LanguageExtensions.Type (Extension )
47
- import Data.IORef (readIORef )
48
- import Name (isDataConName , nameModule_maybe , nameOccName )
49
- import Packages (exposedModules , lookupPackage )
50
47
51
48
plugin :: Plugin c
52
- plugin = codeActionPlugin codeAction <> Plugin mempty setHandlersCodeLens
49
+ plugin = codeActionPluginWithRules rules codeAction <> Plugin mempty setHandlersCodeLens
50
+
51
+ rules :: Rules ()
52
+ rules = rulePackageExports
53
53
54
54
-- | Generate code actions.
55
55
codeAction
@@ -65,15 +65,15 @@ codeAction lsp state (TextDocumentIdentifier uri) _range CodeActionContext{_diag
65
65
contents <- LSP. getVirtualFileFunc lsp $ toNormalizedUri uri
66
66
let text = Rope. toText . (_text :: VirtualFile -> Rope. Rope ) <$> contents
67
67
mbFile = toNormalizedFilePath <$> uriToFilePath uri
68
- (ideOptions, parsedModule, env) <- runAction state $
68
+ (ideOptions, parsedModule, join -> env) <- runAction state $
69
69
(,,) <$> getIdeOptions
70
- <*> getParsedModule `traverse` mbFile
71
- <*> use_ GhcSession `traverse` mbFile
70
+ <*> getParsedModule `traverse` mbFile
71
+ <*> use GhcSession `traverse` mbFile
72
+ pkgExports <- runAction state $ (useNoFile_ . PackageExports ) `traverse` env
72
73
let dflags = hsc_dflags . hscEnv <$> env
73
- eps <- traverse readIORef (hsc_EPS . hscEnv <$> env)
74
74
pure $ Right
75
75
[ CACodeAction $ CodeAction title (Just CodeActionQuickFix ) (Just $ List [x]) (Just edit) Nothing
76
- | x <- xs, (title, tedit) <- suggestAction dflags eps ideOptions ( join parsedModule ) text x
76
+ | x <- xs, (title, tedit) <- suggestAction dflags (fromMaybe mempty pkgExports) ideOptions ( join parsedModule ) text x
77
77
, let edit = WorkspaceEdit (Just $ Map. singleton uri $ List tedit) Nothing
78
78
]
79
79
@@ -114,13 +114,13 @@ executeAddSignatureCommand _lsp _ideState ExecuteCommandParams{..}
114
114
115
115
suggestAction
116
116
:: Maybe DynFlags
117
- -> Maybe ExternalPackageState
117
+ -> PackageExportsMap
118
118
-> IdeOptions
119
119
-> Maybe ParsedModule
120
120
-> Maybe T. Text
121
121
-> Diagnostic
122
122
-> [(T. Text , [TextEdit ])]
123
- suggestAction dflags eps ideOptions parsedModule text diag = concat
123
+ suggestAction dflags packageExports ideOptions parsedModule text diag = concat
124
124
[ suggestAddExtension diag
125
125
, suggestExtendImport dflags text diag
126
126
, suggestFillHole diag
@@ -132,7 +132,7 @@ suggestAction dflags eps ideOptions parsedModule text diag = concat
132
132
] ++ concat
133
133
[ suggestNewDefinition ideOptions pm text diag
134
134
++ suggestRemoveRedundantImport pm text diag
135
- ++ concat [ suggestNewImport dflags eps pm diag | Just eps <- [eps], Just dflags <- [dflags]]
135
+ ++ suggestNewImport packageExports pm diag
136
136
| Just pm <- [parsedModule]]
137
137
138
138
@@ -304,15 +304,10 @@ suggestExtendImport (Just dflags) contents Diagnostic{_range=_range,..}
304
304
_ -> error " bug in srcspan parser"
305
305
importLine = textInRange range c
306
306
in [(" Add " <> binding <> " to the import list of " <> mod
307
- , [TextEdit range (addBindingToImportList (printRdrName name) importLine)])]
307
+ , [TextEdit range (addBindingToImportList (T. pack $ printRdrName name) importLine)])]
308
308
| otherwise = []
309
309
suggestExtendImport Nothing _ _ = []
310
310
311
- printRdrName :: RdrName -> T. Text
312
- printRdrName name = T. pack $ showSDocUnsafe $ parenSymOcc rn (ppr rn)
313
- where
314
- rn = rdrNameOcc name
315
-
316
311
suggestFixConstructorImport :: Maybe T. Text -> Diagnostic -> [(T. Text , [TextEdit ])]
317
312
suggestFixConstructorImport _ Diagnostic {_range= _range,.. }
318
313
-- ‘Success’ is a data constructor of ‘Result’
@@ -353,8 +348,8 @@ suggestSignature _ _ = []
353
348
354
349
-------------------------------------------------------------------------------------------------
355
350
356
- suggestNewImport :: DynFlags -> ExternalPackageState -> ParsedModule -> Diagnostic -> [(T. Text , [TextEdit ])]
357
- suggestNewImport dflags eps ParsedModule {pm_parsed_source = L _ HsModule {.. }} Diagnostic {_message}
351
+ suggestNewImport :: PackageExportsMap -> ParsedModule -> Diagnostic -> [(T. Text , [TextEdit ])]
352
+ suggestNewImport packageExportsMap ParsedModule {pm_parsed_source = L _ HsModule {.. }} Diagnostic {_message}
358
353
| msg <- unifySpaces _message
359
354
, Just name <- extractNotInScopeName msg
360
355
, Just insertLine <- case hsmodImports of
@@ -365,52 +360,36 @@ suggestNewImport dflags eps ParsedModule {pm_parsed_source = L _ HsModule {..}}
365
360
RealSrcLoc s -> Just $ srcLocLine s
366
361
_ -> Nothing
367
362
, insertPos <- Position insertLine 0
368
- , extendImportSuggestions <- -- Just [binding, mod, srcspan] <-
369
- matchRegex msg
363
+ , extendImportSuggestions <- matchRegex msg
370
364
" Perhaps you want to add ‘[^’]*’ to the import list in the import of ‘([^’]*)’"
371
365
= [(imp, [TextEdit (Range insertPos insertPos) (imp <> " \n " )])
372
- | imp <- constructNewImportSuggestions dflags eps name extendImportSuggestions
366
+ | imp <- constructNewImportSuggestions packageExportsMap name extendImportSuggestions
373
367
]
374
- suggestNewImport _ _ _ _ = []
375
-
376
- constructNewImportSuggestions :: DynFlags -> ExternalPackageState -> NotInScope -> Maybe [T. Text ] -> [T. Text ]
377
- constructNewImportSuggestions dflags eps thingMissing notTheseModules = nubOrd
378
- [ case qual of
379
- Nothing -> " import " <> modName <> " (" <> importWhat candidate avail <> " )"
380
- Just q -> " import qualified " <> modName <> " as " <> q
381
- | item <- items,
382
- avail <- tyThingAvailInfo item,
383
- canUseAvail thingMissing avail,
384
- candidate <- availNames avail,
385
- canUseName thingMissing candidate,
386
- occNameString (nameOccName candidate) == T. unpack name,
387
- Just m <- [nameModule_maybe candidate],
388
- Just package <- [lookupPackage dflags (moduleUnitId m)],
389
- moduleName m `elem` map fst (exposedModules package),
390
- let modName = T. pack $ moduleNameString $ moduleName m,
391
- modName `notElem` fromMaybe [] notTheseModules
392
- ]
393
- where
394
- (qual, name) = case T. splitOn " ." (notInScope thingMissing) of
395
- [n] -> (Nothing , n)
396
- segments -> (Just (T. concat $ init segments), last segments)
397
- items = typeEnvElts $ eps_PTE eps
398
- importWhat this (AvailTC parent _ _)
399
- -- "Maybe(Just)"
400
- | this /= parent
401
- = T. pack (occNameString (nameOccName parent)) <>
402
- " (" <> printName this <> " )"
403
- importWhat this _ = printName this
404
-
405
- printName = printRdrName . nameRdrName
406
-
407
- canUseAvail :: NotInScope -> AvailInfo -> Bool
408
- canUseAvail NotInScopeDataConstructor {} Avail {} = False
409
- canUseAvail _ _ = True
410
-
411
- canUseName :: NotInScope -> Name -> Bool
412
- canUseName NotInScopeDataConstructor {} = isDataConName
413
- canUseName _ = const True
368
+ suggestNewImport _ _ _ = []
369
+
370
+ constructNewImportSuggestions
371
+ :: PackageExportsMap -> NotInScope -> Maybe [T. Text ] -> [T. Text ]
372
+ constructNewImportSuggestions exportsMap thingMissing notTheseModules = nubOrd
373
+ [ renderNewImport identInfo m
374
+ | (identInfo, m) <- fromMaybe [] $ Map. lookup name exportsMap
375
+ , canUseIdent thingMissing identInfo
376
+ , m `notElem` fromMaybe [] notTheseModules
377
+ ]
378
+ where
379
+ renderNewImport identInfo m
380
+ | Just q <- qual = " import qualified " <> m <> " as " <> q
381
+ | otherwise = " import " <> m <> " (" <> importWhat identInfo <> " )"
382
+
383
+ (qual, name) = case T. splitOn " ." (notInScope thingMissing) of
384
+ [n] -> (Nothing , n)
385
+ segments -> (Just (T. concat $ init segments), last segments)
386
+ importWhat IdentInfo {parent, rendered}
387
+ | Just p <- parent = p <> " (" <> rendered <> " )"
388
+ | otherwise = rendered
389
+
390
+ canUseIdent :: NotInScope -> IdentInfo -> Bool
391
+ canUseIdent NotInScopeDataConstructor {} = isDatacon
392
+ canUseIdent _ = const True
414
393
415
394
data NotInScope
416
395
= NotInScopeDataConstructor T. Text
@@ -427,6 +406,8 @@ extractNotInScopeName :: T.Text -> Maybe NotInScope
427
406
extractNotInScopeName x
428
407
| Just [name] <- matchRegex x " Data constructor not in scope: ([^ ]+)"
429
408
= Just $ NotInScopeDataConstructor name
409
+ | Just [name] <- matchRegex x " Not in scope: data constructor [^‘]*‘([^’]*)’"
410
+ = Just $ NotInScopeDataConstructor name
430
411
| Just [name] <- matchRegex x " ot in scope: type constructor or class [^‘]*‘([^’]*)’"
431
412
= Just $ NotInScopeTypeConstructorOrClass name
432
413
| Just [name] <- matchRegex x " ot in scope: ([^‘ ]+)"
0 commit comments