diff --git a/ghcide/ghcide.cabal b/ghcide/ghcide.cabal index 7d61bc7795..476cdecdac 100644 --- a/ghcide/ghcide.cabal +++ b/ghcide/ghcide.cabal @@ -114,6 +114,8 @@ library BangPatterns DeriveFunctor DeriveGeneric + DeriveFoldable + DeriveTraversable FlexibleContexts GeneralizedNewtypeDeriving LambdaCase @@ -149,6 +151,7 @@ library Development.IDE.Core.Service Development.IDE.Core.Shake Development.IDE.Core.Tracing + Development.IDE.Core.UseStale Development.IDE.GHC.Compat Development.IDE.Core.Compile Development.IDE.GHC.Error diff --git a/ghcide/src/Development/IDE/Core/PositionMapping.hs b/ghcide/src/Development/IDE/Core/PositionMapping.hs index a9bd4aae7f..4048908b7c 100644 --- a/ghcide/src/Development/IDE/Core/PositionMapping.hs +++ b/ghcide/src/Development/IDE/Core/PositionMapping.hs @@ -11,6 +11,7 @@ module Development.IDE.Core.PositionMapping , PositionDelta(..) , addDelta , idDelta + , composeDelta , mkDelta , toCurrentRange , fromCurrentRange diff --git a/ghcide/src/Development/IDE/Core/Shake.hs b/ghcide/src/Development/IDE/Core/Shake.hs index 4965cb8b82..68cc99c553 100644 --- a/ghcide/src/Development/IDE/Core/Shake.hs +++ b/ghcide/src/Development/IDE/Core/Shake.hs @@ -838,12 +838,14 @@ usesWithStale_ key files = do Nothing -> liftIO $ throwIO $ BadDependency (show key) Just v -> return v -newtype IdeAction a = IdeAction { runIdeActionT :: (ReaderT ShakeExtras IO) a } - deriving newtype (MonadReader ShakeExtras, MonadIO, Functor, Applicative, Monad) - -- | IdeActions are used when we want to return a result immediately, even if it -- is stale Useful for UI actions like hover, completion where we don't want to -- block. +-- +-- Run via 'runIdeAction'. +newtype IdeAction a = IdeAction { runIdeActionT :: (ReaderT ShakeExtras IO) a } + deriving newtype (MonadReader ShakeExtras, MonadIO, Functor, Applicative, Monad) + runIdeAction :: String -> ShakeExtras -> IdeAction a -> IO a runIdeAction _herald s i = runReaderT (runIdeActionT i) s diff --git a/ghcide/src/Development/IDE/Core/UseStale.hs b/ghcide/src/Development/IDE/Core/UseStale.hs new file mode 100644 index 0000000000..04c1755817 --- /dev/null +++ b/ghcide/src/Development/IDE/Core/UseStale.hs @@ -0,0 +1,153 @@ +{-# LANGUAGE DerivingVia #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE RankNTypes #-} + +module Development.IDE.Core.UseStale + ( Age(..) + , Tracked + , unTrack + , PositionMap + , TrackedStale (..) + , unsafeMkStale + , unsafeMkCurrent + , unsafeCopyAge + , MapAge (..) + , dualPositionMap + , useWithStale + , useWithStale_ + ) where + +import Control.Arrow +import Control.Category (Category) +import qualified Control.Category as C +import Control.DeepSeq (NFData) +import Data.Aeson +import Data.Coerce (coerce) +import Data.Functor ((<&>)) +import Data.Functor.Identity (Identity(Identity)) +import Data.Kind (Type) +import Data.String (fromString) +import Development.IDE (NormalizedFilePath, IdeRule, Action, Range, rangeToRealSrcSpan, realSrcSpanToRange) +import qualified Development.IDE.Core.PositionMapping as P +import qualified Development.IDE.Core.Shake as IDE +import qualified FastString as FS +import SrcLoc + + +------------------------------------------------------------------------------ +-- | A data kind for 'Tracked'. +data Age = Current | Stale Type + + +------------------------------------------------------------------------------ +-- | Some value, tagged with its age. All 'Current' ages are considered to be +-- the same thing, but 'Stale' values are protected by an untouchable variable +-- to ensure they can't be unified. +newtype Tracked (age :: Age) a = UnsafeTracked + { unTrack :: a + } + deriving stock (Functor, Foldable, Traversable) + deriving newtype (Eq, Ord, Show, Read, ToJSON, FromJSON, NFData) + deriving (Applicative, Monad) via Identity + + +------------------------------------------------------------------------------ +-- | Like 'P.PositionMapping', but with annotated ages for how 'Tracked' values +-- change. Use the 'Category' instance to compose 'PositionMapping's in order +-- to transform between values of different stale ages. +newtype PositionMap (from :: Age) (to :: Age) = PositionMap + { getPositionMapping :: P.PositionMapping + } + +instance Category PositionMap where + id = coerce P.zeroMapping + (.) = coerce P.composeDelta + + +------------------------------------------------------------------------------ +-- | Get a 'PositionMap' that runs in the opposite direction. +dualPositionMap :: PositionMap from to -> PositionMap to from +dualPositionMap (PositionMap (P.PositionMapping (P.PositionDelta from to))) = + PositionMap $ P.PositionMapping $ P.PositionDelta to from + + +------------------------------------------------------------------------------ +-- | A pair containing a @'Tracked' 'Stale'@ value, as well as +-- a 'PositionMapping' that will fast-forward values to the current age. +data TrackedStale a where + TrackedStale + :: Tracked (Stale s) a + -> PositionMap (Stale s) Current + -> TrackedStale a + +instance Functor TrackedStale where + fmap f (TrackedStale t pm) = TrackedStale (fmap f t) pm + + +------------------------------------------------------------------------------ +-- | A class for which 'Tracked' values can be run across a 'PositionMapping' +-- to change their ages. +class MapAge a where + {-# MINIMAL mapAgeFrom | mapAgeTo #-} + mapAgeFrom :: PositionMap from to -> Tracked to a -> Maybe (Tracked from a) + mapAgeFrom = mapAgeTo . dualPositionMap + + mapAgeTo :: PositionMap from to -> Tracked from a -> Maybe (Tracked to a) + mapAgeTo = mapAgeFrom . dualPositionMap + + +instance MapAge Range where + mapAgeFrom = coerce P.fromCurrentRange + mapAgeTo = coerce P.toCurrentRange + + +instance MapAge RealSrcSpan where + mapAgeFrom = + invMapAge (\fs -> rangeToRealSrcSpan (fromString $ FS.unpackFS fs)) + (srcSpanFile &&& realSrcSpanToRange) + . mapAgeFrom + + +------------------------------------------------------------------------------ +-- | Helper function for deriving 'MapAge' for values in terms of other +-- instances. +invMapAge + :: (c -> a -> b) + -> (b -> (c, a)) + -> (Tracked from a -> Maybe (Tracked to a)) + -> Tracked from b + -> Maybe (Tracked to b) +invMapAge to from f t = + let (c, t') = unTrack $ fmap from t + in fmap (fmap $ to c) $ f $ UnsafeTracked t' + + +unsafeMkCurrent :: age -> Tracked 'Current age +unsafeMkCurrent = coerce + + +unsafeMkStale :: age -> Tracked (Stale s) age +unsafeMkStale = coerce + + +unsafeCopyAge :: Tracked age a -> b -> Tracked age b +unsafeCopyAge _ = coerce + + +-- | Request a Rule result, it not available return the last computed result, if any, which may be stale +useWithStale :: IdeRule k v + => k -> NormalizedFilePath -> Action (Maybe (TrackedStale v)) +useWithStale key file = do + x <- IDE.useWithStale key file + pure $ x <&> \(v, pm) -> + TrackedStale (coerce v) (coerce pm) + +-- | Request a Rule result, it not available return the last computed result which may be stale. +-- Errors out if none available. +useWithStale_ :: IdeRule k v + => k -> NormalizedFilePath -> Action (TrackedStale v) +useWithStale_ key file = do + (v, pm) <- IDE.useWithStale_ key file + pure $ TrackedStale (coerce v) (coerce pm) + diff --git a/plugins/hls-tactics-plugin/src/Wingman/Judgements.hs b/plugins/hls-tactics-plugin/src/Wingman/Judgements.hs index 92c1f7fdd9..37352c5380 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/Judgements.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/Judgements.hs @@ -12,6 +12,7 @@ import qualified Data.Map as M import Data.Maybe import Data.Set (Set) import qualified Data.Set as S +import Development.IDE.Core.UseStale (Tracked, unTrack) import Development.IDE.Spans.LocalBindings import OccName import SrcLoc @@ -22,8 +23,8 @@ import Wingman.Types ------------------------------------------------------------------------------ -- | Given a 'SrcSpan' and a 'Bindings', create a hypothesis. -hypothesisFromBindings :: RealSrcSpan -> Bindings -> Hypothesis CType -hypothesisFromBindings span bs = buildHypothesis $ getLocalScope bs span +hypothesisFromBindings :: Tracked age RealSrcSpan -> Tracked age Bindings -> Hypothesis CType +hypothesisFromBindings (unTrack -> span) (unTrack -> bs) = buildHypothesis $ getLocalScope bs span ------------------------------------------------------------------------------ diff --git a/plugins/hls-tactics-plugin/src/Wingman/Judgements/Theta.hs b/plugins/hls-tactics-plugin/src/Wingman/Judgements/Theta.hs index ff359eb6bf..40328c09cf 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/Judgements/Theta.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/Judgements/Theta.hs @@ -12,6 +12,7 @@ module Wingman.Judgements.Theta import Data.Maybe (fromMaybe, mapMaybe) import Data.Set (Set) import qualified Data.Set as S +import Development.IDE.Core.UseStale import Development.IDE.GHC.Compat import Generics.SYB hiding (tyConName) import GhcPlugins (mkVarOcc, splitTyConApp_maybe, getTyVar_maybe) @@ -50,11 +51,12 @@ mkEvidence _ = Nothing ------------------------------------------------------------------------------ -- | Compute all the 'Evidence' implicitly bound at the given 'SrcSpan'. -getEvidenceAtHole :: SrcSpan -> LHsBinds GhcTc -> [Evidence] -getEvidenceAtHole dst +getEvidenceAtHole :: Tracked age SrcSpan -> Tracked age (LHsBinds GhcTc) -> [Evidence] +getEvidenceAtHole (unTrack -> dst) = mapMaybe mkEvidence . (everything (<>) $ mkQ mempty (absBinds dst) `extQ` wrapperBinds dst `extQ` matchBinds dst) + . unTrack ------------------------------------------------------------------------------ diff --git a/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs b/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs index 417cc0e4ea..7f02483734 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/LanguageServer.hs @@ -18,10 +18,11 @@ import Data.Monoid import qualified Data.Set as S import qualified Data.Text as T import Data.Traversable -import Development.IDE.Core.PositionMapping import Development.IDE.Core.RuleTypes import Development.IDE.Core.Service (runAction) -import Development.IDE.Core.Shake (IdeState (..), useWithStale) +import Development.IDE.Core.Shake (IdeState (..), use) +import qualified Development.IDE.Core.Shake as IDE +import Development.IDE.Core.UseStale import Development.IDE.GHC.Compat import Development.IDE.GHC.Error (realSrcSpanToRange) import Development.IDE.Spans.LocalBindings (Bindings, getDefiningBindings) @@ -29,16 +30,16 @@ import Development.Shake (Action, RuleResult) import Development.Shake.Classes (Typeable, Binary, Hashable, NFData) import qualified FastString import GhcPlugins (tupleDataCon, consDataCon, substTyAddInScope) -import Ide.Types (PluginId) import qualified Ide.Plugin.Config as Plugin -import Ide.PluginUtils (usePropertyLsp) import Ide.Plugin.Properties +import Ide.PluginUtils (usePropertyLsp) +import Ide.Types (PluginId) import Language.LSP.Server (MonadLsp, sendNotification) import Language.LSP.Types import OccName import Prelude hiding (span) import SrcLoc (containsSpan) -import TcRnTypes (tcg_binds) +import TcRnTypes (tcg_binds, TcGblEnv) import Wingman.Context import Wingman.FeatureSet import Wingman.GHC @@ -62,6 +63,19 @@ runIde :: IdeState -> Action a -> IO a runIde state = runAction "tactic" state +runCurrentIde + :: forall a r + . ( r ~ RuleResult a + , Eq a , Hashable a , Binary a , Show a , Typeable a , NFData a + , Show r, Typeable r, NFData r + ) + => IdeState + -> NormalizedFilePath + -> a + -> MaybeT IO (Tracked 'Current r) +runCurrentIde state nfp a = MaybeT $ fmap (fmap unsafeMkCurrent) $ runIde state $ use a nfp + + runStaleIde :: forall a r . ( r ~ RuleResult a @@ -71,10 +85,25 @@ runStaleIde => IdeState -> NormalizedFilePath -> a - -> MaybeT IO (r, PositionMapping) + -> MaybeT IO (TrackedStale r) runStaleIde state nfp a = MaybeT $ runIde state $ useWithStale a nfp +unsafeRunStaleIde + :: forall a r + . ( r ~ RuleResult a + , Eq a , Hashable a , Binary a , Show a , Typeable a , NFData a + , Show r, Typeable r, NFData r + ) + => IdeState + -> NormalizedFilePath + -> a + -> MaybeT IO r +unsafeRunStaleIde state nfp a = do + (r, _) <- MaybeT $ runIde state $ IDE.useWithStale a nfp + pure r + + ------------------------------------------------------------------------------ properties :: Properties @@ -107,7 +136,7 @@ getIdeDynflags getIdeDynflags state nfp = do -- Ok to use the stale 'ModIface', since all we need is its 'DynFlags' -- which don't change very often. - (msr, _) <- runStaleIde state nfp GetModSummaryWithoutTimestamps + msr <- unsafeRunStaleIde state nfp GetModSummaryWithoutTimestamps pure $ ms_hspp_opts $ msrModSummary msr @@ -117,60 +146,65 @@ getIdeDynflags state nfp = do judgementForHole :: IdeState -> NormalizedFilePath - -> Range + -> Tracked 'Current Range -> FeatureSet - -> MaybeT IO (Range, Judgement, Context, DynFlags) + -> MaybeT IO (Tracked 'Current Range, Judgement, Context, DynFlags) judgementForHole state nfp range features = do - (asts, amapping) <- runStaleIde state nfp GetHieAst - case asts of + TrackedStale asts amapping <- runStaleIde state nfp GetHieAst + case unTrack asts of HAR _ _ _ _ (HieFromDisk _) -> fail "Need a fresh hie file" - HAR _ hf _ _ HieFresh -> do - (binds, _) <- runStaleIde state nfp GetBindings - (tcmod, _) <- runStaleIde state nfp TypeCheck - (rss, g) <- liftMaybe $ getSpanAndTypeAtHole amapping range hf - resulting_range <- liftMaybe $ toCurrentRange amapping $ realSrcSpanToRange rss - let (jdg, ctx) = mkJudgementAndContext features g binds rss tcmod + HAR _ (unsafeCopyAge asts -> hf) _ _ HieFresh -> do + range' <- liftMaybe $ mapAgeFrom amapping range + binds <- runStaleIde state nfp GetBindings + tcmod <- fmap (fmap tmrTypechecked) + $ runStaleIde state nfp TypeCheck + + (rss, g) <- liftMaybe $ getSpanAndTypeAtHole range' hf + new_rss <- liftMaybe $ mapAgeTo amapping rss + (jdg, ctx) <- liftMaybe $ mkJudgementAndContext features g binds new_rss tcmod dflags <- getIdeDynflags state nfp - pure (resulting_range, jdg, ctx, dflags) + pure (fmap realSrcSpanToRange new_rss, jdg, ctx, dflags) mkJudgementAndContext :: FeatureSet -> Type - -> Bindings - -> RealSrcSpan - -> TcModuleResult - -> (Judgement, Context) -mkJudgementAndContext features g binds rss tcmod = do - let tcg = tmrTypechecked tcmod - tcs = tcg_binds tcg - ctx = mkContext features - (mapMaybe (sequenceA . (occName *** coerce)) - $ getDefiningBindings binds rss) - tcg - top_provs = getRhsPosVals rss tcs - local_hy = spliceProvenance top_provs - $ hypothesisFromBindings rss binds - evidence = getEvidenceAtHole (RealSrcSpan rss) tcs - cls_hy = foldMap evidenceToHypothesis evidence - subst = ts_unifier $ appEndo (foldMap (Endo . evidenceToSubst) evidence) defaultTacticState - in ( fmap (CType . substTyAddInScope subst . unCType) $ mkFirstJudgement - (local_hy <> cls_hy) - (isRhsHole rss tcs) - g - , ctx - ) + -> TrackedStale Bindings + -> Tracked 'Current RealSrcSpan + -> TrackedStale TcGblEnv + -> Maybe (Judgement, Context) +mkJudgementAndContext features g (TrackedStale binds bmap) rss (TrackedStale tcg tcgmap) = do + binds_rss <- mapAgeFrom bmap rss + tcg_rss <- mapAgeFrom tcgmap rss + + let tcs = fmap tcg_binds tcg + ctx = mkContext features + (mapMaybe (sequenceA . (occName *** coerce)) + $ unTrack + $ getDefiningBindings <$> binds <*> binds_rss) + (unTrack tcg) + top_provs = getRhsPosVals tcg_rss tcs + local_hy = spliceProvenance top_provs + $ hypothesisFromBindings binds_rss binds + evidence = getEvidenceAtHole (fmap RealSrcSpan tcg_rss) tcs + cls_hy = foldMap evidenceToHypothesis evidence + subst = ts_unifier $ appEndo (foldMap (Endo . evidenceToSubst) evidence) defaultTacticState + pure + ( fmap (CType . substTyAddInScope subst . unCType) $ mkFirstJudgement + (local_hy <> cls_hy) + (isRhsHole tcg_rss tcs) + g + , ctx + ) getSpanAndTypeAtHole - :: PositionMapping - -> Range - -> HieASTs b - -> Maybe (Span, b) -getSpanAndTypeAtHole amapping range hf = do - range' <- fromCurrentRange amapping range + :: Tracked age Range + -> Tracked age (HieASTs b) + -> Maybe (Tracked age RealSrcSpan, b) +getSpanAndTypeAtHole r@(unTrack -> range) (unTrack -> hf) = do join $ listToMaybe $ M.elems $ flip M.mapWithKey (getAsts hf) $ \fs ast -> - case selectSmallestContaining (rangeToRealSrcSpan (FastString.unpackFS fs) range') ast of + case selectSmallestContaining (rangeToRealSrcSpan (FastString.unpackFS fs) range) ast of Nothing -> Nothing Just ast' -> do let info = nodeInfo ast' @@ -179,7 +213,7 @@ getSpanAndTypeAtHole amapping range hf = do -- Ensure we're actually looking at a hole here guard $ all (either (const False) $ isHole . occName) $ M.keysSet $ nodeIdentifiers info - pure (nodeSpan ast', ty) + pure (unsafeCopyAge r $ nodeSpan ast', ty) liftMaybe :: Monad m => Maybe a -> MaybeT m a @@ -200,8 +234,11 @@ spliceProvenance top x = ------------------------------------------------------------------------------ -- | Compute top-level position vals of a function -getRhsPosVals :: RealSrcSpan -> TypecheckedSource -> Hypothesis CType -getRhsPosVals rss tcs +getRhsPosVals + :: Tracked age RealSrcSpan + -> Tracked age TypecheckedSource + -> Hypothesis CType +getRhsPosVals (unTrack -> rss) (unTrack -> tcs) = everything (<>) (mkQ mempty $ \case TopLevelRHS name ps (L (RealSrcSpan span) -- body with no guards and a single defn @@ -344,11 +381,12 @@ mkIdHypothesis (splitId -> (name, ty)) prov = ------------------------------------------------------------------------------ -- | Is this hole immediately to the right of an equals sign? -isRhsHole :: RealSrcSpan -> TypecheckedSource -> Bool -isRhsHole rss tcs = everything (||) (mkQ False $ \case - TopLevelRHS _ _ (L (RealSrcSpan span) _) -> containsSpan rss span - _ -> False - ) tcs +isRhsHole :: Tracked age RealSrcSpan -> Tracked age TypecheckedSource -> Bool +isRhsHole (unTrack -> rss) (unTrack -> tcs) = + everything (||) (mkQ False $ \case + TopLevelRHS _ _ (L (RealSrcSpan span) _) -> containsSpan rss span + _ -> False + ) tcs ufmSeverity :: UserFacingMessage -> MessageType @@ -364,3 +402,4 @@ mkShowMessageParams ufm = ShowMessageParams (ufmSeverity ufm) $ T.pack $ show uf showLspMessage :: MonadLsp cfg m => ShowMessageParams -> m () showLspMessage = sendNotification SWindowShowMessage + diff --git a/plugins/hls-tactics-plugin/src/Wingman/LanguageServer/TacticProviders.hs b/plugins/hls-tactics-plugin/src/Wingman/LanguageServer/TacticProviders.hs index 839a0615d7..f8ef87eb18 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/LanguageServer/TacticProviders.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/LanguageServer/TacticProviders.hs @@ -20,21 +20,22 @@ import Data.Monoid import qualified Data.Text as T import Data.Traversable import DataCon (dataConName) +import Development.IDE.Core.UseStale (Tracked, Age(..)) import Development.IDE.GHC.Compat import GHC.Generics import GHC.LanguageExtensions.Type (Extension (LambdaCase)) -import Wingman.Auto -import Wingman.FeatureSet -import Wingman.GHC -import Wingman.Judgements -import Wingman.Tactics -import Wingman.Types import Ide.PluginUtils import Ide.Types import Language.LSP.Types import OccName import Prelude hiding (span) import Refinery.Tactic (goal) +import Wingman.Auto +import Wingman.FeatureSet +import Wingman.GHC +import Wingman.Judgements +import Wingman.Tactics +import Wingman.Types ------------------------------------------------------------------------------ @@ -150,14 +151,14 @@ data TacticProviderData = TacticProviderData , tpd_config :: Config , tpd_plid :: PluginId , tpd_uri :: Uri - , tpd_range :: Range + , tpd_range :: Tracked 'Current Range , tpd_jdg :: Judgement } data TacticParams = TacticParams { tp_file :: Uri -- ^ Uri of the file to fill the hole in - , tp_range :: Range -- ^ The range of the hole + , tp_range :: Tracked 'Current Range -- ^ The range of the hole , tp_var_name :: T.Text } deriving stock (Show, Eq, Generic) diff --git a/plugins/hls-tactics-plugin/src/Wingman/Plugin.hs b/plugins/hls-tactics-plugin/src/Wingman/Plugin.hs index a2b84ad807..b376176816 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/Plugin.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/Plugin.hs @@ -13,11 +13,12 @@ import Control.Monad.Trans import Control.Monad.Trans.Maybe import Data.Aeson import Data.Bifunctor (first) +import Data.Data import Data.Foldable (for_) import Data.Maybe -import Data.Proxy (Proxy(..)) import qualified Data.Text as T import Development.IDE.Core.Shake (IdeState (..)) +import Development.IDE.Core.UseStale (Tracked, TrackedStale(..), unTrack, mapAgeFrom, unsafeMkCurrent) import Development.IDE.GHC.Compat import Development.IDE.GHC.ExactPrint import Ide.Types @@ -39,21 +40,22 @@ import Wingman.Types descriptor :: PluginId -> PluginDescriptor IdeState descriptor plId = (defaultPluginDescriptor plId) - { pluginCommands - = fmap (\tc -> - PluginCommand - (tcCommandId tc) - (tacticDesc $ tcCommandName tc) - (tacticCmd (commandTactic tc) plId)) - [minBound .. maxBound] - , pluginHandlers = - mkPluginHandler STextDocumentCodeAction codeActionProvider - , pluginCustomConfig = - mkCustomConfig properties - } + { pluginCommands + = fmap (\tc -> + PluginCommand + (tcCommandId tc) + (tacticDesc $ tcCommandName tc) + (tacticCmd (commandTactic tc) plId)) + [minBound .. maxBound] + , pluginHandlers = + mkPluginHandler STextDocumentCodeAction codeActionProvider + , pluginCustomConfig = + mkCustomConfig properties + } + codeActionProvider :: PluginMethodHandler IdeState TextDocumentCodeAction -codeActionProvider state plId (CodeActionParams _ _ (TextDocumentIdentifier uri) range _ctx) +codeActionProvider state plId (CodeActionParams _ _ (TextDocumentIdentifier uri) (unsafeMkCurrent -> range) _ctx) | Just nfp <- uriToNormalizedFilePath $ toNormalizedUri uri = do cfg <- getTacticConfig plId liftIO $ fromMaybeT (Right $ List []) $ do @@ -88,8 +90,9 @@ tacticCmd tac pId state (TacticParams uri range var_name) ccs <- getClientCapabilities res <- liftIO $ runMaybeT $ do (range', jdg, ctx, dflags) <- judgementForHole state nfp range features - let span = rangeToRealSrcSpan (fromNormalizedFilePath nfp) range' - pm <- MaybeT $ useAnnotatedSource "tacticsCmd" state nfp + let span = fmap (rangeToRealSrcSpan (fromNormalizedFilePath nfp)) range' + TrackedStale pm pmmap <- runStaleIde state nfp GetAnnotatedParsedSource + pm_span <- liftMaybe $ mapAgeFrom pmmap span timingOut 2e8 $ join $ case runTactic ctx jdg $ tac $ mkVarOcc $ T.unpack var_name of @@ -98,7 +101,7 @@ tacticCmd tac pId state (TacticParams uri range var_name) case rtr_extract rtr of L _ (HsVar _ (L _ rdr)) | isHole (occName rdr) -> Left NothingToDo - _ -> pure $ mkWorkspaceEdits span dflags ccs uri pm rtr + _ -> pure $ mkWorkspaceEdits pm_span dflags ccs uri pm rtr case res of Nothing -> do @@ -130,14 +133,14 @@ mkErr code err = ResponseError code err Nothing -- | Turn a 'RunTacticResults' into concrete edits to make in the source -- document. mkWorkspaceEdits - :: RealSrcSpan + :: Tracked age RealSrcSpan -> DynFlags -> ClientCapabilities -> Uri - -> Annotated ParsedSource + -> Tracked age (Annotated ParsedSource) -> RunTacticResults -> Either UserFacingMessage WorkspaceEdit -mkWorkspaceEdits span dflags ccs uri pm rtr = do +mkWorkspaceEdits (unTrack -> span) dflags ccs uri (unTrack -> pm) rtr = do for_ (rtr_other_solns rtr) $ \soln -> do traceMX "other solution" $ syn_val soln traceMX "with score" $ scoreSolution soln (rtr_jdg rtr) [] diff --git a/plugins/hls-tactics-plugin/src/Wingman/Range.hs b/plugins/hls-tactics-plugin/src/Wingman/Range.hs index 470e207742..fed5729996 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/Range.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/Range.hs @@ -1,18 +1,23 @@ +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE RankNTypes #-} + module Wingman.Range where -import Development.IDE.Types.Location +import Development.IDE hiding (rangeToRealSrcSpan, rangeToSrcSpan) import qualified FastString as FS import SrcLoc + ------------------------------------------------------------------------------ -- | Convert a DAML compiler Range to a GHC SrcSpan -- TODO(sandy): this doesn't belong here rangeToSrcSpan :: String -> Range -> SrcSpan rangeToSrcSpan file range = RealSrcSpan $ rangeToRealSrcSpan file range + rangeToRealSrcSpan :: String -> Range -> RealSrcSpan rangeToRealSrcSpan file (Range (Position startLn startCh) (Position endLn endCh)) = - mkRealSrcSpan - (mkRealSrcLoc (FS.fsLit file) (startLn + 1) (startCh + 1)) - (mkRealSrcLoc (FS.fsLit file) (endLn + 1) (endCh + 1)) + mkRealSrcSpan + (mkRealSrcLoc (FS.fsLit file) (startLn + 1) (startCh + 1)) + (mkRealSrcLoc (FS.fsLit file) (endLn + 1) (endCh + 1)) diff --git a/plugins/hls-tactics-plugin/src/Wingman/Types.hs b/plugins/hls-tactics-plugin/src/Wingman/Types.hs index cf5aa9655d..4db95dd5e1 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/Types.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/Types.hs @@ -11,7 +11,6 @@ module Wingman.Types , Type , TyVar , Span - , Range ) where import ConLike (ConLike) @@ -29,7 +28,6 @@ import qualified Data.Text as T import Data.Tree import Development.IDE.GHC.Compat hiding (Node) import Development.IDE.GHC.Orphans () -import Development.IDE.Types.Location import GHC.Generics import GHC.SourceGen (var) import OccName