1- {-# LANGUAGE DataKinds #-}
2- {-# LANGUAGE DerivingStrategies #-}
3- {-# LANGUAGE OverloadedLabels #-}
4- {-# LANGUAGE OverloadedRecordDot #-}
5- {-# LANGUAGE OverloadedStrings #-}
6- {-# LANGUAGE RecordWildCards #-}
7- {-# LANGUAGE TemplateHaskell #-}
8- {-# LANGUAGE TypeFamilies #-}
9- {-# LANGUAGE UnicodeSyntax #-}
1+ {-# LANGUAGE BlockArguments #-}
2+ {-# LANGUAGE DataKinds #-}
3+ {-# LANGUAGE DerivingStrategies #-}
4+ {-# LANGUAGE ImpredicativeTypes #-}
5+ {-# LANGUAGE LiberalTypeSynonyms #-}
6+ {-# LANGUAGE MultiWayIf #-}
7+ {-# LANGUAGE OverloadedLabels #-}
8+ {-# LANGUAGE OverloadedRecordDot #-}
9+ {-# LANGUAGE OverloadedStrings #-}
10+ {-# LANGUAGE PatternSynonyms #-}
11+ {-# LANGUAGE QuantifiedConstraints #-}
12+ {-# LANGUAGE RecordWildCards #-}
13+ {-# LANGUAGE TemplateHaskell #-}
14+ {-# LANGUAGE TypeFamilies #-}
15+ {-# LANGUAGE UnicodeSyntax #-}
16+ {-# LANGUAGE ViewPatterns #-}
1017
1118-- |
1219-- This module provides the core functionality of the plugin.
@@ -20,20 +27,29 @@ import Control.Monad.Except (ExceptT, liftEither,
2027import Control.Monad.IO.Class (MonadIO (.. ))
2128import Control.Monad.Trans (lift )
2229import Control.Monad.Trans.Except (runExceptT )
30+ import Control.Monad.Trans.Maybe
31+ import Data.Data (Data (.. ))
32+ import Data.List
2333import qualified Data.Map.Strict as M
34+ import Data.Maybe
35+ import Data.Semigroup (First (.. ))
2436import Data.Text (Text )
2537import qualified Data.Text as T
38+ import Debug.Trace
2639import Development.IDE (Action ,
2740 GetDocMap (GetDocMap ),
2841 GetHieAst (GetHieAst ),
42+ GetParsedModuleWithComments (.. ),
2943 HieAstResult (HAR , hieAst , hieModule , refMap ),
3044 IdeResult , IdeState ,
3145 Priority (.. ),
3246 Recorder , Rules ,
3347 WithPriority ,
3448 cmapWithPrio , define ,
35- fromNormalizedFilePath ,
36- hieKind )
49+ hieKind ,
50+ srcSpanToRange ,
51+ toNormalizedUri ,
52+ useWithStale )
3753import Development.IDE.Core.PluginUtils (runActionE , useE ,
3854 useWithStaleE )
3955import Development.IDE.Core.Rules (toIdeResult )
@@ -43,9 +59,9 @@ import Development.IDE.Core.Shake (ShakeExtras (..),
4359 getVirtualFile )
4460import Development.IDE.GHC.Compat hiding (Warning )
4561import Development.IDE.GHC.Compat.Util (mkFastString )
62+ import GHC.Parser.Annotation
4663import Ide.Logger (logWith )
47- import Ide.Plugin.Error (PluginError (PluginInternalError ),
48- getNormalizedFilePathE ,
64+ import Ide.Plugin.Error (PluginError (PluginInternalError , PluginRuleFailed ),
4965 handleMaybe ,
5066 handleMaybeM )
5167import Ide.Plugin.SemanticTokens.Mappings
@@ -57,11 +73,18 @@ import Ide.Types
5773import qualified Language.LSP.Protocol.Lens as L
5874import Language.LSP.Protocol.Message (MessageResult ,
5975 Method (Method_TextDocumentSemanticTokensFull , Method_TextDocumentSemanticTokensFullDelta ))
60- import Language.LSP.Protocol.Types (NormalizedFilePath ,
76+ import Language.LSP.Protocol.Types (NormalizedUri , Range ,
6177 SemanticTokens ,
78+ fromNormalizedUri ,
79+ getUri ,
6280 type (|? ) (InL , InR ))
6381import Prelude hiding (span )
6482import qualified StmContainers.Map as STM
83+ import Type.Reflection (Typeable , eqTypeRep ,
84+ pattern App ,
85+ type (:~~: ) (HRefl ),
86+ typeOf , typeRep ,
87+ withTypeable )
6588
6689
6790$ mkSemanticConfigFunctions
@@ -75,8 +98,17 @@ computeSemanticTokens recorder pid _ nfp = do
7598 config <- lift $ useSemanticConfigAction pid
7699 logWith recorder Debug (LogConfig config)
77100 semanticId <- lift getAndIncreaseSemanticTokensId
78- (RangeHsSemanticTokenTypes {rangeSemanticList}, mapping) <- useWithStaleE GetSemanticTokens nfp
79- withExceptT PluginInternalError $ liftEither $ rangeSemanticsSemanticTokens semanticId config mapping rangeSemanticList
101+
102+ (sortOn fst -> tokenList, First mapping) <- do
103+ rangesyntacticTypes <- lift $ useWithStale GetSyntacticTokens nuri
104+ rangesemanticTypes <- lift $ useWithStale GetSemanticTokens nuri
105+ let mk w u (toks, mapping) = (map (fmap w) $ u toks, First mapping)
106+ maybeToExceptT (PluginRuleFailed " no syntactic nor semantic tokens" ) $ hoistMaybe $
107+ (mk HsSyntacticTokenType rangeSyntacticList <$> rangesyntacticTypes)
108+ <> (mk HsSemanticTokenType rangeSemanticList <$> rangesemanticTypes)
109+
110+ -- NOTE: rangeSemanticsSemanticTokens actually assumes that the tokesn are in order. that means they have to be sorted by position
111+ withExceptT PluginInternalError $ liftEither $ rangeSemanticsSemanticTokens semanticId config mapping tokenList
80112
81113semanticTokensFull :: Recorder (WithPriority SemanticLog ) -> PluginMethodHandler IdeState 'Method_TextDocumentSemanticTokensFull
82114semanticTokensFull recorder state pid param = runActionE " SemanticTokens.semanticTokensFull" state computeSemanticTokensFull
@@ -130,6 +162,87 @@ getSemanticTokensRule recorder =
130162 let hsFinder = idSemantic getTyThingMap (hieKindFunMasksKind hieKind) refMap
131163 return $ computeRangeHsSemanticTokenTypeList hsFinder virtualFile ast
132164
165+ getSyntacticTokensRule :: Recorder (WithPriority SemanticLog ) -> Rules ()
166+ getSyntacticTokensRule recorder =
167+ define (cmapWithPrio LogShake recorder) $ \ GetSyntacticTokens nfp -> handleError recorder $ do
168+ (parsedModule, _) <- withExceptT LogDependencyError $ useWithStaleE GetParsedModuleWithComments nfp
169+ let tokList = computeRangeHsSyntacticTokenTypeList parsedModule
170+ logWith recorder Debug $ LogSyntacticTokens tokList
171+ pure tokList
172+
173+ astTraversalWith :: forall b r . Data b => b -> (forall a . Data a => a -> [r ]) -> [r ]
174+ astTraversalWith ast f = mconcat $ flip gmapQ ast \ y -> f y <> astTraversalWith y f
175+
176+ {-# inline extractTyToTy #-}
177+ extractTyToTy :: forall f a . (Typeable f , Data a ) => a -> Maybe (forall r . (forall b . Typeable b => f b -> r ) -> r )
178+ extractTyToTy node
179+ | App conRep argRep <- typeOf node
180+ , Just HRefl <- eqTypeRep conRep (typeRep @ f )
181+ = Just $ withTypeable argRep $ (\ k -> k node)
182+ | otherwise = Nothing
183+
184+ {-# inline extractTy #-}
185+ extractTy :: forall b a . (Typeable b , Data a ) => a -> Maybe b
186+ extractTy node
187+ | Just HRefl <- eqTypeRep (typeRep @ b ) (typeOf node)
188+ = Just node
189+ | otherwise = Nothing
190+
191+ computeRangeHsSyntacticTokenTypeList :: ParsedModule -> RangeHsSyntacticTokenTypes
192+ computeRangeHsSyntacticTokenTypeList ParsedModule {pm_parsed_source} =
193+ let toks = astTraversalWith pm_parsed_source \ node -> mconcat
194+ [ maybeToList $ mkFromLocatable TKeyword . (\ k -> k \ x k' -> k' x) =<< extractTyToTy @ EpToken node
195+ -- FIXME: probably needs to be commented out for ghc > 9.10
196+ , maybeToList $ mkFromLocatable TKeyword . (\ x k -> k x) =<< extractTy @ AddEpAnn node
197+ , do
198+ EpAnnImportDecl i p s q pkg a <- maybeToList $ extractTy @ EpAnnImportDecl node
199+
200+ mapMaybe (mkFromLocatable TKeyword . (\ x k -> k x)) $ catMaybes $ [Just i, s, q, pkg, a] <> foldMap (\ (l, l') -> [Just l, Just l']) p
201+ , maybeToList $ mkFromLocatable TComment . (\ x k -> k x) =<< extractTy @ LEpaComment node
202+ , do
203+ L loc expr <- maybeToList $ extractTy @ (LHsExpr GhcPs ) node
204+ let fromSimple = maybeToList . flip mkFromLocatable \ k -> k loc
205+ case expr of
206+ HsOverLabel {} -> fromSimple TStringLit
207+ HsOverLit _ (OverLit _ lit) -> fromSimple case lit of
208+ HsIntegral {} -> TNumberLit
209+ HsFractional {} -> TNumberLit
210+
211+ HsIsString {} -> TStringLit
212+ HsLit _ lit -> fromSimple case lit of
213+ HsChar {} -> TCharLit
214+ HsCharPrim {} -> TCharLit
215+
216+ HsInt {} -> TNumberLit
217+ HsInteger {} -> TNumberLit
218+ HsIntPrim {} -> TNumberLit
219+ HsWordPrim {} -> TNumberLit
220+ HsWord8Prim {} -> TNumberLit
221+ HsWord16Prim {} -> TNumberLit
222+ HsWord32Prim {} -> TNumberLit
223+ HsWord64Prim {} -> TNumberLit
224+ HsInt8Prim {} -> TNumberLit
225+ HsInt16Prim {} -> TNumberLit
226+ HsInt32Prim {} -> TNumberLit
227+ HsInt64Prim {} -> TNumberLit
228+ HsFloatPrim {} -> TNumberLit
229+ HsDoublePrim {} -> TNumberLit
230+ HsRat {} -> TNumberLit
231+
232+ HsString {} -> TStringLit
233+ HsStringPrim {} -> TStringLit
234+ HsGetField _ _ field -> trace " ============== HIT RECORD SELECTOR" $ maybeToList $ mkFromLocatable TRecordSelector \ k -> k field
235+ HsProjection _ projs -> trace " ============== HIT RECORD SELECTOR" $ foldMap (\ proj -> maybeToList $ mkFromLocatable TRecordSelector \ k -> k proj) projs
236+ _ -> []
237+ ]
238+ in RangeHsSyntacticTokenTypes toks
239+
240+ {-# inline mkFromLocatable #-}
241+ mkFromLocatable
242+ :: HsSyntacticTokenType
243+ -> (forall r . (forall a . HasSrcSpan a => a -> r ) -> r )
244+ -> Maybe (Range , HsSyntacticTokenType )
245+ mkFromLocatable tt w = w \ tok -> let mrange = srcSpanToRange $ getLoc tok in fmap (, tt) mrange
133246
134247-- taken from /haskell-language-server/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs
135248
0 commit comments