1- {-# LANGUAGE OverloadedStrings #-}
2- {-# LANGUAGE RecordWildCards #-}
3- {-# LANGUAGE ScopedTypeVariables #-}
1+ {-# LANGUAGE ExistentialQuantification #-}
2+ {-# LANGUAGE OverloadedStrings #-}
3+ {-# LANGUAGE RecordWildCards #-}
4+ {-# LANGUAGE ScopedTypeVariables #-}
45
56module Ide.Plugin.CodeRange (
67 descriptor
@@ -13,27 +14,29 @@ module Ide.Plugin.CodeRange (
1314 ) where
1415
1516import Control.Monad.Except (ExceptT (ExceptT ),
16- runExceptT )
17+ mapExceptT )
1718import Control.Monad.IO.Class (liftIO )
1819import Control.Monad.Trans.Maybe (MaybeT (MaybeT ),
1920 maybeToExceptT )
2021import Data.Either.Extra (maybeToEither )
2122import Data.Maybe (fromMaybe )
2223import Data.Vector (Vector )
2324import qualified Data.Vector as V
24- import Development.IDE (IdeAction ,
25+ import Development.IDE (Action , IdeAction ,
2526 IdeState (shakeExtras ),
2627 Range (Range ), Recorder ,
2728 WithPriority ,
28- cmapWithPrio ,
29+ cmapWithPrio , runAction ,
2930 runIdeAction ,
3031 toNormalizedFilePath' ,
31- uriToFilePath' )
32- import Development.IDE.Core.Actions ( useE )
32+ uriToFilePath' , use ,
33+ useWithStaleFast )
3334import Development.IDE.Core.PositionMapping (PositionMapping ,
3435 fromCurrentPosition ,
3536 toCurrentRange )
36- import Development.IDE.Types.Logger (Pretty (.. ))
37+ import Development.IDE.Types.Logger (Pretty (.. ),
38+ Priority (Warning ),
39+ logWith )
3740import Ide.Plugin.CodeRange.Rules (CodeRange (.. ),
3841 GetCodeRange (.. ),
3942 codeRangeRule , crkToFrk )
@@ -44,7 +47,7 @@ import Ide.Types (PluginDescriptor (pluginH
4447 PluginId ,
4548 defaultPluginDescriptor ,
4649 mkPluginHandler )
47- import Language.LSP.Server (LspM )
50+ import Language.LSP.Server (LspM , LspT )
4851import Language.LSP.Types (FoldingRange (.. ),
4952 FoldingRangeParams (.. ),
5053 List (List ),
@@ -61,65 +64,97 @@ import Prelude hiding (log, span)
6164
6265descriptor :: Recorder (WithPriority Log ) -> PluginId -> PluginDescriptor IdeState
6366descriptor recorder plId = (defaultPluginDescriptor plId)
64- { pluginHandlers = mkPluginHandler STextDocumentSelectionRange selectionRangeHandler
65- <> mkPluginHandler STextDocumentFoldingRange foldingRangeHandler
67+ { pluginHandlers = mkPluginHandler STextDocumentSelectionRange ( selectionRangeHandler recorder)
68+ <> mkPluginHandler STextDocumentFoldingRange ( foldingRangeHandler recorder)
6669 , pluginRules = codeRangeRule (cmapWithPrio LogRules recorder)
6770 }
6871
6972data Log = LogRules Rules. Log
73+ | forall rule . Show rule => LogBadDependency rule
7074
7175instance Pretty Log where
7276 pretty log = case log of
7377 LogRules codeRangeLog -> pretty codeRangeLog
78+ LogBadDependency rule -> pretty $ " bad dependency: " <> show rule
7479
75- foldingRangeHandler :: IdeState -> PluginId -> FoldingRangeParams -> LspM c (Either ResponseError (List FoldingRange ))
76- foldingRangeHandler ide _ FoldingRangeParams {.. } = do
80+ foldingRangeHandler :: Recorder ( WithPriority Log ) -> IdeState -> PluginId -> FoldingRangeParams -> LspM c (Either ResponseError (List FoldingRange ))
81+ foldingRangeHandler recorder ide _ FoldingRangeParams {.. } = do
7782 pluginResponse $ do
7883 filePath <- ExceptT . pure . maybeToEither " fail to convert uri to file path" $
7984 toNormalizedFilePath' <$> uriToFilePath' uri
80- foldingRanges <- ExceptT . liftIO . runIdeAction " FoldingRange " (shakeExtras ide) . runExceptT $
85+ foldingRanges <- mapExceptT runAction' $
8186 getFoldingRanges filePath
8287 pure . List $ foldingRanges
83- where
84- uri :: Uri
85- TextDocumentIdentifier uri = _textDocument
88+ where
89+ uri :: Uri
90+ TextDocumentIdentifier uri = _textDocument
8691
87- getFoldingRanges :: NormalizedFilePath -> ExceptT String IdeAction [FoldingRange ]
88- getFoldingRanges file = do
89- (codeRange, _) <- maybeToExceptT " fail to get code range" $ useE GetCodeRange file
92+ runAction' :: Action (Either FoldingRangeError [FoldingRange ]) -> LspT c IO (Either String [FoldingRange ])
93+ runAction' action = do
94+ result <- liftIO $ runAction " FoldingRange" ide action
95+ case result of
96+ Left err -> case err of
97+ FoldingRangeBadDependency rule -> do
98+ logWith recorder Warning $ LogBadDependency rule
99+ pure $ Right []
100+ Right list -> pure $ Right list
90101
91- -- removing first node because it folds the entire file
92- pure $ drop 1 $ findFoldingRanges codeRange
102+ data FoldingRangeError = forall rule . Show rule => FoldingRangeBadDependency rule
93103
94- selectionRangeHandler :: IdeState -> PluginId -> SelectionRangeParams -> LspM c (Either ResponseError (List SelectionRange ))
95- selectionRangeHandler ide _ SelectionRangeParams {.. } = do
104+ getFoldingRanges :: NormalizedFilePath -> ExceptT FoldingRangeError Action [FoldingRange ]
105+ getFoldingRanges file = do
106+ codeRange <- maybeToExceptT (FoldingRangeBadDependency GetCodeRange ) . MaybeT $ use GetCodeRange file
107+ pure $ findFoldingRanges codeRange
108+
109+ selectionRangeHandler :: Recorder (WithPriority Log ) -> IdeState -> PluginId -> SelectionRangeParams -> LspM c (Either ResponseError (List SelectionRange ))
110+ selectionRangeHandler recorder ide _ SelectionRangeParams {.. } = do
96111 pluginResponse $ do
97112 filePath <- ExceptT . pure . maybeToEither " fail to convert uri to file path" $
98113 toNormalizedFilePath' <$> uriToFilePath' uri
99- selectionRanges <- ExceptT . liftIO . runIdeAction " SelectionRange" (shakeExtras ide) . runExceptT $
100- getSelectionRanges filePath positions
101- pure . List $ selectionRanges
114+ fmap List . mapExceptT runIdeAction' . getSelectionRanges filePath $ positions
102115 where
103116 uri :: Uri
104117 TextDocumentIdentifier uri = _textDocument
105118
106119 positions :: [Position ]
107120 List positions = _positions
108121
109- getSelectionRanges :: NormalizedFilePath -> [Position ] -> ExceptT String IdeAction [SelectionRange ]
122+ runIdeAction' :: IdeAction (Either SelectionRangeError [SelectionRange ]) -> LspT c IO (Either String [SelectionRange ])
123+ runIdeAction' action = do
124+ result <- liftIO $ runIdeAction " SelectionRange" (shakeExtras ide) action
125+ case result of
126+ Left err -> case err of
127+ SelectionRangeBadDependency rule -> do
128+ logWith recorder Warning $ LogBadDependency rule
129+ -- This might happen if the HieAst is not ready,
130+ -- so we give it a default value instead of throwing an error
131+ pure $ Right []
132+ SelectionRangeInputPositionMappingFailure -> pure $
133+ Left " failed to apply position mapping to input positions"
134+ SelectionRangeOutputPositionMappingFailure -> pure $
135+ Left " failed to apply position mapping to output positions"
136+ Right list -> pure $ Right list
137+
138+ data SelectionRangeError = forall rule . Show rule => SelectionRangeBadDependency rule
139+ | SelectionRangeInputPositionMappingFailure
140+ | SelectionRangeOutputPositionMappingFailure
141+
142+ getSelectionRanges :: NormalizedFilePath -> [Position ] -> ExceptT SelectionRangeError IdeAction [SelectionRange ]
110143getSelectionRanges file positions = do
111- (codeRange, positionMapping) <- maybeToExceptT " fail to get code range" $ useE GetCodeRange file
144+ (codeRange, positionMapping) <- maybeToExceptT (SelectionRangeBadDependency GetCodeRange ) . MaybeT $
145+ useWithStaleFast GetCodeRange file
112146 -- 'positionMapping' should be appied to the input before using them
113- positions' <- maybeToExceptT " fail to apply position mapping to input positions " . MaybeT . pure $
147+ positions' <- maybeToExceptT SelectionRangeInputPositionMappingFailure . MaybeT . pure $
114148 traverse (fromCurrentPosition positionMapping) positions
115149
116150 let selectionRanges = flip fmap positions' $ \ pos ->
117- -- We need a default selection range if the lookup fails, so that other positions can still have valid results.
151+ -- We need a default selection range if the lookup fails,
152+ -- so that other positions can still have valid results.
118153 let defaultSelectionRange = SelectionRange (Range pos pos) Nothing
119154 in fromMaybe defaultSelectionRange . findPosition pos $ codeRange
120155
121156 -- 'positionMapping' should be applied to the output ranges before returning them
122- maybeToExceptT " fail to apply position mapping to output positions " . MaybeT . pure $
157+ maybeToExceptT SelectionRangeOutputPositionMappingFailure . MaybeT . pure $
123158 traverse (toCurrentSelectionRange positionMapping) selectionRanges
124159
125160-- | Find 'Position' in 'CodeRange'. This can fail, if the given position is not covered by the 'CodeRange'.
@@ -169,8 +204,13 @@ findPosition pos root = go Nothing root
169204--
170205-- Discussion reference: https://github.com/haskell/haskell-language-server/pull/3058#discussion_r973737211
171206findFoldingRanges :: CodeRange -> [FoldingRange ]
172- findFoldingRanges r@ (CodeRange _ children _) =
173- let frChildren :: [FoldingRange ] = concat $ V. toList $ fmap findFoldingRanges children
207+ findFoldingRanges codeRange =
208+ -- removing the first node because it folds the entire file
209+ drop 1 $ findFoldingRangesRec codeRange
210+
211+ findFoldingRangesRec :: CodeRange -> [FoldingRange ]
212+ findFoldingRangesRec r@ (CodeRange _ children _) =
213+ let frChildren :: [FoldingRange ] = concat $ V. toList $ fmap findFoldingRangesRec children
174214 in case createFoldingRange r of
175215 Just x -> x: frChildren
176216 Nothing -> frChildren
0 commit comments