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