@@ -19,10 +19,12 @@ import Control.Monad.IO.Class (MonadIO (liftIO))
1919import Control.Monad.Trans.Class (lift )
2020import Control.Monad.Trans.Maybe
2121import Data.Aeson (ToJSON (toJSON ))
22+ import Data.Function (on )
2223import Data.Generics (GenericQ , everything ,
2324 everythingBut , extQ , mkQ )
2425import qualified Data.IntMap.Strict as IntMap
25- import Data.List (find , intersperse )
26+ import Data.List (find , intersperse ,
27+ sortOn )
2628import qualified Data.Map as Map
2729import Data.Maybe (fromMaybe , isJust ,
2830 mapMaybe , maybeToList )
@@ -99,6 +101,7 @@ import Ide.Plugin.Error (PluginError (PluginIntern
99101import Ide.Plugin.RangeMap (RangeMap )
100102import qualified Ide.Plugin.RangeMap as RangeMap
101103import Ide.Plugin.Resolve (mkCodeActionWithResolveAndCommand )
104+ import Ide.PluginUtils (subRange )
102105import Ide.Types (PluginDescriptor (.. ),
103106 PluginId (.. ),
104107 PluginMethodHandler ,
@@ -111,7 +114,7 @@ import Language.LSP.Protocol.Message (Method (..),
111114import Language.LSP.Protocol.Types (CodeAction (.. ),
112115 CodeActionKind (CodeActionKind_RefactorRewrite ),
113116 CodeActionParams (CodeActionParams ),
114- Command , InlayHint (.. ),
117+ InlayHint (.. ),
115118 InlayHintLabelPart (InlayHintLabelPart ),
116119 InlayHintParams (InlayHintParams , _range , _textDocument ),
117120 TextDocumentIdentifier (TextDocumentIdentifier ),
@@ -155,17 +158,19 @@ codeActionProvider ideState _ (CodeActionParams _ _ docId range _) = do
155158 CRR {crCodeActions, crCodeActionResolve, enabledExtensions} <- runActionE " ExplicitFields.CollectRecords" ideState $ useE CollectRecords nfp
156159 -- All we need to build a code action is the list of extensions, and a int to
157160 -- allow us to resolve it later.
158- let recordUids = [ uid
161+ let recordsWithUid = [ ( uid, record)
159162 | uid <- RangeMap. filterByRange range crCodeActions
160163 , Just record <- [IntMap. lookup uid crCodeActionResolve]
161164 -- Only fully saturated constructor applications can be
162165 -- converted to the record syntax through the code action
163166 , isConvertible record
164167 ]
165- let actions = map (mkCodeAction enabledExtensions) recordUids
166- pure $ InL actions
168+ recordsOnly = map snd recordsWithUid
169+ sortedRecords = sortOn (recordDepth recordsOnly . snd ) recordsWithUid
170+ pure $ InL $ case sortedRecords of
171+ (top : _) -> [mkCodeAction enabledExtensions (fst top)]
172+ [] -> []
167173 where
168- mkCodeAction :: [Extension ] -> Int -> Command |? CodeAction
169174 mkCodeAction exts uid = InR CodeAction
170175 { _title = mkTitle exts -- TODO: `Expand positional record` without NamedFieldPuns if RecordInfoApp
171176 , _kind = Just CodeActionKind_RefactorRewrite
@@ -266,9 +271,12 @@ inlayHintPosRecProvider _ state _pId InlayHintParams {_textDocument = TextDocume
266271 pure $ InL (concatMap (mkInlayHints nameMap pm) records)
267272 where
268273 mkInlayHints :: UniqFM Name [Name ] -> PositionMapping -> RecordInfo -> [InlayHint ]
269- mkInlayHints nameMap pm record@ (RecordInfoApp _ (RecordAppExpr _ _ fla)) =
270- let textEdits = renderRecordInfoAsTextEdit nameMap record
271- in mapMaybe (mkInlayHint textEdits pm) fla
274+ mkInlayHints nameMap pm record@ (RecordInfoApp _ (RecordAppExpr sat _ fla)) =
275+ -- Only create inlay hints for fully saturated constructors
276+ case sat of
277+ Saturated -> let textEdits = renderRecordInfoAsTextEdit nameMap record
278+ in mapMaybe (mkInlayHint textEdits pm) fla
279+ Unsaturated -> []
272280 mkInlayHints _ _ _ = []
273281
274282 mkInlayHint :: Maybe TextEdit -> PositionMapping -> (Located FieldLabel , HsExpr GhcTc ) -> Maybe InlayHint
@@ -296,6 +304,12 @@ mkTitle exts = "Expand record wildcard"
296304 then mempty
297305 else " (needs extension: NamedFieldPuns)"
298306
307+ -- Calculate the nesting depth of a record by counting how many other records
308+ -- contain it. Used to prioritize more deeply nested records in code actions.
309+ recordDepth :: [RecordInfo ] -> RecordInfo -> Int
310+ recordDepth allRecords record =
311+ let isSubrangeOf = subRange `on` recordInfoToRange
312+ in length $ filter (`isSubrangeOf` record) allRecords
299313
300314pragmaEdit :: [Extension ] -> NextPragmaInfo -> Maybe TextEdit
301315pragmaEdit exts pragma = if NamedFieldPuns `elem` exts
@@ -602,7 +616,11 @@ getRecCons e@(unLoc -> RecordCon _ _ flds)
602616getRecCons expr@ (unLoc -> app@ (HsApp _ _ _)) =
603617 let fieldss = maybeToList $ getFields app []
604618 recInfo = concatMap mkRecInfo fieldss
605- in (recInfo, not (null recInfo))
619+ -- Search control for positional constructors.
620+ -- True stops further (nested) searching; False allows recursive search.
621+ -- Currently hardcoded to False to enable nested positional searches.
622+ -- Use `in (recInfo, not (null recInfo))` to disable nested searching.
623+ in (recInfo, False )
606624 where
607625 mkRecInfo :: RecordAppExpr -> [RecordInfo ]
608626 mkRecInfo appExpr =
0 commit comments